Add completion handlers to all animate methods

This commit is contained in:
Reda Lemeden 2018-01-01 21:12:33 +01:00
parent 7d627009f9
commit 0991ad36d8
6 changed files with 50 additions and 21 deletions

View File

@ -4,7 +4,6 @@
<adaptation id="fullscreen"/> <adaptation id="fullscreen"/>
</device> </device>
<dependencies> <dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/> <capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -23,7 +22,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" text="Tap the image to pause/resume. Swipe to change GIF." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wsv-cU-WO5"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" text="Tap the image to pause/resume. Swipe to change GIF." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wsv-cU-WO5">
<rect key="frame" x="95.5" y="124.5" width="185" height="29"/> <rect key="frame" x="95" y="125" width="185" height="29"/>
<constraints> <constraints>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="200" id="73c-sg-Egr"/> <constraint firstAttribute="width" relation="lessThanOrEqual" constant="200" id="73c-sg-Egr"/>
</constraints> </constraints>
@ -32,7 +31,7 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<imageView clipsSubviews="YES" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" placeholderIntrinsicWidth="600" placeholderIntrinsicHeight="300" translatesAutoresizingMaskIntoConstraints="NO" id="FSz-xF-Xds" customClass="GIFImageView" customModule="Gifu"> <imageView clipsSubviews="YES" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" placeholderIntrinsicWidth="600" placeholderIntrinsicHeight="300" translatesAutoresizingMaskIntoConstraints="NO" id="FSz-xF-Xds" customClass="GIFImageView" customModule="Gifu">
<rect key="frame" x="0.0" y="203.5" width="375" height="300"/> <rect key="frame" x="0.0" y="204" width="375" height="300"/>
<gestureRecognizers/> <gestureRecognizers/>
<constraints> <constraints>
<constraint firstAttribute="width" secondItem="FSz-xF-Xds" secondAttribute="height" multiplier="5:4" id="EOH-hn-KxM"/> <constraint firstAttribute="width" secondItem="FSz-xF-Xds" secondAttribute="height" multiplier="5:4" id="EOH-hn-KxM"/>
@ -45,20 +44,30 @@
</connections> </connections>
</imageView> </imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" text="Gifu" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="c8Y-41-BaC"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" text="Gifu" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="c8Y-41-BaC">
<rect key="frame" x="170.5" y="100" width="34" height="20.5"/> <rect key="frame" x="171" y="100" width="34" height="21"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" red="0.99144423007965088" green="0.56549066305160522" blue="0.033751130104064941" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.99144423007965088" green="0.56549066305160522" blue="0.033751130104064941" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XcY-7q-76n">
<rect key="frame" x="0.5" y="514" width="375" height="18"/>
<fontDescription key="fontDescription" type="italicSystem" pointSize="15"/>
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
</subviews> </subviews>
<color key="backgroundColor" cocoaTouchSystemColor="darkTextColor"/> <color key="backgroundColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="tintColor" red="0.99144423007965088" green="0.56549066305160522" blue="0.033751130104064941" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="tintColor" red="0.99144423007965088" green="0.56549066305160522" blue="0.033751130104064941" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="XcY-7q-76n" firstAttribute="width" secondItem="FSz-xF-Xds" secondAttribute="width" id="306-mf-Sm0"/>
<constraint firstAttribute="trailing" secondItem="FSz-xF-Xds" secondAttribute="trailing" id="8kQ-x1-WJl"/> <constraint firstAttribute="trailing" secondItem="FSz-xF-Xds" secondAttribute="trailing" id="8kQ-x1-WJl"/>
<constraint firstItem="XcY-7q-76n" firstAttribute="top" secondItem="FSz-xF-Xds" secondAttribute="bottom" constant="10" id="BH2-vh-hUc"/>
<constraint firstItem="XcY-7q-76n" firstAttribute="centerX" secondItem="FSz-xF-Xds" secondAttribute="centerX" id="FBH-PS-mfM"/>
<constraint firstItem="c8Y-41-BaC" firstAttribute="top" secondItem="jyV-Pf-zRb" secondAttribute="bottom" constant="80" id="JHy-q1-JuJ"/> <constraint firstItem="c8Y-41-BaC" firstAttribute="top" secondItem="jyV-Pf-zRb" secondAttribute="bottom" constant="80" id="JHy-q1-JuJ"/>
<constraint firstAttribute="centerX" secondItem="c8Y-41-BaC" secondAttribute="centerX" id="Kc0-P5-KMZ"/> <constraint firstAttribute="centerX" secondItem="c8Y-41-BaC" secondAttribute="centerX" id="Kc0-P5-KMZ"/>
<constraint firstItem="FSz-xF-Xds" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" id="O4L-QH-SvV"/> <constraint firstItem="FSz-xF-Xds" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" id="O4L-QH-SvV"/>
<constraint firstItem="FSz-xF-Xds" firstAttribute="top" secondItem="wsv-cU-WO5" secondAttribute="bottom" constant="50" id="ODd-UW-Pca"/> <constraint firstItem="FSz-xF-Xds" firstAttribute="top" secondItem="wsv-cU-WO5" secondAttribute="bottom" constant="50" id="ODd-UW-Pca"/>
<constraint firstItem="2fi-mo-0CV" firstAttribute="top" relation="greaterThanOrEqual" secondItem="XcY-7q-76n" secondAttribute="bottom" constant="12" id="VEE-1g-0W4"/>
<constraint firstItem="wsv-cU-WO5" firstAttribute="top" secondItem="c8Y-41-BaC" secondAttribute="bottom" constant="4" id="ZG4-fK-WvN"/> <constraint firstItem="wsv-cU-WO5" firstAttribute="top" secondItem="c8Y-41-BaC" secondAttribute="bottom" constant="4" id="ZG4-fK-WvN"/>
<constraint firstAttribute="centerX" secondItem="FSz-xF-Xds" secondAttribute="centerX" id="oih-yH-vRh"/> <constraint firstAttribute="centerX" secondItem="FSz-xF-Xds" secondAttribute="centerX" id="oih-yH-vRh"/>
<constraint firstAttribute="centerX" secondItem="wsv-cU-WO5" secondAttribute="centerX" id="yLb-zR-gfU"/> <constraint firstAttribute="centerX" secondItem="wsv-cU-WO5" secondAttribute="centerX" id="yLb-zR-gfU"/>
@ -68,6 +77,7 @@
<nil key="simulatedTopBarMetrics"/> <nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/> <nil key="simulatedBottomBarMetrics"/>
<connections> <connections>
<outlet property="imageDataLabel" destination="XcY-7q-76n" id="8b5-Hu-aDc"/>
<outlet property="imageView" destination="FSz-xF-Xds" id="gtV-MK-Fwd"/> <outlet property="imageView" destination="FSz-xF-Xds" id="gtV-MK-Fwd"/>
</connections> </connections>
</viewController> </viewController>

