diff --git a/Source/AnimatableImageView.swift b/Source/AnimatableImageView.swift index cd01581..8514ae3 100644 --- a/Source/AnimatableImageView.swift +++ b/Source/AnimatableImageView.swift @@ -1,17 +1,15 @@ -import ImageIO import Runes import UIKit /// A subclass of `UIImageView` that can be animated using an image name string or raw data. public class AnimatableImageView: UIImageView { /// An `Animator` instance that holds the frames of a specific image in memory. - var animator: Animator? + private var animator: Animator? /// A display link that keeps calling the `updateFrame` method on every screen refresh. private lazy var displayLink: CADisplayLink = CADisplayLink(target: self, selector: Selector("updateFrame")) - deinit { - println("deinit animatable view") - } + /// The size of the frame cache. + public var framePreloadCount = 50 /// A computed property that returns whether the image view is animating. public var isAnimatingGIF: Bool { @@ -32,7 +30,8 @@ public class AnimatableImageView: UIImageView { /// :param: data GIF image data. public func prepareForAnimation(imageData data: NSData) { image = UIImage(data: data) - animator = Animator(data: data, size: frame.size, contentMode: contentMode) + animator = Animator(data: data, size: frame.size, contentMode: contentMode, framePreloadCount: framePreloadCount) + animator?.prepareFrames() attachDisplayLink() } @@ -74,18 +73,16 @@ public class AnimatableImageView: UIImageView { /// Stops the image view animation. public func stopAnimatingGIF() { displayLink.paused = true - cleanup() } - /// Cleanup the animator to reduce memory. + /// Invalidate the displayLink so it releases this object. public func cleanup() { - image = .None - animator = .None + displayLink.invalidate() } - /// Attaches the dsiplay link. - func attachDisplayLink() { - displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes) + /// Attaches the display link. + private func attachDisplayLink() { + displayLink.addToRunLoop(.mainRunLoop(), forMode: NSRunLoopCommonModes) } } diff --git a/Source/Animator.swift b/Source/Animator.swift index ae4f7cf..6fb2836 100644 --- a/Source/Animator.swift +++ b/Source/Animator.swift @@ -3,7 +3,7 @@ import ImageIO import Runes /// Responsible for storing and updating the frames of a `AnimatableImageView` instance via delegation. -class Animator: NSObject { +class Animator { /// Maximum duration to increment the frame timer with. private let maxTimeStep = 1.0 /// An array of animated frames from a single GIF image. @@ -13,7 +13,7 @@ class Animator: NSObject { /// The content mode to use when resizing private let contentMode: UIViewContentMode /// Maximum number of frames to load at once - private let maxNumberOfFrames = 50 + private let maxNumberOfFrames: Int /// The total number of frames in the GIF. private var numberOfFrames = 0 /// A reference to the original image source. @@ -37,24 +37,19 @@ class Animator: NSObject { /// /// :param: data The raw GIF image data. /// :param: delegate An `Animatable` delegate. - required init(data: NSData, size: CGSize, contentMode: UIViewContentMode) { + init(data: NSData, size: CGSize, contentMode: UIViewContentMode, framePreloadCount: Int) { let options = [String(kCGImageSourceShouldCache): kCFBooleanFalse] imageSource = CGImageSourceCreateWithData(data, options) self.size = size self.contentMode = contentMode - super.init() - prepareFrames() - } - - deinit { - println("deinit animator") + maxNumberOfFrames = framePreloadCount } // MARK: - Frames /// Loads the frames from an image source, resizes them, then caches them in `animatedFrames`. - private func prepareFrames() { + func prepareFrames() { numberOfFrames = Int(CGImageSourceGetCount(imageSource)) - let framesToProcess = numberOfFrames > maxNumberOfFrames ? maxNumberOfFrames : numberOfFrames + let framesToProcess = min(numberOfFrames, maxNumberOfFrames) animatedFrames.reserveCapacity(framesToProcess) animatedFrames = reduce(0.. Bool { - if animatedFrames.count <= 1 { return false } - timeSinceLastFrameChange += min(maxTimeStep, duration) var frameDuration = animatedFrames[currentFrameIndex % animatedFrames.count].duration @@ -101,7 +94,7 @@ class Animator: NSObject { let lastFrameIndex = currentFrameIndex currentFrameIndex = ++currentFrameIndex % numberOfFrames - // load the next needed frame for progressive loading + // Loads the next needed frame for progressive loading if animatedFrames.count < numberOfFrames { let nextFrameToLoad = (lastFrameIndex + animatedFrames.count) % numberOfFrames animatedFrames[lastFrameIndex % animatedFrames.count] = prepareFrame(nextFrameToLoad)