             _imageView.trailingAnchor.constraint(equalTo: trailingAnchor),
             _imageView.bottomAnchor.constraint(equalTo: bottomAnchor),
         ])
-        
-        if let image = image, let output = image.decoder.output {
-            switch output {
-            case .success(let cgImage):
-                _imageView.image = UIImage(cgImage: cgImage, scale: image.scale, orientation: .up)
-            case .failure(let error):
-                print("[APNGKit] Encountered an error when decoding image frame: \(error). Trying to reverting to the default image.")
-                do {
-                    let data = try image.decoder.createDefaultImageData()
-                    _imageView.image = UIImage(data: data, scale: image.scale)
-                } catch {
-                    print("[APNGKit] Encountered an error when decoding the default image. \(error)")
-                    _imageView.image = nil
-                }
-            }
-        }
-    }
-    
+    }
+    
+    /// A flag used to determine how a view lays out its content when its bounds change.
     open override var contentMode: UIView.ContentMode {
         get { _imageView.contentMode }
         set { _imageView.contentMode = newValue }
     }
     
+    /// The natural size for the receiving view, considering only properties of the view itself.
+    ///
+    /// For an APNGImageView, its intrinsic content size is the `size` property of its set `APNGImage` object.
     open override var intrinsicContentSize: CGSize {
         _image?.size ?? .zero
     }
     
-    open private(set) var isAnimating: Bool = false
-    
+    /// Whether the image view is performing animation.
+    public private(set) var isAnimating: Bool = false
+    
+    /// The run loop where the animation (or say, the display link) should be run on.
+    ///
+    /// By default, the animation will run on the `.common` runloop, which means the animation continues when user
+    /// perform other action such as scrolling. You are only allowed to set it once before the animation started.
+    /// Otherwise it causes an assertion or has no effect.
     open var runLoopMode: RunLoop.Mode? {
         didSet {
             if oldValue != nil {

import UIKit
import Delegate

open class APNGImageView: UIView {
    
    /// Whether the animation should be played automatically when a valid `APNGImage` is set to the `image` property

    private var frameMissed: Bool = false
    
    private var displayLink: CADisplayLink?
    
    // Backing storage.
    private var _image: APNGImage?
    private let _imageView = UIImageView(frame: .zero)
    
    // Number of played plays of the animated image.
    private var playedCount = 0
    
    /// Creates an APNG image view with the specified animated image.
    /// - Parameter image: The initial image to display in the image view.
    public convenience init(image: APNGImage?) {
        self.init(frame: .zero)
        self.image = image
    }

    /// Creates an APNG image view with the specified normal image.
    /// - Parameter image: The initial image to display in the image view.
    ///
    /// This method is provided as a fallback for setting a normal `UIImage`. This does not start the animation or
    public convenience init(image: UIImage?) {
        self.init(frame: .zero)
        self._imageView.image = image
    }
    
    /// Creates an APNG image view with the specified frame.
    /// - Parameter frame: The initial frame that this image view should be placed.
    public override init(frame: CGRect) {
        super.init(frame: frame)
        commonSetup()