View File

@ -3,19 +3,22 @@ import Gifu
class ViewController: UIViewController { class ViewController: UIViewController {
@IBOutlet weak var imageView: GIFImageView! @IBOutlet weak var imageView: GIFImageView!
@IBOutlet weak var imageDataLabel: UILabel!
@IBAction func unwindToRootViewController(segue: UIStoryboardSegue) { } @IBAction func unwindToRootViewController(segue: UIStoryboardSegue) { }
var currentGIFName: String = "mugen" { var currentGIFName: String = "mugen" {
didSet { didSet {
imageView.animate(withGIFNamed: currentGIFName) self.animate()
} }
} }
@IBAction func toggleAnimation(_ sender: AnyObject) { @IBAction func toggleAnimation(_ sender: AnyObject) {
if imageView.isAnimatingGIF { if imageView.isAnimatingGIF {
imageView.stopAnimatingGIF() imageView.stopAnimatingGIF()
print(imageView.gifLoopDuration)
} else { } else {
imageView.startAnimatingGIF() imageView.startAnimatingGIF()
print(imageView.gifLoopDuration)
} }
} }
@ -33,6 +36,14 @@ class ViewController: UIViewController {
} }
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
imageView.animate(withGIFNamed: currentGIFName) self.animate()
}
func animate() {
imageView.animate(withGIFNamed: currentGIFName) {
DispatchQueue.main.async {
self.imageDataLabel.text = self.currentGIFName.capitalized + " (\(self.imageView.frameCount) frames / \(String(format: "%.2f", self.imageView.gifLoopDuration))s)"
}
}
} }
} }

View File

