Repository: onevcat/Kingfisher
File: Sources/AnimatedImageView.swift (Swift)

Code before:

/// Keeps a reference to an `Image` instance and its duration as a GIF frame.
struct AnimatedFrame {
    var image: Image?
    let duration: TimeInterval
    
    static let null: AnimatedFrame = AnimatedFrame(image: .none, duration: 0.0)
}

protocol AnimatorDelegate: class {
    func animator(_ animator: Animator, didPlayAnimationLoops count: UInt)
}

// MARK: - Animator
class Animator {
    // MARK: Private property
    fileprivate let size: CGSize
    fileprivate let maxFrameCount: Int
    fileprivate let imageSource: CGImageSource
    fileprivate let maxRepeatCount: AnimatedImageView.RepeatCount
    
    fileprivate var animatedFrames = [AnimatedFrame]()
    fileprivate let maxTimeStep: TimeInterval = 1.0
    fileprivate var frameCount = 0
    fileprivate var currentFrameIndex = 0
    fileprivate var currentPreloadIndex = 0
    fileprivate var timeSinceLastFrameChange: TimeInterval = 0.0
    fileprivate var needsPrescaling = true
    fileprivate var currentRepeatCount: UInt = 0
    fileprivate weak var delegate: AnimatorDelegate?
    
    /// Loop count of animated image.
    private var loopCount = 0
    
    var currentFrame: UIImage? {
        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
    

Review comment (suggestion):
`currentFrameIndex` now tracks the current frame of the gif being displayed. `currentFrameIndexInBuffer` serves the old purpose of `currentFrameIndex`, which is the index of the current frame of the gif as it exists in the `animatedFrames` buffer.

Code after:

/// Keeps a reference to an `Image` instance and its duration as a GIF frame.
struct AnimatedFrame {
    var image: Image?
    let duration: TimeInterval
    
    static let null: AnimatedFrame = AnimatedFrame(image: .none, duration: 0.0)
}

protocol AnimatorDelegate: class {
    func animator(_ animator: Animator, didPlayAnimationLoops count: UInt)
}

// MARK: - Animator
class Animator {
    // MARK: Private property
    fileprivate let size: CGSize
    fileprivate let maxFrameCount: Int
    fileprivate let imageSource: CGImageSource
    fileprivate let maxRepeatCount: AnimatedImageView.RepeatCount
    
    fileprivate var animatedFrames = [AnimatedFrame]()
    fileprivate let maxTimeStep: TimeInterval = 1.0
    fileprivate var frameCount = 0
    fileprivate var currentFrameIndex = 0
    fileprivate var currentFrameIndexInBuffer = 0
    fileprivate var currentPreloadIndex = 0
    fileprivate var timeSinceLastFrameChange: TimeInterval = 0.0
    fileprivate var needsPrescaling = true
    fileprivate var currentRepeatCount: UInt = 0
    fileprivate weak var delegate: AnimatorDelegate?
    
    /// Loop count of animated image.
    private var loopCount = 0
    
    var currentFrame: UIImage? {
        return frame(at: currentFrameIndexInBuffer)
    }

    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