Kaynağa Gözat

Merge pull request #829 from JacobMao/PlayGIFOnlyOnceWithAnimatedImageView

Play gif only once with animated image view
Wei Wang 8 yıl önce
ebeveyn
işleme
01839349a5
1 değiştirilmiş dosya ile 111 ekleme ve 3 silme
  1. 111 3
      Sources/AnimatedImageView.swift

+ 111 - 3
Sources/AnimatedImageView.swift

@@ -34,6 +34,29 @@
 import UIKit
 import UIKit
 import ImageIO
 import ImageIO
 
 
+/// Protocol of `AnimatedImageView`.
+public protocol AnimatedImageViewDelegate: class {
+    /**
+     Called after the animatedImageView has finished each animation loop.
+
+     - parameter imageView: The animatedImageView that is being animated.
+     - parameter count: The looped count.
+     */
+    func animatedImageView(_ imageView: AnimatedImageView, didPlayAnimationLoops count: UInt)
+
+    /**
+     Called after the animatedImageView has reached the max repeat count.
+
+     - parameter imageView: The animatedImageView that is being animated.
+     */
+    func animatedImageViewDidFinishAnimating(_ imageView: AnimatedImageView)
+}
+
+extension AnimatedImageViewDelegate {
+    public func animatedImageView(_ imageView: AnimatedImageView, didPlayAnimationLoops count: UInt) {}
+    public func animatedImageViewDidFinishAnimating(_ imageView: AnimatedImageView) {}
+}
+
 /// `AnimatedImageView` is a subclass of `UIImageView` for displaying animated image.
 /// `AnimatedImageView` is a subclass of `UIImageView` for displaying animated image.
 open class AnimatedImageView: UIImageView {
 open class AnimatedImageView: UIImageView {
     
     
@@ -49,6 +72,27 @@ open class AnimatedImageView: UIImageView {
             target?.updateFrame()
             target?.updateFrame()
         }
         }
     }
     }
+
+    /// Enumeration that specifies repeat count of GIF
+    public enum RepeatCount: Equatable {
+        case once
+        case finite(count: UInt)
+        case infinite
+
+        public static func ==(lhs: RepeatCount, rhs: RepeatCount) -> Bool {
+            switch (lhs, rhs) {
+            case let (.finite(l), .finite(r)):
+                return l == r
+            case (.once, .once),
+                 (.infinite, .infinite):
+                return true
+            case (.once, _),
+                 (.infinite, _),
+                 (.finite, _):
+                return false
+            }
+        }
+    }
     
     
     // MARK: - Public property
     // MARK: - Public property
     /// Whether automatically play the animation when the view become visible. Default is true.
     /// Whether automatically play the animation when the view become visible. Default is true.
@@ -73,6 +117,20 @@ open class AnimatedImageView: UIImageView {
             }
             }
         }
         }
     }
     }
+
+    /// The repeat count.
+    public var repeatCount = RepeatCount.infinite {
+        didSet {
+            if oldValue != repeatCount {
+                reset()
+                setNeedsDisplay()
+                layer.setNeedsDisplay()
+            }
+        }
+    }
+
+    /// Delegate of this `AnimatedImageView` object. See `AnimatedImageViewDelegate` protocol for more.
+    public weak var delegate: AnimatedImageViewDelegate?
     
     
     // MARK: - Private property
     // MARK: - Private property
     /// `Animator` instance that holds the frames of a specific image in memory.
     /// `Animator` instance that holds the frames of a specific image in memory.
@@ -120,6 +178,10 @@ open class AnimatedImageView: UIImageView {
         if self.isAnimating {
         if self.isAnimating {
             return
             return
         } else {
         } else {
+            if animator?.isReachMaxRepeatCount ?? false {
+                return
+            }
+
             displayLink.isPaused = false
             displayLink.isPaused = false
         }
         }
     }
     }
@@ -160,7 +222,12 @@ open class AnimatedImageView: UIImageView {
     private func reset() {
     private func reset() {
         animator = nil
         animator = nil
         if let imageSource = image?.kf.imageSource?.imageRef {
         if let imageSource = image?.kf.imageSource?.imageRef {
-            animator = Animator(imageSource: imageSource, contentMode: contentMode, size: bounds.size, framePreloadCount: framePreloadCount)
+            animator = Animator(imageSource: imageSource,
+                                contentMode: contentMode,
+                                size: bounds.size,
+                                framePreloadCount: framePreloadCount,
+                                repeatCount: repeatCount)
+            animator?.delegate = self
             animator?.needsPrescaling = needsPrescaling
             animator?.needsPrescaling = needsPrescaling
             animator?.prepareFramesAsynchronously()
             animator?.prepareFramesAsynchronously()
         }
         }
@@ -199,10 +266,21 @@ open class AnimatedImageView: UIImageView {
     
     
         if animator?.updateCurrentFrame(duration: duration) ?? false {
         if animator?.updateCurrentFrame(duration: duration) ?? false {
             layer.setNeedsDisplay()
             layer.setNeedsDisplay()
+
+            if animator?.isReachMaxRepeatCount ?? false {
+                stopAnimating()
+                delegate?.animatedImageViewDidFinishAnimating(self)
+            }
         }
         }
     }
     }
 }
 }
 
 