@ -93,12 +93,11 @@ public class Animator {
contentMode: contentMode, contentMode: contentMode,
framePreloadCount: frameBufferCount, framePreloadCount: frameBufferCount,
loopCount: loopCount) loopCount: loopCount)
frameStore?.shouldResizeFrames = shouldResizeFrames frameStore!.shouldResizeFrames = shouldResizeFrames
frameStore?.prepareFrames(completionHandler) frameStore!.prepareFrames(completionHandler)
attachDisplayLink() attachDisplayLink()
} }
/// Add the display link to the main run loop. /// Add the display link to the main run loop.
private func attachDisplayLink() { private func attachDisplayLink() {
displayLink.add(to: .main, forMode: RunLoopMode.commonModes) displayLink.add(to: .main, forMode: RunLoopMode.commonModes)
@ -128,11 +127,13 @@ public class Animator {
/// - parameter size: The target size of the individual frames. /// - parameter size: The target size of the individual frames.
/// - parameter contentMode: The view content mode to use for the individual frames. /// - parameter contentMode: The view content mode to use for the individual frames.
/// - parameter loopCount: Desired number of loops, <= 0 for infinite loop. /// - parameter loopCount: Desired number of loops, <= 0 for infinite loop.
func animate(withGIFNamed imageName: String, size: CGSize, contentMode: UIViewContentMode, loopCount: Int = 0) { /// - parameter completionHandler: Completion callback function
func animate(withGIFNamed imageName: String, size: CGSize, contentMode: UIViewContentMode, loopCount: Int = 0, completionHandler: (() -> Void)? = nil) {
prepareForAnimation(withGIFNamed: imageName, prepareForAnimation(withGIFNamed: imageName,
size: size, size: size,
contentMode: contentMode, contentMode: contentMode,
loopCount: loopCount) loopCount: loopCount,
completionHandler: completionHandler)
startAnimating() startAnimating()
} }
@ -142,11 +143,13 @@ public class Animator {
/// - parameter size: The target size of the individual frames. /// - parameter size: The target size of the individual frames.
/// - parameter contentMode: The view content mode to use for the individual frames. /// - parameter contentMode: The view content mode to use for the individual frames.
/// - parameter loopCount: Desired number of loops, <= 0 for infinite loop. /// - parameter loopCount: Desired number of loops, <= 0 for infinite loop.
func animate(withGIFData imageData: Data, size: CGSize, contentMode: UIViewContentMode, loopCount: Int = 0) { /// - parameter completionHandler: Completion callback function
func animate(withGIFData imageData: Data, size: CGSize, contentMode: UIViewContentMode, loopCount: Int = 0, completionHandler: (() -> Void)? = nil) {
prepareForAnimation(withGIFData: imageData, prepareForAnimation(withGIFData: imageData,
size: size, size: size,
contentMode: contentMode, contentMode: contentMode,
loopCount: loopCount) loopCount: loopCount,
completionHandler: completionHandler)
startAnimating() startAnimating()
} }

View File

@ -98,7 +98,7 @@ class FrameStore {
animatedFrames.reserveCapacity(frameCount) animatedFrames.reserveCapacity(frameCount)
preloadFrameQueue.async { preloadFrameQueue.async {
self.setupAnimatedFrames() self.setupAnimatedFrames()
if let handler = completionHandler { handler() } completionHandler?()
} }
} }

View File

@ -54,29 +54,34 @@ extension GIFAnimatable {
/// ///
/// - parameter imageName: The file name of the GIF in the main bundle. /// - parameter imageName: The file name of the GIF in the main bundle.
/// - parameter loopCount: Desired number of loops, <= 0 for infinite loop. /// - parameter loopCount: Desired number of loops, <= 0 for infinite loop.
public func animate(withGIFNamed imageName: String, loopCount: Int = 0) { /// - parameter completionHandler: Completion callback function
public func animate(withGIFNamed imageName: String, loopCount: Int = 0, completionHandler: (() -> Void)? = nil) {
animator?.animate(withGIFNamed: imageName, animator?.animate(withGIFNamed: imageName,
size: frame.size, size: frame.size,
contentMode: contentMode, contentMode: contentMode,
loopCount: loopCount) loopCount: loopCount,
completionHandler: completionHandler)
} }
/// Prepare for animation and start animating immediately. /// Prepare for animation and start animating immediately.
/// ///
/// - parameter imageData: GIF image data. /// - parameter imageData: GIF image data.
/// - parameter loopCount: Desired number of loops, <= 0 for infinite loop. /// - parameter loopCount: Desired number of loops, <= 0 for infinite loop.
public func animate(withGIFData imageData: Data, loopCount: Int = 0) { /// - parameter completionHandler: Completion callback function
public func animate(withGIFData imageData: Data, loopCount: Int = 0, completionHandler: (() -> Void)? = nil) {
animator?.animate(withGIFData: imageData, animator?.animate(withGIFData: imageData,
size: frame.size, size: frame.size,
contentMode: contentMode, contentMode: contentMode,
loopCount: loopCount) loopCount: loopCount,
completionHandler: completionHandler)
} }
/// Prepare for animation and start animating immediately. /// Prepare for animation and start animating immediately.
/// ///
/// - parameter imageURL: GIF image url. /// - parameter imageURL: GIF image url.
/// - parameter loopCount: Desired number of loops, <= 0 for infinite loop. /// - parameter loopCount: Desired number of loops, <= 0 for infinite loop.
public func animate(withGIFURL imageURL: URL, loopCount: Int = 0) { /// - parameter completionHandler: Completion callback function
public func animate(withGIFURL imageURL: URL, loopCount: Int = 0, completionHandler: (() -> Void)? = nil) {
let session = URLSession.shared let session = URLSession.shared
let task = session.dataTask(with: imageURL) { (data, response, error) in let task = session.dataTask(with: imageURL) { (data, response, error) in
@ -85,7 +90,7 @@ extension GIFAnimatable {
print("Error downloading gif:", error.localizedDescription, "at url:", imageURL.absoluteString) print("Error downloading gif:", error.localizedDescription, "at url:", imageURL.absoluteString)
case (let data?, _, _): case (let data?, _, _):
DispatchQueue.main.async { DispatchQueue.main.async {
self.animate(withGIFData: data, loopCount: loopCount) self.animate(withGIFData: data, loopCount: loopCount, completionHandler: completionHandler)
} }
default: () default: ()
} }

View File

@ -19,7 +19,7 @@
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>135</string> <string>136</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
<string></string> <string></string>
</dict> </dict>