Invalidate display link

This commit is contained in:
Tony DiPasquale 2015-06-04 19:36:47 -04:00
parent a2894d59cd
commit 6a22c41aa1
2 changed files with 17 additions and 27 deletions

View File

@ -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)
}
}

View File

@ -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..<framesToProcess, []) { $0 + pure(prepareFrame($1)) }
}
@ -91,8 +86,6 @@ class Animator: NSObject {
///
/// :returns: An optional image at a given frame.
func updateCurrentFrame(duration: CFTimeInterval) -> 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)