+extension AnimatedImageView: AnimatorDelegate {
+    func animator(_ animator: Animator, didPlayAnimationLoops count: UInt) {
+        delegate?.animatedImageView(self, didPlayAnimationLoops: count)
+    }
+}
+
 /// Keeps a reference to an `Image` instance and its duration as a GIF frame.
 /// Keeps a reference to an `Image` instance and its duration as a GIF frame.
 struct AnimatedFrame {
 struct AnimatedFrame {
     var image: Image?
     var image: Image?
@@ -211,12 +289,17 @@ struct AnimatedFrame {
     static let null: AnimatedFrame = AnimatedFrame(image: .none, duration: 0.0)
     static let null: AnimatedFrame = AnimatedFrame(image: .none, duration: 0.0)
 }
 }
 
 
+protocol AnimatorDelegate: class {
+    func animator(_ animator: Animator, didPlayAnimationLoops count: UInt)
+}
+
 // MARK: - Animator
 // MARK: - Animator
 class Animator {
 class Animator {
     // MARK: Private property
     // MARK: Private property
     fileprivate let size: CGSize
     fileprivate let size: CGSize
     fileprivate let maxFrameCount: Int
     fileprivate let maxFrameCount: Int
     fileprivate let imageSource: CGImageSource
     fileprivate let imageSource: CGImageSource
+    fileprivate let maxRepeatCount: AnimatedImageView.RepeatCount
     
     
     fileprivate var animatedFrames = [AnimatedFrame]()
     fileprivate var animatedFrames = [AnimatedFrame]()
     fileprivate let maxTimeStep: TimeInterval = 1.0
     fileprivate let maxTimeStep: TimeInterval = 1.0
@@ -225,6 +308,8 @@ class Animator {
     fileprivate var currentPreloadIndex = 0
     fileprivate var currentPreloadIndex = 0
     fileprivate var timeSinceLastFrameChange: TimeInterval = 0.0
     fileprivate var timeSinceLastFrameChange: TimeInterval = 0.0
     fileprivate var needsPrescaling = true
     fileprivate var needsPrescaling = true
+    fileprivate var currentRepeatCount: UInt = 0
+    fileprivate weak var delegate: AnimatorDelegate?
     
     
     /// Loop count of animated image.
     /// Loop count of animated image.
     private var loopCount = 0
     private var loopCount = 0
@@ -232,6 +317,17 @@ class Animator {
     var currentFrame: UIImage? {
     var currentFrame: UIImage? {
         return frame(at: currentFrameIndex)
         return frame(at: currentFrameIndex)
     }
     }
+
+    var isReachMaxRepeatCount: Bool {
+        switch maxRepeatCount {
+        case .once:
+            return currentRepeatCount >= 1
+        case .finite(let maxCount):
+            return currentRepeatCount >= maxCount
+        case .infinite:
+            return false
+        }
+    }
     
     
     var contentMode = UIViewContentMode.scaleToFill
     var contentMode = UIViewContentMode.scaleToFill
     
     
@@ -249,11 +345,16 @@ class Animator {
      
      
      - returns: The animator object.
      - returns: The animator object.
      */
      */
-    init(imageSource source: CGImageSource, contentMode mode: UIViewContentMode, size: CGSize, framePreloadCount count: Int) {
+    init(imageSource source: CGImageSource,
+         contentMode mode: UIViewContentMode,
+         size: CGSize,
+         framePreloadCount count: Int,
+         repeatCount: AnimatedImageView.RepeatCount) {
         self.imageSource = source
         self.imageSource = source
         self.contentMode = mode
         self.contentMode = mode
         self.size = size
         self.size = size
         self.maxFrameCount = count
         self.maxFrameCount = count
+        self.maxRepeatCount = repeatCount
     }
     }
     
     
     func frame(at index: Int) -> Image? {
     func frame(at index: Int) -> Image? {
@@ -334,10 +435,17 @@ class Animator {
         let lastFrameIndex = currentFrameIndex
         let lastFrameIndex = currentFrameIndex
         currentFrameIndex += 1
         currentFrameIndex += 1
         currentFrameIndex = currentFrameIndex % animatedFrames.count
         currentFrameIndex = currentFrameIndex % animatedFrames.count
-        
+
         if animatedFrames.count < frameCount {
         if animatedFrames.count < frameCount {
             preloadFrameAsynchronously(at: lastFrameIndex)
             preloadFrameAsynchronously(at: lastFrameIndex)
         }
         }
+
+        if currentFrameIndex == 0 {
+            currentRepeatCount += 1
+
+            delegate?.animator(self, didPlayAnimationLoops: currentRepeatCount)
+        }
+
         return true
         return true
     }
     }