Support loading remote GIFs

- Closes #111
This commit is contained in:
Shubham 2017-07-19 15:04:33 +05:30 committed by Reda Lemeden
parent 6d19f73aad
commit 693dbc9eb4
3 changed files with 93 additions and 13 deletions

View File

@ -73,7 +73,11 @@ public class Animator {
let imagePath = Bundle.main.url(forResource: extensionRemoved, withExtension: "gif"), let imagePath = Bundle.main.url(forResource: extensionRemoved, withExtension: "gif"),
let data = try? Data(contentsOf: imagePath) else { return } let data = try? Data(contentsOf: imagePath) else { return }
prepareForAnimation(withGIFData: data, size: size, contentMode: contentMode, loopCount: loopCount, completionHandler: completionHandler) prepareForAnimation(withGIFData: data,
size: size,
contentMode: contentMode,
loopCount: loopCount,
completionHandler: completionHandler)
} }
/// Prepares the animator instance for animation. /// Prepares the animator instance for animation.
@ -84,7 +88,11 @@ public class Animator {
/// - parameter loopCount: Desired number of loops, <= 0 for infinite loop. /// - parameter loopCount: Desired number of loops, <= 0 for infinite loop.
/// - parameter completionHandler: Completion callback function /// - parameter completionHandler: Completion callback function
func prepareForAnimation(withGIFData imageData: Data, size: CGSize, contentMode: UIViewContentMode, loopCount: Int = 0, completionHandler: (() -> Void)? = nil) { func prepareForAnimation(withGIFData imageData: Data, size: CGSize, contentMode: UIViewContentMode, loopCount: Int = 0, completionHandler: (() -> Void)? = nil) {
frameStore = FrameStore(data: imageData, size: size, contentMode: contentMode, framePreloadCount: frameBufferCount, loopCount: loopCount) frameStore = FrameStore(data: imageData,
size: size,
contentMode: contentMode,
framePreloadCount: frameBufferCount,
loopCount: loopCount)
frameStore?.shouldResizeFrames = shouldResizeFrames frameStore?.shouldResizeFrames = shouldResizeFrames
frameStore?.prepareFrames(completionHandler) frameStore?.prepareFrames(completionHandler)
attachDisplayLink() attachDisplayLink()
@ -121,7 +129,10 @@ public class Animator {
/// - parameter contentMode: The view content mode to use for the individual frames. /// - parameter contentMode: The view content mode to use for the individual frames.
/// - parameter loopCount: Desired number of loops, <= 0 for infinite loop. /// - parameter loopCount: Desired number of loops, <= 0 for infinite loop.
func animate(withGIFNamed imageName: String, size: CGSize, contentMode: UIViewContentMode, loopCount: Int = 0) { func animate(withGIFNamed imageName: String, size: CGSize, contentMode: UIViewContentMode, loopCount: Int = 0) {
prepareForAnimation(withGIFNamed: imageName, size: size, contentMode: contentMode, loopCount: loopCount) prepareForAnimation(withGIFNamed: imageName,
size: size,
contentMode: contentMode,
loopCount: loopCount)
startAnimating() startAnimating()
} }
@ -132,7 +143,10 @@ public class Animator {
/// - parameter contentMode: The view content mode to use for the individual frames. /// - parameter contentMode: The view content mode to use for the individual frames.
/// - parameter loopCount: Desired number of loops, <= 0 for infinite loop. /// - parameter loopCount: Desired number of loops, <= 0 for infinite loop.
func animate(withGIFData imageData: Data, size: CGSize, contentMode: UIViewContentMode, loopCount: Int = 0) { func animate(withGIFData imageData: Data, size: CGSize, contentMode: UIViewContentMode, loopCount: Int = 0) {
prepareForAnimation(withGIFData: imageData, size: size, contentMode: contentMode, loopCount: loopCount) prepareForAnimation(withGIFData: imageData,
size: size,
contentMode: contentMode,
loopCount: loopCount)
startAnimating() startAnimating()
} }

View File

@ -1,6 +1,7 @@
import Foundation
/// The protocol that view classes need to conform to to enable animated GIF support. /// The protocol that view classes need to conform to to enable animated GIF support.
public protocol GIFAnimatable: class { public protocol GIFAnimatable: class {
/// Responsible for managing the animation frames. /// Responsible for managing the animation frames.
var animator: Animator? { get set } var animator: Animator? { get set }
@ -33,7 +34,7 @@ extension GIFAnimatable {
public var gifLoopDuration: TimeInterval { public var gifLoopDuration: TimeInterval {
return animator?.loopDuration ?? 0 return animator?.loopDuration ?? 0
} }
/// Returns the active frame if available. /// Returns the active frame if available.
public var activeFrame: UIImage? { public var activeFrame: UIImage? {
return animator?.activeFrame() return animator?.activeFrame()
@ -54,7 +55,10 @@ extension GIFAnimatable {
/// - parameter imageName: The file name of the GIF in the main bundle. /// - parameter imageName: The file name of the GIF in the main bundle.
/// - parameter loopCount: Desired number of loops, <= 0 for infinite loop. /// - parameter loopCount: Desired number of loops, <= 0 for infinite loop.
public func animate(withGIFNamed imageName: String, loopCount: Int = 0) { public func animate(withGIFNamed imageName: String, loopCount: Int = 0) {
animator?.animate(withGIFNamed: imageName, size: frame.size, contentMode: contentMode, loopCount: loopCount) animator?.animate(withGIFNamed: imageName,
size: frame.size,
contentMode: contentMode,
loopCount: loopCount)
} }
/// Prepare for animation and start animating immediately. /// Prepare for animation and start animating immediately.
@ -62,27 +66,89 @@ extension GIFAnimatable {
/// - parameter imageData: GIF image data. /// - parameter imageData: GIF image data.
/// - parameter loopCount: Desired number of loops, <= 0 for infinite loop. /// - parameter loopCount: Desired number of loops, <= 0 for infinite loop.
public func animate(withGIFData imageData: Data, loopCount: Int = 0) { public func animate(withGIFData imageData: Data, loopCount: Int = 0) {
animator?.animate(withGIFData: imageData, size: frame.size, contentMode: contentMode, loopCount: loopCount) animator?.animate(withGIFData: imageData,
size: frame.size,
contentMode: contentMode,
loopCount: loopCount)
}
/// Prepare for animation and start animating immediately.
///
/// - parameter imageURL: GIF image url.
/// - parameter loopCount: Desired number of loops, <= 0 for infinite loop.
public func animate(withGIFURL imageURL: URL, loopCount: Int = 0) {
let session = URLSession.shared
let task = session.dataTask(with: imageURL) { (data, response, error) in
switch (data, response, error) {
case (.none, _, let error?):
print("Error downloading gif:", error.localizedDescription, "at url:", imageURL.absoluteString)
case (let data?, _, _):
DispatchQueue.main.async {
self.animate(withGIFData: data, loopCount: loopCount)
}
default: ()
}
}
task.resume()
} }
/// Prepares the animator instance for animation. /// Prepares the animator instance for animation.
/// ///
/// - parameter imageName: The file name of the GIF in the main bundle. /// - parameter imageName: The file name of the GIF in the main bundle.
/// - parameter loopCount: Desired number of loops, <= 0 for infinite loop. /// - parameter loopCount: Desired number of loops, <= 0 for infinite loop.
public func prepareForAnimation(withGIFNamed imageName: String, loopCount: Int = 0, completionHandler: (() -> Void)? = nil) { public func prepareForAnimation(withGIFNamed imageName: String,
animator?.prepareForAnimation(withGIFNamed: imageName, size: frame.size, contentMode: contentMode, loopCount: loopCount, completionHandler: completionHandler) loopCount: Int = 0,
completionHandler: (() -> Void)? = nil) {
animator?.prepareForAnimation(withGIFNamed: imageName,
size: frame.size,
contentMode: contentMode,
loopCount: loopCount,
completionHandler: completionHandler)
} }
/// Prepare for animation and start animating immediately. /// Prepare for animation and start animating immediately.
/// ///
/// - parameter imageData: GIF image data. /// - parameter imageData: GIF image data.
/// - parameter loopCount: Desired number of loops, <= 0 for infinite loop. /// - parameter loopCount: Desired number of loops, <= 0 for infinite loop.
public func prepareForAnimation(withGIFData imageData: Data, loopCount: Int = 0, completionHandler: (() -> Void)? = nil) { public func prepareForAnimation(withGIFData imageData: Data,
loopCount: Int = 0,
completionHandler: (() -> Void)? = nil) {
if var imageContainer = self as? ImageContainer { if var imageContainer = self as? ImageContainer {
imageContainer.image = UIImage(data: imageData) imageContainer.image = UIImage(data: imageData)
} }
animator?.prepareForAnimation(withGIFData: imageData, size: frame.size, contentMode: contentMode, loopCount: loopCount, completionHandler: completionHandler) animator?.prepareForAnimation(withGIFData: imageData,
size: frame.size,
contentMode: contentMode,
loopCount: loopCount,
completionHandler: completionHandler)
}
/// Prepare for animation and start animating immediately.
///
/// - parameter imageURL: GIF image url.
/// - parameter loopCount: Desired number of loops, <= 0 for infinite loop.
public func prepareForAnimation(withGIFURL imageURL: URL,
loopCount: Int = 0,
completionHandler: (() -> Void)? = nil) {
let session = URLSession.shared
let task = session.dataTask(with: imageURL) { (data, response, error) in
switch (data, response, error) {
case (.none, _, let error?):
print("Error downloading gif:", error.localizedDescription, "at url:", imageURL.absoluteString)
case (let data?, _, _):
DispatchQueue.main.async {
self.prepareForAnimation(withGIFData: data,
loopCount: loopCount,
completionHandler: completionHandler)
}
default: ()
}
}
task.resume()
} }
/// Stop animating and free up GIF data from memory. /// Stop animating and free up GIF data from memory.

View File

@ -19,7 +19,7 @@
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>128</string> <string>133</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
<string></string> <string></string>
</dict> </dict>