diff --git a/PPSwiftGifs/PPSwiftGifs.swift b/PPSwiftGifs/PPSwiftGifs.swift index a51ae93..b06c4fb 100644 --- a/PPSwiftGifs/PPSwiftGifs.swift +++ b/PPSwiftGifs/PPSwiftGifs.swift @@ -6,135 +6,138 @@ // Copyright (c) 2014 Peter Prokop. All rights reserved. // -import Foundation -import UIKit import CoreFoundation +import Foundation import ImageIO +import UIKit -public class PPSwiftGifs -{ +public class PPSwiftGifs { // MARK: Public - public class func animatedImageWithGIFNamed(name: String!) -> UIImage? { - let screenScale = Int(UIScreen.mainScreen().scale) + + public class func animatedImageWithGIF(named name: String!) -> UIImage? { + let screenScale = Int(UIScreen.main.scale) let possibleScales = [1, 2, 3] - let orderedScales = [screenScale] + possibleScales.filter{$0 != screenScale} - - let tmp = orderedScales.map{["@" + String($0) + "x", "@" + String($0) + "X"]} - let orderedSuffixes = tmp.reduce([], combine: +) + [""] - + let orderedScales = [screenScale] + possibleScales.filter { $0 != screenScale } + + let suffixes = orderedScales.map { ["@\($0)x", "@\($0)X"] } + let orderedSuffixes = suffixes.flatMap { $0 } + [""] + for suffix in orderedSuffixes { - if let url = NSBundle.mainBundle().URLForResource(name + suffix, withExtension: "gif") { - let source = CGImageSourceCreateWithURL(url, nil) - - return animatedImageWithImageSource(source) + if let url = Bundle.main.url(forResource: name + suffix, withExtension: "gif"), + let source = CGImageSourceCreateWithURL(url as CFURL, nil) + { + return animatedImageWithImageSource(source: source) } } - + return nil } - - public class func animatedImageWithGIFData(data: NSData!) -> UIImage? { - if let source = CGImageSourceCreateWithData(data, nil) { - return animatedImageWithImageSource(source) + + public class func animatedImageWithGIF(data: Data) -> UIImage? { + if let source = CGImageSourceCreateWithData(data as NSData, nil) { + return animatedImageWithImageSource(source: source) } - + return nil } - + // MARK: Private - private class func animatedImageWithImageSource (source: CGImageSourceRef) -> UIImage? { - let (images, delays) = createImagesAndDelays(source); - let totalDuration = delays.reduce(0, combine: +) - let frames = frameArray(images, delays, totalDuration) - + + private class func animatedImageWithImageSource(source: CGImageSource) -> UIImage? { + let (images, delays) = createImagesAndDelays(source: source) + let totalDuration = delays.reduce(0, +) + let frames = frameArray( + images: images, + delays: delays, + totalDuration: totalDuration + ) + // All durations in GIF are in 1/100th of second - let duration = NSTimeInterval(Double(totalDuration)/100.0) - let animation = UIImage.animatedImageWithImages(frames, duration: duration) - + let duration = TimeInterval(Double(totalDuration) / 100.0) + let animation = UIImage.animatedImage(with: frames, duration: duration) + return animation } - - private class func createImagesAndDelays(source: CGImageSourceRef) -> (Array, Array) { + + private class func createImagesAndDelays(source: CGImageSource) -> ([CGImage], [Int]) { let count = Int(CGImageSourceGetCount(source)) - - var images = Array() - var delays = Array() - - for i in 0 ..< count { - images.append(CGImageSourceCreateImageAtIndex(source, i, nil)) - delays.append(delayForImageAtIndex(source, UInt(i))) + + var images = [CGImage]() + var delays = [Int]() + + for i in 0.. Int { + + private class func delayForImage(source: CGImageSource, at i: Int) -> Int { var delay = 1 - - let properties = CGImageSourceCopyPropertiesAtIndex(source, Int(i), nil) - - if (properties != nil) { - let gifDictionaryProperty = unsafeBitCast(kCGImagePropertyGIFDictionary, UnsafePointer.self) + + let properties = CGImageSourceCopyPropertiesAtIndex(source, i, nil) + + if properties != nil { + let gifDictionaryProperty = unsafeBitCast(kCGImagePropertyGIFDictionary, to: UnsafeRawPointer.self) let gifProperties = CFDictionaryGetValue(properties, gifDictionaryProperty) - - if (gifProperties != nil) { - let gifPropertiesCFD = unsafeBitCast(gifProperties, CFDictionary.self) - - let unclampedDelayTimeProperty = unsafeBitCast(kCGImagePropertyGIFUnclampedDelayTime, UnsafePointer.self) - var number = unsafeBitCast(CFDictionaryGetValue(gifPropertiesCFD, unclampedDelayTimeProperty), NSNumber.self); - - if (number.doubleValue == 0) { - let delayTimeProperty = unsafeBitCast(kCGImagePropertyGIFDelayTime, UnsafePointer.self) - number = unsafeBitCast(CFDictionaryGetValue(gifPropertiesCFD, delayTimeProperty), NSNumber.self); + + if gifProperties != nil { + let gifPropertiesCFD = unsafeBitCast(gifProperties, to: CFDictionary.self) + + let unclampedDelayTimeProperty = unsafeBitCast( + kCGImagePropertyGIFUnclampedDelayTime, + to: UnsafeRawPointer.self + ) + var number = unsafeBitCast( + CFDictionaryGetValue(gifPropertiesCFD, unclampedDelayTimeProperty), + to: NSNumber.self + ) + + if number.doubleValue == 0 { + let delayTimeProperty = unsafeBitCast(kCGImagePropertyGIFDelayTime, to: UnsafeRawPointer.self) + number = unsafeBitCast(CFDictionaryGetValue(gifPropertiesCFD, delayTimeProperty), to: NSNumber.self) } - - if (number.doubleValue > 0) { - delay = lrint(number.doubleValue * 100); + + if number.doubleValue > 0 { + delay = lrint(number.doubleValue * 100) } } } - - return delay; + + return delay } - - private class func frameArray(images: Array, _ delays: Array, _ totalDuration: Int) -> Array { - let delayGCD = gcd(delays) - let frameCount = totalDuration / delayGCD - var frames = Array() + + private class func frameArray(images: [CGImage], delays: [Int], totalDuration: Int) -> [UIImage] { + let delayGCD = gcd(values: delays) + var frames = [UIImage]() frames.reserveCapacity(images.count) - - for i in 0 ..< images.count { - let frame = UIImage(CGImage: images[i], scale: UIScreen.mainScreen().scale, orientation: .Up) - for j in 0 ..< delays[i]/delayGCD { - frames.append(frame!) + + for i in 0..) -> Int { - if values.count == 0 { - return 1; - } - - var currentGCD = values[0] - - for i in 0 ..< values.count { - currentGCD = gcd(values[i], currentGCD) - } - - return currentGCD; + + private class func gcd(values: [Int]) -> Int { + return values.reduce(1, gcd) } - - private class func gcd(var a: Int, var _ b: Int) -> Int { - while (true) { - var r = a % b - if (r == 0) { + + private class func gcd(_ num1: Int, _ num2: Int) -> Int { + var a = num1 + var b = num2 + + while true { + let r = a % b + if r == 0 { return b } - a = b; - b = r; + a = b + b = r } } } diff --git a/PPSwiftGifsExample/PPSwiftGifsExample.xcodeproj/project.pbxproj b/PPSwiftGifsExample/PPSwiftGifsExample.xcodeproj/project.pbxproj index f1cc5b2..523bf3b 100644 --- a/PPSwiftGifsExample/PPSwiftGifsExample.xcodeproj/project.pbxproj +++ b/PPSwiftGifsExample/PPSwiftGifsExample.xcodeproj/project.pbxproj @@ -326,6 +326,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -482,11 +483,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -518,9 +520,10 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -602,11 +605,13 @@ ); INFOPLIST_FILE = PPSwiftGifs/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 13.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -626,11 +631,13 @@ GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = PPSwiftGifs/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 13.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -711,6 +718,7 @@ 0DFFAEAB1AFF49F3009E1DFB /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; 0DFFAEAF1AFF49F3009E1DFB /* Build configuration list for PBXNativeTarget "PPSwiftGifsTests" */ = { isa = XCConfigurationList; @@ -719,6 +727,7 @@ 0DFFAEAD1AFF49F3009E1DFB /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/PPSwiftGifsExample/PPSwiftGifsExample/AppDelegate.swift b/PPSwiftGifsExample/PPSwiftGifsExample/AppDelegate.swift index 8b998e4..7cacf30 100644 --- a/PPSwiftGifsExample/PPSwiftGifsExample/AppDelegate.swift +++ b/PPSwiftGifsExample/PPSwiftGifsExample/AppDelegate.swift @@ -14,33 +14,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + private func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // Override point for customization after application launch. return true } - func applicationWillResignActive(application: UIApplication) { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. - } - - func applicationDidEnterBackground(application: UIApplication) { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. - } - - func applicationWillEnterForeground(application: UIApplication) { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. - } - - func applicationDidBecomeActive(application: UIApplication) { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - } - - func applicationWillTerminate(application: UIApplication) { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. - } - - } diff --git a/PPSwiftGifsExample/PPSwiftGifsExample/ViewController.swift b/PPSwiftGifsExample/PPSwiftGifsExample/ViewController.swift index 2b27633..c2b7a14 100644 --- a/PPSwiftGifsExample/PPSwiftGifsExample/ViewController.swift +++ b/PPSwiftGifsExample/PPSwiftGifsExample/ViewController.swift @@ -17,21 +17,14 @@ class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - imageView1.image = PPSwiftGifs.animatedImageWithGIFNamed("Loading") - imageView2.image = PPSwiftGifs.animatedImageWithGIFNamed("VariableDuration") + imageView1.image = PPSwiftGifs.animatedImageWithGIF(named: "Loading") + imageView2.image = PPSwiftGifs.animatedImageWithGIF(named: "VariableDuration") - if let url = NSBundle.mainBundle().URLForResource("Loading", withExtension: "gif") { - if let data = NSData(contentsOfURL: url) { - imageView3.image = PPSwiftGifs.animatedImageWithGIFData(data) + if let url = Bundle.main.url(forResource: "Loading", withExtension: "gif") { + if let data = try? Data(contentsOf: url) { + imageView3.image = PPSwiftGifs.animatedImageWithGIF(data: data) } } } - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. - } - - } - diff --git a/PPSwiftGifsExample/PPSwiftGifsExampleTests/PPSwiftGifsExampleTests.swift b/PPSwiftGifsExample/PPSwiftGifsExampleTests/PPSwiftGifsExampleTests.swift index 68ca627..f60862a 100644 --- a/PPSwiftGifsExample/PPSwiftGifsExampleTests/PPSwiftGifsExampleTests.swift +++ b/PPSwiftGifsExample/PPSwiftGifsExampleTests/PPSwiftGifsExampleTests.swift @@ -11,26 +11,4 @@ import XCTest class PPSwiftGifsExampleTests: XCTestCase { - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testExample() { - // This is an example of a functional test case. - XCTAssert(true, "Pass") - } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measureBlock() { - // Put the code you want to measure the time of here. - } - } - } diff --git a/PPSwiftGifsExample/PPSwiftGifsTests/PPSwiftGifsTests.swift b/PPSwiftGifsExample/PPSwiftGifsTests/PPSwiftGifsTests.swift index 801eea2..90d044f 100644 --- a/PPSwiftGifsExample/PPSwiftGifsTests/PPSwiftGifsTests.swift +++ b/PPSwiftGifsExample/PPSwiftGifsTests/PPSwiftGifsTests.swift @@ -10,27 +10,5 @@ import UIKit import XCTest class PPSwiftGifsTests: XCTestCase { - - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testExample() { - // This is an example of a functional test case. - XCTAssert(true, "Pass") - } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measureBlock() { - // Put the code you want to measure the time of here. - } - } - + }