Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 57 additions & 2 deletions Distrib/MoaDistrib.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ The class can be instantiated and used without an image view:
public final class Moa {
private var imageDownloader: MoaImageDownloader?
private weak var imageView: MoaImageView?
/// Image download did start at this time.
private var imageDownloadStart: Date?

/// If the request finishes before this time, changes are not animated (e.g. by fading).
private static let animateChangeLimit: TimeInterval = 0.1

/// Image download settings.
public static var settings = MoaSettings() {
Expand Down Expand Up @@ -117,6 +122,7 @@ public final class Moa {
public func cancel() {
imageDownloader?.cancel()
imageDownloader = nil
imageDownloadStart = nil
}

/**
Expand Down Expand Up @@ -192,19 +198,30 @@ public final class Moa {
*/
public static var errorImage: MoaImage?

#if os(iOS) || os(tvOS)
/// Set a value greater than zero to make Moa fade in the image over the given time after downloading it from the network.
public var imageFadeDuration: TimeInterval?

/// Set a value greater than zero to make Moa fade in images over the given time after downloading them from the network.
public static var imageFadeDuration: TimeInterval?
#endif

private func startDownload(_ url: String) {
cancel()

let simulatedDownloader = MoaSimulator.createDownloader(url)
imageDownloader = simulatedDownloader ?? MoaHttpImageDownloader(logger: Moa.logger)
let simulated = simulatedDownloader != nil


imageDownloadStart = Date()

imageDownloader?.startDownload(url,
onSuccess: { [weak self] image in
self?.handleSuccessAsync(image, isSimulated: simulated)
},
onError: { [weak self] error, response in
self?.handleErrorAsync(error, response: response, isSimulated: simulated)
self?.imageDownloadStart = nil
}
)
}
Expand Down Expand Up @@ -247,9 +264,47 @@ public final class Moa {
if let onSuccess = onSuccess, let image = image {
imageForView = onSuccess(image)
}


#if os(iOS) || os(tvOS)
if
let imageView = self.imageView,
imageView.image != imageForView,
let imageFadeDuration = self.shouldFadeWithDuration()
{
UIView.transition(
with: imageView,
duration: imageFadeDuration,
options: .transitionCrossDissolve,
animations: { imageView.image = imageForView }
)
} else {
imageView?.image = imageForView
}
#else
imageView?.image = imageForView
#endif

self.imageDownloadStart = nil
}

#if os(iOS) || os(tvOS)
/// A fade duration that is greater than zero. Only provided when the download time surpasses animateChangeLimit.
private func shouldFadeWithDuration() -> TimeInterval? {

let downloadTime: TimeInterval = imageDownloadStart.map { Date().timeIntervalSince($0) } ?? .zero
guard downloadTime > Moa.animateChangeLimit else {
return nil
}

if let duration = self.imageFadeDuration, duration > 0 {
return duration
}
if let duration = Moa.imageFadeDuration, duration > 0 {
return duration
}
return nil
}
#endif

/**

Expand Down
59 changes: 57 additions & 2 deletions Moa/Moa.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ The class can be instantiated and used without an image view:
public final class Moa {
private var imageDownloader: MoaImageDownloader?
private weak var imageView: MoaImageView?
/// Image download did start at this time.
private var imageDownloadStart: Date?

/// If the request finishes before this time, changes are not animated (e.g. by fading).
private static let animateChangeLimit: TimeInterval = 0.1

/// Image download settings.
public static var settings = MoaSettings() {
Expand Down Expand Up @@ -102,6 +107,7 @@ public final class Moa {
public func cancel() {
imageDownloader?.cancel()
imageDownloader = nil
imageDownloadStart = nil
}

/**
Expand Down Expand Up @@ -177,19 +183,30 @@ public final class Moa {
*/
public static var errorImage: MoaImage?

#if os(iOS) || os(tvOS)
/// Set a value greater than zero to make Moa fade in the image over the given time after downloading it from the network.
public var imageFadeDuration: TimeInterval?

/// Set a value greater than zero to make Moa fade in images over the given time after downloading them from the network.
public static var imageFadeDuration: TimeInterval?
#endif

private func startDownload(_ url: String) {
cancel()

let simulatedDownloader = MoaSimulator.createDownloader(url)
imageDownloader = simulatedDownloader ?? MoaHttpImageDownloader(logger: Moa.logger)
let simulated = simulatedDownloader != nil


imageDownloadStart = Date()

imageDownloader?.startDownload(url,
onSuccess: { [weak self] image in
self?.handleSuccessAsync(image, isSimulated: simulated)
},
onError: { [weak self] error, response in
self?.handleErrorAsync(error, response: response, isSimulated: simulated)
self?.imageDownloadStart = nil
}
)
}
Expand Down Expand Up @@ -232,9 +249,47 @@ public final class Moa {
if let onSuccess = onSuccess, let image = image {
imageForView = onSuccess(image)
}


#if os(iOS) || os(tvOS)
if
let imageView = self.imageView,
imageView.image != imageForView,
let imageFadeDuration = self.shouldFadeWithDuration()
{
UIView.transition(
with: imageView,
duration: imageFadeDuration,
options: .transitionCrossDissolve,
animations: { imageView.image = imageForView }
)
} else {
imageView?.image = imageForView
}
#else
imageView?.image = imageForView
#endif

self.imageDownloadStart = nil
}

#if os(iOS) || os(tvOS)
/// A fade duration that is greater than zero. Only provided when the download time surpasses animateChangeLimit.
private func shouldFadeWithDuration() -> TimeInterval? {

let downloadTime: TimeInterval = imageDownloadStart.map { Date().timeIntervalSince($0) } ?? .zero
guard downloadTime > Moa.animateChangeLimit else {
return nil
}

if let duration = self.imageFadeDuration, duration > 0 {
return duration
}
if let duration = Moa.imageFadeDuration, duration > 0 {
return duration
}
return nil
}
#endif

/**

Expand Down
5 changes: 3 additions & 2 deletions MoaTests/Utils/MoaTimeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import XCTest

class MoaTimeTests: XCTestCase {
func testLogTimeDate() {
let calendar = Calendar.current
var calendar = Calendar.current
calendar.timeZone = TimeZone(secondsFromGMT: 0)!
let components = NSDateComponents()
components.year = 2027
components.month = 11
Expand All @@ -17,6 +18,6 @@ class MoaTimeTests: XCTestCase {

let result = MoaTime.logTime(date)

XCTAssertEqual("2027-11-21 02:51:41.000", result)
XCTAssertEqual("2027-11-21 13:51:41.000", result)
}
}
8 changes: 4 additions & 4 deletions moa.podspec
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Pod::Spec.new do |s|
s.name = "moa"
s.version = "12.0.0"
s.version = "12.1.0"
s.license = { :type => "MIT" }
s.homepage = "https://github.com/evgenyneu/moa"
s.homepage = "https://github.com/r-dent/moa"
s.summary = "An image download extension for image view written in Swift."
s.description = <<-DESC
Moa is an image download library written in Swift for iOS, tvOS and macOS.
Expand All @@ -16,8 +16,8 @@ Pod::Spec.new do |s|
* Includes unit testing mode for faking network responses.
DESC
s.authors = { "Evgenii Neumerzhitckii" => "sausageskin@gmail.com" }
s.source = { :git => "https://github.com/evgenyneu/moa.git", :tag => s.version }
s.screenshots = "https://raw.githubusercontent.com/evgenyneu/moa/master/Graphics/Hunting_Moa.jpg"
s.source = { :git => "https://github.com/r-dent/moa.git", :tag => s.version }
s.screenshots = "https://raw.githubusercontent.com/r-dent/moa/master/Graphics/Hunting_Moa.jpg"
s.source_files = "Moa/**/*.swift"
s.ios.deployment_target = "8.0"
s.osx.deployment_target = "10.9"
Expand Down