/// The protocol that view classes need to conform to to enable animated GIF support. public protocol GIFAnimatable: class { /// Responsible for managing the animation frames. var animator: Animator? { get set } /// Used for displaying the animation frames. var image: UIImage? { get set } /// Notifies the instance that it needs display. var layer: CALayer { get } /// View frame used for resizing the frames. var frame: CGRect { get set } /// Content mode used for resizing the frames. var contentMode: UIViewContentMode { get set } } extension GIFAnimatable { /// Returns the intrinsic content size based on the size of the image. public var intrinsicContentSize: CGSize { return image?.size ?? CGSize.zero } /// Returns the active frame if available. public var activeFrame: UIImage? { return animator?.activeFrame() } /// Total frame count of the GIF. public var frameCount: Int { return animator?.frameCount ?? 0 } /// Introspect whether the instance is animating. public var isAnimatingGIF: Bool { return animator?.isAnimating ?? false } /// Prepare for animation and start animating immediately. /// /// - parameter imageName: The file name of the GIF in the main bundle. public func animate(withGIFNamed imageName: String) { animator?.animate(withGIFNamed: imageName, size: frame.size, contentMode: contentMode) } /// Prepare for animation and start animating immediately. /// /// - parameter imageData: GIF image data. public func animate(withGIFData imageData: Data) { animator?.animate(withGIFData: imageData, size: frame.size, contentMode: contentMode) } /// Prepares the animator instance for animation. /// /// - parameter imageName: The file name of the GIF in the main bundle. public func prepareForAnimation(withGIFNamed imageName: String) { animator?.prepareForAnimation(withGIFNamed: imageName, size: frame.size, contentMode: contentMode) } /// Prepare for animation and start animating immediately. /// /// - parameter imageData: GIF image data. public func prepareForAnimation(withGIFData imageData: Data) { image = UIImage(data: imageData) animator?.prepareForAnimation(withGIFData: imageData, size: frame.size, contentMode: contentMode) } /// Stop animating and free up GIF data from memory. public func prepareForReuse() { animator?.prepareForReuse() } /// Start animating GIF. public func startAnimatingGIF() { animator?.startAnimating() } /// Stop animating GIF. public func stopAnimatingGIF() { animator?.stopAnimating() } /// Updates the image with a new frame if necessary. public func updateImageIfNeeded() { let frame = animator?.activeFrame() ?? image if image != frame { image = frame } } } extension GIFAnimatable { /// Calls setNeedsDisplay on the layer whenever the animator has a new frame. Should *not* be called directly. func animatorHasNewFrame() { layer.setNeedsDisplay() } }