parent
ae615d4577
commit
534a9b5f29
|
@ -8,8 +8,10 @@ class ViewController: UIViewController {
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
imageView.setAnimatableImage(named: "mugen.gif")
|
if let image = AnimatedImage.animatedImageWithName("mugen.gif") {
|
||||||
|
imageView.setAnimatedImage(image)
|
||||||
imageView.startAnimatingGIF()
|
imageView.startAnimatingGIF()
|
||||||
|
}
|
||||||
UIApplication.sharedApplication().setStatusBarStyle(.LightContent, animated: false)
|
UIApplication.sharedApplication().setStatusBarStyle(.LightContent, animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,19 +7,25 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
00B8C75E1A364DCE00C188E7 /* Gifu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B8C75B1A364DCE00C188E7 /* Gifu.swift */; };
|
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 */; };
|
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 */; };
|
||||||
|
EAF49C811A3A4FAA00B395DF /* Functional.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF49C801A3A4FAA00B395DF /* Functional.swift */; };
|
||||||
|
EAF49CB11A3B6EEB00B395DF /* AnimatedFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF49CB01A3B6EEB00B395DF /* AnimatedFrame.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
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 /* Gifu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Gifu.swift; 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>"; };
|
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>"; };
|
||||||
|
EAF49C801A3A4FAA00B395DF /* Functional.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Functional.swift; sourceTree = "<group>"; };
|
||||||
|
EAF49CB01A3B6EEB00B395DF /* AnimatedFrame.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatedFrame.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -63,9 +69,12 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
00B8C7951A3650EE00C188E7 /* Gifu.h */,
|
00B8C7951A3650EE00C188E7 /* Gifu.h */,
|
||||||
00B8C75B1A364DCE00C188E7 /* Gifu.swift */,
|
00B8C75B1A364DCE00C188E7 /* AnimatedImage.swift */,
|
||||||
00B8C75C1A364DCE00C188E7 /* ImageSourceHelpers.swift */,
|
00B8C75C1A364DCE00C188E7 /* ImageSourceHelpers.swift */,
|
||||||
00B8C75D1A364DCE00C188E7 /* UIImageView+Gifu.swift */,
|
00B8C75D1A364DCE00C188E7 /* UIImageView+Gifu.swift */,
|
||||||
|
EAF49C7E1A3A4DE000B395DF /* UIImageExtension.swift */,
|
||||||
|
EAF49C801A3A4FAA00B395DF /* Functional.swift */,
|
||||||
|
EAF49CB01A3B6EEB00B395DF /* AnimatedFrame.swift */,
|
||||||
);
|
);
|
||||||
path = Source;
|
path = Source;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -148,9 +157,12 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
EAF49CB11A3B6EEB00B395DF /* AnimatedFrame.swift in Sources */,
|
||||||
00B8C7601A364DCE00C188E7 /* UIImageView+Gifu.swift in Sources */,
|
00B8C7601A364DCE00C188E7 /* UIImageView+Gifu.swift in Sources */,
|
||||||
00B8C75F1A364DCE00C188E7 /* ImageSourceHelpers.swift in Sources */,
|
00B8C75F1A364DCE00C188E7 /* ImageSourceHelpers.swift in Sources */,
|
||||||
00B8C75E1A364DCE00C188E7 /* Gifu.swift in Sources */,
|
EAF49C811A3A4FAA00B395DF /* Functional.swift in Sources */,
|
||||||
|
00B8C75E1A364DCE00C188E7 /* AnimatedImage.swift in Sources */,
|
||||||
|
EAF49C7F1A3A4DE000B395DF /* UIImageExtension.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
struct AnimatedFrame {
|
||||||
|
let image: UIImage?
|
||||||
|
let duration: NSTimeInterval
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
import UIKit
|
||||||
|
import ImageIO
|
||||||
|
|
||||||
|
public class AnimatedImage: UIImage {
|
||||||
|
// MARK: - Constants
|
||||||
|
let maxTimeStep = 1.0
|
||||||
|
|
||||||
|
// MARK: - Public Properties
|
||||||
|
var delegate: UIImageView?
|
||||||
|
var animatedFrames = [AnimatedFrame]()
|
||||||
|
var totalDuration: NSTimeInterval = 0.0
|
||||||
|
|
||||||
|
// MARK: - Private Properties
|
||||||
|
private lazy var displayLink: CADisplayLink = CADisplayLink(target: self, selector: "updateCurrentFrame")
|
||||||
|
private var currentFrameIndex = 0
|
||||||
|
private var timeSinceLastFrameChange: NSTimeInterval = 0.0
|
||||||
|
|
||||||
|
// MARK: - Computed Properties
|
||||||
|
var currentFrame: UIImage? {
|
||||||
|
return frameAtIndex(currentFrameIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var isAnimated: Bool {
|
||||||
|
return totalDuration != 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Initializers
|
||||||
|
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)
|
||||||
|
attachDisplayLink()
|
||||||
|
curry(prepareFrames) <^> imageSource <*> size
|
||||||
|
pauseAnimation()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Factories
|
||||||
|
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) {
|
||||||
|
let numberOfFrames = Int(CGImageSourceGetCount(imageSource))
|
||||||
|
animatedFrames.reserveCapacity(numberOfFrames)
|
||||||
|
|
||||||
|
(animatedFrames, totalDuration) = reduce(0..<numberOfFrames, ([AnimatedFrame](), 0.0)) { accum, index in
|
||||||
|
let frameDuration = CGImageSourceGIFFrameDuration(imageSource, index)
|
||||||
|
let frameImageRef = CGImageSourceCreateImageAtIndex(imageSource, UInt(index), nil)
|
||||||
|
let frame = UIImage(CGImage: frameImageRef)?.resize(size)
|
||||||
|
let animatedFrame = AnimatedFrame(image: frame, duration: frameDuration)
|
||||||
|
return (accum.0 + [animatedFrame], accum.1 + frameDuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func frameAtIndex(index: Int) -> UIImage? {
|
||||||
|
if index >= animatedFrames.count { return .None }
|
||||||
|
return animatedFrames[index].image
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateCurrentFrame() {
|
||||||
|
if !isAnimated { return }
|
||||||
|
|
||||||
|
timeSinceLastFrameChange += min(maxTimeStep, displayLink.duration)
|
||||||
|
var frameDuration = animatedFrames[currentFrameIndex].duration
|
||||||
|
|
||||||
|
if timeSinceLastFrameChange >= frameDuration {
|
||||||
|
timeSinceLastFrameChange -= frameDuration
|
||||||
|
currentFrameIndex = ++currentFrameIndex % animatedFrames.count
|
||||||
|
delegate?.layer.setNeedsDisplay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Animation
|
||||||
|
func pauseAnimation() {
|
||||||
|
displayLink.paused = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func resumeAnimation() {
|
||||||
|
if isAnimated {
|
||||||
|
displayLink.paused = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAnimating() -> Bool {
|
||||||
|
return !displayLink.paused
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
infix operator >>- { associativity left precedence 150 }
|
||||||
|
infix operator <^> { associativity left precedence 150 }
|
||||||
|
infix operator <*> { associativity left precedence 150 }
|
||||||
|
|
||||||
|
func >>-<A, B>(a: A?, f: A -> B?) -> B? {
|
||||||
|
switch a {
|
||||||
|
case let .Some(x): return f(x)
|
||||||
|
case .None: return .None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func <^><A, B>(f: A -> B, a: A?) -> B? {
|
||||||
|
switch a {
|
||||||
|
case let .Some(x): return f(x)
|
||||||
|
case .None: return .None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func <*><A, B>(f: (A -> B)?, a: A?) -> B? {
|
||||||
|
switch f {
|
||||||
|
case let .Some(fx): return fx <^> a
|
||||||
|
case .None: return .None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func curry<A, B, C>(f: (A, B) -> C) -> A -> B -> C {
|
||||||
|
return { a in { b in f(a, b) } }
|
||||||
|
}
|
|
@ -1,151 +0,0 @@
|
||||||
import UIKit
|
|
||||||
import ImageIO
|
|
||||||
|
|
||||||
public class AnimatedImage: UIImage {
|
|
||||||
// MARK: - Constants
|
|
||||||
let framesToPreload = 10
|
|
||||||
let maxTimeStep = 1.0
|
|
||||||
|
|
||||||
// MARK: - Public Properties
|
|
||||||
var delegate: UIImageView?
|
|
||||||
var frameDurations = [NSTimeInterval]()
|
|
||||||
var frames = [UIImage?]()
|
|
||||||
var totalDuration: NSTimeInterval = 0.0
|
|
||||||
|
|
||||||
// MARK: - Private Properties
|
|
||||||
private lazy var displayLink: CADisplayLink = CADisplayLink(target: self, selector: "updateCurrentFrame")
|
|
||||||
private lazy var preloadFrameQueue = dispatch_queue_create("co.kaishin.GIFPreloadImages", DISPATCH_QUEUE_SERIAL)
|
|
||||||
private var currentFrameIndex = 0
|
|
||||||
private var imageSource: CGImageSource?
|
|
||||||
private var timeSinceLastFrameChange: NSTimeInterval = 0.0
|
|
||||||
|
|
||||||
// MARK: - Computed Properties
|
|
||||||
var currentFrame: UIImage? {
|
|
||||||
return frameAtIndex(currentFrameIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var isAnimated: Bool {
|
|
||||||
return imageSource != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Initializers
|
|
||||||
required public init(coder aDecoder: NSCoder) {
|
|
||||||
super.init(coder: aDecoder)
|
|
||||||
}
|
|
||||||
|
|
||||||
required public init(data: NSData, delegate: UIImageView?) {
|
|
||||||
let imageSource = CGImageSourceCreateWithData(data, nil)
|
|
||||||
self.delegate = delegate
|
|
||||||
|
|
||||||
if CGImageSourceContainsAnimatedGIF(imageSource) {
|
|
||||||
super.init()
|
|
||||||
attachDisplayLink()
|
|
||||||
prepareFrames(imageSource)
|
|
||||||
pauseAnimation()
|
|
||||||
} else {
|
|
||||||
super.init(data: data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Factories
|
|
||||||
class func imageWithName(name: String, delegate: UIImageView?) -> Self? {
|
|
||||||
let path = NSBundle.mainBundle().bundlePath.stringByAppendingPathComponent(name)
|
|
||||||
|
|
||||||
if let data = NSData(contentsOfFile: path) {
|
|
||||||
return imageWithData(data, delegate: delegate)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
class func imageWithData(data: NSData, delegate: UIImageView?) -> Self? {
|
|
||||||
return self(data: data, delegate: delegate)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Display Link Helpers
|
|
||||||
func attachDisplayLink() {
|
|
||||||
displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Frame Methods
|
|
||||||
private func prepareFrames(source: CGImageSource!) {
|
|
||||||
imageSource = source
|
|
||||||
|
|
||||||
let numberOfFrames = Int(CGImageSourceGetCount(self.imageSource))
|
|
||||||
frameDurations.reserveCapacity(numberOfFrames)
|
|
||||||
frames.reserveCapacity(numberOfFrames)
|
|
||||||
|
|
||||||
for index in 0..<numberOfFrames {
|
|
||||||
let frameDuration = CGImageSourceGIFFrameDuration(source, index)
|
|
||||||
frameDurations.append(frameDuration)
|
|
||||||
totalDuration += frameDuration
|
|
||||||
|
|
||||||
if index < framesToPreload {
|
|
||||||
let frameImageRef = CGImageSourceCreateImageAtIndex(self.imageSource, UInt(index), nil)
|
|
||||||
let frame = UIImage(CGImage: frameImageRef, scale: 0.0, orientation: UIImageOrientation.Up)
|
|
||||||
frames.append(frame)
|
|
||||||
} else {
|
|
||||||
frames.append(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func frameAtIndex(index: Int) -> UIImage? {
|
|
||||||
if Int(index) >= self.frames.count { return nil }
|
|
||||||
|
|
||||||
var image: UIImage? = self.frames[Int(index)]
|
|
||||||
updatePreloadedFramesAtIndex(index)
|
|
||||||
|
|
||||||
return image
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updatePreloadedFramesAtIndex(index: Int) {
|
|
||||||
if frames.count <= framesToPreload { return }
|
|
||||||
|
|
||||||
if index != 0 {
|
|
||||||
frames[index] = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for internalIndex in (index + 1)...(index + framesToPreload) {
|
|
||||||
let adjustedIndex = internalIndex % frames.count
|
|
||||||
|
|
||||||
if frames[adjustedIndex] == nil {
|
|
||||||
dispatch_async(preloadFrameQueue) {
|
|
||||||
let frameImageRef = CGImageSourceCreateImageAtIndex(self.imageSource, UInt(adjustedIndex), nil)
|
|
||||||
self.frames[adjustedIndex] = UIImage(CGImage: frameImageRef)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateCurrentFrame() {
|
|
||||||
if !isAnimated { return }
|
|
||||||
|
|
||||||
timeSinceLastFrameChange += min(maxTimeStep, displayLink.duration)
|
|
||||||
var frameDuration = frameDurations[currentFrameIndex]
|
|
||||||
|
|
||||||
while timeSinceLastFrameChange >= frameDuration {
|
|
||||||
timeSinceLastFrameChange -= frameDuration
|
|
||||||
currentFrameIndex++
|
|
||||||
|
|
||||||
if currentFrameIndex >= frames.count {
|
|
||||||
currentFrameIndex = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate?.layer.setNeedsDisplay()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Animation
|
|
||||||
func pauseAnimation() {
|
|
||||||
displayLink.paused = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func resumeAnimation() {
|
|
||||||
displayLink.paused = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isAnimating() -> Bool {
|
|
||||||
return !displayLink.paused
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@ import UIKit
|
||||||
import ImageIO
|
import ImageIO
|
||||||
import MobileCoreServices
|
import MobileCoreServices
|
||||||
|
|
||||||
func CGImageSourceContainsAnimatedGIF(imageSource: CGImageSource) -> Bool {
|
private func CGImageSourceContainsAnimatedGIF(imageSource: CGImageSource) -> Bool {
|
||||||
let isTypeGIF = UTTypeConformsTo(CGImageSourceGetType(imageSource), kUTTypeGIF)
|
let isTypeGIF = UTTypeConformsTo(CGImageSourceGetType(imageSource), kUTTypeGIF)
|
||||||
let imageCount = CGImageSourceGetCount(imageSource)
|
let imageCount = CGImageSourceGetCount(imageSource)
|
||||||
return isTypeGIF != 0 && imageCount > 1
|
return isTypeGIF != 0 && imageCount > 1
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
extension UIImage {
|
||||||
|
func resize(size: CGSize) -> UIImage {
|
||||||
|
UIGraphicsBeginImageContext(size)
|
||||||
|
self.drawInRect(CGRectMake(0, 0, size.width, size.height))
|
||||||
|
let newImage = UIGraphicsGetImageFromCurrentImageContext()
|
||||||
|
UIGraphicsEndImageContext()
|
||||||
|
return newImage
|
||||||
|
}
|
||||||
|
|
||||||
|
class func imageWithData(data: NSData, size: CGSize) -> UIImage? {
|
||||||
|
return UIImage(data: data)?.resize(size)
|
||||||
|
}
|
||||||
|
class func sizeForImageData(data: NSData) -> CGSize? {
|
||||||
|
return UIImage(data: data)?.size
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,11 +3,7 @@ import UIKit
|
||||||
public extension UIImageView {
|
public extension UIImageView {
|
||||||
// MARK: - Computed Properties
|
// MARK: - Computed Properties
|
||||||
var animatableImage: AnimatedImage? {
|
var animatableImage: AnimatedImage? {
|
||||||
if image is AnimatedImage {
|
|
||||||
return image as? AnimatedImage
|
return image as? AnimatedImage
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var isAnimatingGIF: Bool {
|
var isAnimatingGIF: Bool {
|
||||||
|
@ -15,43 +11,27 @@ public extension UIImageView {
|
||||||
}
|
}
|
||||||
|
|
||||||
var animatable: Bool {
|
var animatable: Bool {
|
||||||
return animatableImage != nil
|
return animatableImage != .None
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Method Overrides
|
// MARK: - Method Overrides
|
||||||
override public func displayLayer(layer: CALayer!) {
|
override public func displayLayer(layer: CALayer!) {
|
||||||
if let image = animatableImage {
|
layer.contents = animatableImage?.currentFrame?.CGImage
|
||||||
if let frame = image.currentFrame {
|
|
||||||
layer.contents = frame.CGImage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Setter Methods
|
// MARK: - Setter Methods
|
||||||
func setAnimatableImage(named name: String) {
|
public func setAnimatedImage(image: AnimatedImage) {
|
||||||
image = AnimatedImage.imageWithName(name, delegate: self)
|
image.delegate = self
|
||||||
layer.setNeedsDisplay()
|
self.image = image
|
||||||
}
|
|
||||||
|
|
||||||
func setAnimatableImage(#data: NSData) {
|
|
||||||
image = AnimatedImage.imageWithData(data, delegate: self)
|
|
||||||
layer.setNeedsDisplay()
|
layer.setNeedsDisplay()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Animation
|
// MARK: - Animation
|
||||||
func startAnimatingGIF() {
|
func startAnimatingGIF() {
|
||||||
if animatable {
|
animatableImage?.resumeAnimation() ?? startAnimating()
|
||||||
animatableImage!.resumeAnimation()
|
|
||||||
} else {
|
|
||||||
startAnimating()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopAnimatingGIF() {
|
func stopAnimatingGIF() {
|
||||||
if animatable {
|
animatableImage?.pauseAnimation() ?? stopAnimating()
|
||||||
animatableImage!.pauseAnimation()
|
|
||||||
} else {
|
|
||||||
stopAnimating()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue