Use image view subclass
This commit is contained in:
parent
d6a7b29aaf
commit
b15e7e3399
|
@ -1,8 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6206.9" systemVersion="13E28" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="vXZ-lx-hvc">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6254" systemVersion="14C99d" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="vXZ-lx-hvc">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7026.1"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
|
||||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--Gifu-->
|
<!--Gifu-->
|
||||||
|
@ -17,13 +16,27 @@
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="FSz-xF-Xds">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Gifu" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="c8Y-41-BaC">
|
||||||
|
<rect key="frame" x="283" y="45" width="42" height="20.5"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="42" id="1Mg-oH-beY"/>
|
||||||
|
</constraints>
|
||||||
|
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||||
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Not the Japanese prefecture." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wsv-cU-WO5">
|
||||||
|
<rect key="frame" x="217" y="69" width="175" height="13.5"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="175" id="qtB-pf-agE"/>
|
||||||
|
</constraints>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="11"/>
|
||||||
|
<color key="textColor" red="1" green="1" blue="1" alpha="0.5" colorSpace="calibratedRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" placeholderIntrinsicWidth="450" placeholderIntrinsicHeight="300" translatesAutoresizingMaskIntoConstraints="NO" id="FSz-xF-Xds" customClass="AnimatableImageView" customModule="Gifu">
|
||||||
<rect key="frame" x="75" y="150" width="450" height="300"/>
|
<rect key="frame" x="75" y="150" width="450" height="300"/>
|
||||||
<color key="backgroundColor" red="0.7725490196" green="0.1764705882" blue="0.1843137255" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="backgroundColor" red="0.7725490196" green="0.1764705882" blue="0.1843137255" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="300" id="oev-E3-JrW"/>
|
|
||||||
<constraint firstAttribute="width" secondItem="FSz-xF-Xds" secondAttribute="height" multiplier="1.5:1" id="vUc-PW-K4t"/>
|
|
||||||
</constraints>
|
|
||||||
</imageView>
|
</imageView>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bFY-3J-OXr" customClass="FlatButton" customModule="gifu_demo" customModuleProvider="target">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bFY-3J-OXr" customClass="FlatButton" customModule="gifu_demo" customModuleProvider="target">
|
||||||
<rect key="frame" x="275" y="509" width="50" height="50"/>
|
<rect key="frame" x="275" y="509" width="50" height="50"/>
|
||||||
|
@ -44,26 +57,8 @@
|
||||||
<action selector="toggleAnimation:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="PvH-3E-LbB"/>
|
<action selector="toggleAnimation:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="PvH-3E-LbB"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Gifu" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="c8Y-41-BaC">
|
|
||||||
<rect key="frame" x="283" y="45" width="42" height="20.5"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="42" id="1Mg-oH-beY"/>
|
|
||||||
</constraints>
|
|
||||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
|
||||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Not the Japanese prefecture." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wsv-cU-WO5">
|
|
||||||
<rect key="frame" x="217" y="69" width="175" height="13.5"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="width" constant="175" id="qtB-pf-agE"/>
|
|
||||||
</constraints>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="11"/>
|
|
||||||
<color key="textColor" red="1" green="1" blue="1" alpha="0.5" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" red="0.2304944545" green="0.24875254929999999" blue="0.2778563201" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="backgroundColor" red="0.24722154438495636" green="0.26659342646598816" blue="0.2988148033618927" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="centerY" secondItem="bFY-3J-OXr" secondAttribute="centerY" id="09i-ag-RTM"/>
|
<constraint firstAttribute="centerY" secondItem="bFY-3J-OXr" secondAttribute="centerY" id="09i-ag-RTM"/>
|
||||||
<constraint firstItem="bFY-3J-OXr" firstAttribute="top" secondItem="FSz-xF-Xds" secondAttribute="bottom" constant="-73" id="1KN-hB-V2y"/>
|
<constraint firstItem="bFY-3J-OXr" firstAttribute="top" secondItem="FSz-xF-Xds" secondAttribute="bottom" constant="-73" id="1KN-hB-V2y"/>
|
||||||
|
|
|
@ -3,16 +3,13 @@ import Gifu
|
||||||
|
|
||||||
class ViewController: UIViewController {
|
class ViewController: UIViewController {
|
||||||
|
|
||||||
@IBOutlet weak var imageView: UIImageView!
|
@IBOutlet weak var imageView: AnimatableImageView!
|
||||||
@IBOutlet weak var button: FlatButton!
|
@IBOutlet weak var button: FlatButton!
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
if let image = AnimatedImage.animatedImageWithName("mugen.gif") {
|
imageView.animateWithImage(named: "mugen.gif")
|
||||||
imageView.setAnimatedImage(image)
|
|
||||||
imageView.startAnimatingGIF()
|
|
||||||
}
|
|
||||||
|
|
||||||
UIApplication.sharedApplication().setStatusBarStyle(.LightContent, animated: false)
|
UIApplication.sharedApplication().setStatusBarStyle(.LightContent, animated: false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,11 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
005656ED1A6F14D6008A0ED1 /* Animator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005656EC1A6F14D6008A0ED1 /* Animator.swift */; };
|
||||||
|
005656EF1A6F1C26008A0ED1 /* AnimatableImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005656EE1A6F1C26008A0ED1 /* AnimatableImageView.swift */; };
|
||||||
|
005656F11A7042E9008A0ED1 /* Animatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005656F01A7042E9008A0ED1 /* Animatable.swift */; };
|
||||||
006F97011A6EDE7900CB5CE8 /* Runes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 006F97001A6EDE7900CB5CE8 /* Runes.framework */; };
|
006F97011A6EDE7900CB5CE8 /* Runes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 006F97001A6EDE7900CB5CE8 /* Runes.framework */; };
|
||||||
00B8C75E1A364DCE00C188E7 /* AnimatedImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B8C75B1A364DCE00C188E7 /* AnimatedImage.swift */; };
|
|
||||||
00B8C75F1A364DCE00C188E7 /* ImageSourceHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B8C75C1A364DCE00C188E7 /* ImageSourceHelpers.swift */; };
|
00B8C75F1A364DCE00C188E7 /* ImageSourceHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B8C75C1A364DCE00C188E7 /* ImageSourceHelpers.swift */; };
|
||||||
00B8C7601A364DCE00C188E7 /* UIImageView+Gifu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B8C75D1A364DCE00C188E7 /* UIImageView+Gifu.swift */; };
|
|
||||||
00B8C7961A3650EE00C188E7 /* Gifu.h in Headers */ = {isa = PBXBuildFile; fileRef = 00B8C7951A3650EE00C188E7 /* Gifu.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
00B8C7961A3650EE00C188E7 /* Gifu.h in Headers */ = {isa = PBXBuildFile; fileRef = 00B8C7951A3650EE00C188E7 /* Gifu.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
EAF49C7F1A3A4DE000B395DF /* UIImageExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF49C7E1A3A4DE000B395DF /* UIImageExtension.swift */; };
|
EAF49C7F1A3A4DE000B395DF /* UIImageExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF49C7E1A3A4DE000B395DF /* UIImageExtension.swift */; };
|
||||||
EAF49C811A3A4FAA00B395DF /* Curry.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF49C801A3A4FAA00B395DF /* Curry.swift */; };
|
EAF49C811A3A4FAA00B395DF /* Curry.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF49C801A3A4FAA00B395DF /* Curry.swift */; };
|
||||||
|
@ -18,12 +19,13 @@
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
005656EC1A6F14D6008A0ED1 /* Animator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Animator.swift; sourceTree = "<group>"; };
|
||||||
|
005656EE1A6F1C26008A0ED1 /* AnimatableImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatableImageView.swift; sourceTree = "<group>"; };
|
||||||
|
005656F01A7042E9008A0ED1 /* Animatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Animatable.swift; sourceTree = "<group>"; };
|
||||||
006F97001A6EDE7900CB5CE8 /* Runes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Runes.framework; path = "../Carthage/Checkouts/runes/build/Debug-iphoneos/Runes.framework"; sourceTree = "<group>"; };
|
006F97001A6EDE7900CB5CE8 /* Runes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Runes.framework; path = "../Carthage/Checkouts/runes/build/Debug-iphoneos/Runes.framework"; sourceTree = "<group>"; };
|
||||||
00B8C73E1A364DA400C188E7 /* Gifu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Gifu.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
00B8C73E1A364DA400C188E7 /* Gifu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Gifu.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
00B8C7421A364DA400C188E7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../Source/Info.plist; sourceTree = "<group>"; };
|
00B8C7421A364DA400C188E7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../Source/Info.plist; sourceTree = "<group>"; };
|
||||||
00B8C75B1A364DCE00C188E7 /* AnimatedImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatedImage.swift; sourceTree = "<group>"; };
|
|
||||||
00B8C75C1A364DCE00C188E7 /* ImageSourceHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageSourceHelpers.swift; sourceTree = "<group>"; };
|
00B8C75C1A364DCE00C188E7 /* ImageSourceHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageSourceHelpers.swift; sourceTree = "<group>"; };
|
||||||
00B8C75D1A364DCE00C188E7 /* UIImageView+Gifu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+Gifu.swift"; sourceTree = "<group>"; };
|
|
||||||
00B8C7951A3650EE00C188E7 /* Gifu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Gifu.h; sourceTree = "<group>"; };
|
00B8C7951A3650EE00C188E7 /* Gifu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Gifu.h; sourceTree = "<group>"; };
|
||||||
EAF49C7E1A3A4DE000B395DF /* UIImageExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageExtension.swift; sourceTree = "<group>"; };
|
EAF49C7E1A3A4DE000B395DF /* UIImageExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageExtension.swift; sourceTree = "<group>"; };
|
||||||
EAF49C801A3A4FAA00B395DF /* Curry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Curry.swift; sourceTree = "<group>"; };
|
EAF49C801A3A4FAA00B395DF /* Curry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Curry.swift; sourceTree = "<group>"; };
|
||||||
|
@ -72,13 +74,14 @@
|
||||||
00B8C75A1A364DBE00C188E7 /* Source */ = {
|
00B8C75A1A364DBE00C188E7 /* Source */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
005656EE1A6F1C26008A0ED1 /* AnimatableImageView.swift */,
|
||||||
|
EAF49CB01A3B6EEB00B395DF /* AnimatedFrame.swift */,
|
||||||
|
005656EC1A6F14D6008A0ED1 /* Animator.swift */,
|
||||||
|
005656F01A7042E9008A0ED1 /* Animatable.swift */,
|
||||||
00B8C7951A3650EE00C188E7 /* Gifu.h */,
|
00B8C7951A3650EE00C188E7 /* Gifu.h */,
|
||||||
00B8C75B1A364DCE00C188E7 /* AnimatedImage.swift */,
|
|
||||||
00B8C75C1A364DCE00C188E7 /* ImageSourceHelpers.swift */,
|
00B8C75C1A364DCE00C188E7 /* ImageSourceHelpers.swift */,
|
||||||
00B8C75D1A364DCE00C188E7 /* UIImageView+Gifu.swift */,
|
|
||||||
EAF49C7E1A3A4DE000B395DF /* UIImageExtension.swift */,
|
EAF49C7E1A3A4DE000B395DF /* UIImageExtension.swift */,
|
||||||
EAF49C801A3A4FAA00B395DF /* Curry.swift */,
|
EAF49C801A3A4FAA00B395DF /* Curry.swift */,
|
||||||
EAF49CB01A3B6EEB00B395DF /* AnimatedFrame.swift */,
|
|
||||||
);
|
);
|
||||||
path = Source;
|
path = Source;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -161,11 +164,12 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
005656EF1A6F1C26008A0ED1 /* AnimatableImageView.swift in Sources */,
|
||||||
|
005656ED1A6F14D6008A0ED1 /* Animator.swift in Sources */,
|
||||||
EAF49CB11A3B6EEB00B395DF /* AnimatedFrame.swift in Sources */,
|
EAF49CB11A3B6EEB00B395DF /* AnimatedFrame.swift in Sources */,
|
||||||
00B8C7601A364DCE00C188E7 /* UIImageView+Gifu.swift in Sources */,
|
005656F11A7042E9008A0ED1 /* Animatable.swift in Sources */,
|
||||||
00B8C75F1A364DCE00C188E7 /* ImageSourceHelpers.swift in Sources */,
|
00B8C75F1A364DCE00C188E7 /* ImageSourceHelpers.swift in Sources */,
|
||||||
EAF49C811A3A4FAA00B395DF /* Curry.swift in Sources */,
|
EAF49C811A3A4FAA00B395DF /* Curry.swift in Sources */,
|
||||||
00B8C75E1A364DCE00C188E7 /* AnimatedImage.swift in Sources */,
|
|
||||||
EAF49C7F1A3A4DE000B395DF /* UIImageExtension.swift in Sources */,
|
EAF49C7F1A3A4DE000B395DF /* UIImageExtension.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
protocol Animatable {
|
||||||
|
var layer: CALayer { get }
|
||||||
|
var frame: CGRect { get }
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
import UIKit
|
||||||
|
import ImageIO
|
||||||
|
import Runes
|
||||||
|
|
||||||
|
public class AnimatableImageView: UIImageView, Animatable {
|
||||||
|
var animator: Animator?
|
||||||
|
|
||||||
|
public var isAnimatingGIF: Bool {
|
||||||
|
return animator?.isAnimating ?? isAnimating()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func prepareForAnimation(imageNamed imageName: String) {
|
||||||
|
let path = NSBundle.mainBundle().bundlePath.stringByAppendingPathComponent(imageName)
|
||||||
|
prepareForAnimation <^> NSData(contentsOfFile: path)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func prepareForAnimation(imageData data: NSData) {
|
||||||
|
image = UIImage(data: data)
|
||||||
|
animator = Animator(data: data, delegate: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func animateWithImage(named imageName: String) {
|
||||||
|
prepareForAnimation(imageNamed: imageName)
|
||||||
|
startAnimatingGIF()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func animateWithImageData(#data: NSData) {
|
||||||
|
prepareForAnimation(imageData: data)
|
||||||
|
startAnimatingGIF()
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func displayLayer(layer: CALayer!) {
|
||||||
|
image = animator?.currentFrame?
|
||||||
|
}
|
||||||
|
|
||||||
|
public func startAnimatingGIF() {
|
||||||
|
animator?.resumeAnimation() ?? startAnimating()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func stopAnimatingGIF() {
|
||||||
|
animator?.pauseAnimation() ?? stopAnimating()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,77 +2,33 @@ import UIKit
|
||||||
import ImageIO
|
import ImageIO
|
||||||
import Runes
|
import Runes
|
||||||
|
|
||||||
public class AnimatedImage: UIImage {
|
class Animator: NSObject {
|
||||||
// MARK: - Constants
|
|
||||||
let maxTimeStep = 1.0
|
let maxTimeStep = 1.0
|
||||||
|
|
||||||
// MARK: - Public Properties
|
|
||||||
var delegate: UIImageView?
|
|
||||||
var animatedFrames = [AnimatedFrame]()
|
var animatedFrames = [AnimatedFrame]()
|
||||||
var totalDuration: NSTimeInterval = 0.0
|
var totalDuration: NSTimeInterval = 0.0
|
||||||
|
let delegate: Animatable
|
||||||
override public var size: CGSize {
|
|
||||||
return frameAtIndex(0)?.size ?? CGSizeZero
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Private Properties
|
|
||||||
private lazy var displayLink: CADisplayLink = CADisplayLink(target: self, selector: "updateCurrentFrame")
|
|
||||||
private var currentFrameIndex = 0
|
private var currentFrameIndex = 0
|
||||||
private var timeSinceLastFrameChange: NSTimeInterval = 0.0
|
private var timeSinceLastFrameChange: NSTimeInterval = 0.0
|
||||||
|
private lazy var displayLink: CADisplayLink = CADisplayLink(target: self, selector: "updateCurrentFrame")
|
||||||
|
|
||||||
// MARK: - Computed Properties
|
|
||||||
var currentFrame: UIImage? {
|
var currentFrame: UIImage? {
|
||||||
return frameAtIndex(currentFrameIndex)
|
return frameAtIndex(currentFrameIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isAnimated: Bool {
|
var isAnimating: Bool {
|
||||||
return totalDuration != 0.0
|
return !displayLink.paused
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Initializers
|
required init(data: NSData, delegate: Animatable) {
|
||||||
required public init(coder aDecoder: NSCoder) {
|
|
||||||
super.init(coder: aDecoder)
|
|
||||||
}
|
|
||||||
|
|
||||||
public override convenience init(data: NSData) {
|
|
||||||
self.init(data: data, size: CGSizeZero)
|
|
||||||
}
|
|
||||||
|
|
||||||
required public init(data: NSData, size: CGSize) {
|
|
||||||
super.init()
|
|
||||||
|
|
||||||
let imageSource = CGImageSourceCreateWithData(data, nil)
|
let imageSource = CGImageSourceCreateWithData(data, nil)
|
||||||
|
self.delegate = delegate
|
||||||
|
super.init()
|
||||||
attachDisplayLink()
|
attachDisplayLink()
|
||||||
curry(prepareFrames) <^> imageSource <*> size
|
curry(prepareFrames) <^> imageSource <*> delegate.frame.size
|
||||||
pauseAnimation()
|
pauseAnimation()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Factories
|
// MARK: - Frames
|
||||||
public class func animatedImageWithName(name: String) -> AnimatedImage? {
|
|
||||||
let path = NSBundle.mainBundle().bundlePath.stringByAppendingPathComponent(name)
|
|
||||||
return animatedImageWithData <^> NSData(contentsOfFile: path)
|
|
||||||
}
|
|
||||||
|
|
||||||
public class func animatedImageWithData(data: NSData) -> AnimatedImage {
|
|
||||||
let size = UIImage.sizeForImageData(data) ?? CGSizeZero
|
|
||||||
return self(data: data, size: size)
|
|
||||||
}
|
|
||||||
|
|
||||||
public class func animatedImageWithName(name: String, size: CGSize) -> AnimatedImage? {
|
|
||||||
let path = NSBundle.mainBundle().bundlePath.stringByAppendingPathComponent(name)
|
|
||||||
return curry(animatedImageWithData) <^> NSData(contentsOfFile: path) <*> size
|
|
||||||
}
|
|
||||||
|
|
||||||
public class func animatedImageWithData(data: NSData, size: CGSize) -> AnimatedImage {
|
|
||||||
return self(data: data, size: size)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Display Link Helpers
|
|
||||||
func attachDisplayLink() {
|
|
||||||
displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Frame Methods
|
|
||||||
private func prepareFrames(imageSource: CGImageSourceRef, size: CGSize) {
|
private func prepareFrames(imageSource: CGImageSourceRef, size: CGSize) {
|
||||||
let numberOfFrames = Int(CGImageSourceGetCount(imageSource))
|
let numberOfFrames = Int(CGImageSourceGetCount(imageSource))
|
||||||
animatedFrames.reserveCapacity(numberOfFrames)
|
animatedFrames.reserveCapacity(numberOfFrames)
|
||||||
|
@ -96,7 +52,7 @@ public class AnimatedImage: UIImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateCurrentFrame() {
|
func updateCurrentFrame() {
|
||||||
if !isAnimated { return }
|
if totalDuration == 0 { return }
|
||||||
|
|
||||||
timeSinceLastFrameChange += min(maxTimeStep, displayLink.duration)
|
timeSinceLastFrameChange += min(maxTimeStep, displayLink.duration)
|
||||||
var frameDuration = animatedFrames[currentFrameIndex].duration
|
var frameDuration = animatedFrames[currentFrameIndex].duration
|
||||||
|
@ -104,7 +60,7 @@ public class AnimatedImage: UIImage {
|
||||||
if timeSinceLastFrameChange >= frameDuration {
|
if timeSinceLastFrameChange >= frameDuration {
|
||||||
timeSinceLastFrameChange -= frameDuration
|
timeSinceLastFrameChange -= frameDuration
|
||||||
currentFrameIndex = ++currentFrameIndex % animatedFrames.count
|
currentFrameIndex = ++currentFrameIndex % animatedFrames.count
|
||||||
delegate?.layer.setNeedsDisplay()
|
delegate.layer.setNeedsDisplay()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,12 +70,12 @@ public class AnimatedImage: UIImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
func resumeAnimation() {
|
func resumeAnimation() {
|
||||||
if isAnimated {
|
if totalDuration > 0 {
|
||||||
displayLink.paused = false
|
displayLink.paused = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isAnimating() -> Bool {
|
func attachDisplayLink() {
|
||||||
return !displayLink.paused
|
displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,37 +0,0 @@
|
||||||
import UIKit
|
|
||||||
|
|
||||||
public extension UIImageView {
|
|
||||||
// MARK: - Computed Properties
|
|
||||||
var animatableImage: AnimatedImage? {
|
|
||||||
return image as? AnimatedImage
|
|
||||||
}
|
|
||||||
|
|
||||||
var isAnimatingGIF: Bool {
|
|
||||||
return animatableImage?.isAnimating() ?? isAnimating()
|
|
||||||
}
|
|
||||||
|
|
||||||
var animatable: Bool {
|
|
||||||
return animatableImage != .None
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Method Overrides
|
|
||||||
override public func displayLayer(layer: CALayer!) {
|
|
||||||
layer.contents = animatableImage?.currentFrame?.CGImage
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Setter Methods
|
|
||||||
public func setAnimatedImage(image: AnimatedImage) {
|
|
||||||
image.delegate = self
|
|
||||||
self.image = image
|
|
||||||
layer.setNeedsDisplay()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Animation
|
|
||||||
func startAnimatingGIF() {
|
|
||||||
animatableImage?.resumeAnimation() ?? startAnimating()
|
|
||||||
}
|
|
||||||
|
|
||||||
func stopAnimatingGIF() {
|
|
||||||
animatableImage?.pauseAnimation() ?? stopAnimating()
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue