diff --git a/Example/INSPhotoGallery.xcodeproj/project.pbxproj b/Example/INSPhotoGallery.xcodeproj/project.pbxproj index c83f074..2872153 100644 --- a/Example/INSPhotoGallery.xcodeproj/project.pbxproj +++ b/Example/INSPhotoGallery.xcodeproj/project.pbxproj @@ -20,7 +20,6 @@ 642427441CDE10970037E8DA /* INSPhotoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6424273A1CDE10970037E8DA /* INSPhotoViewController.swift */; }; 642427451CDE10970037E8DA /* INSScalingImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6424273B1CDE10970037E8DA /* INSScalingImageView.swift */; }; 642427461CDE10970037E8DA /* UIVIew+INSPhotoViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6424273C1CDE10970037E8DA /* UIVIew+INSPhotoViewer.swift */; }; - CC2C6F6CFCB580C59030916E /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFDCCEF4783A040A397D6717 /* Pods.framework */; }; D2EF35F51CB288F300033131 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EF35F41CB288F300033131 /* AppDelegate.swift */; }; D2EF35F71CB288F300033131 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EF35F61CB288F300033131 /* ViewController.swift */; }; D2EF35FA1CB288F300033131 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D2EF35F81CB288F300033131 /* Main.storyboard */; }; @@ -32,6 +31,7 @@ D2EF36201CB299CF00033131 /* CustomOverlayView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D2EF361F1CB299CF00033131 /* CustomOverlayView.xib */; }; D2EF36221CB29C6600033131 /* CustomPhotoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EF36211CB29C6600033131 /* CustomPhotoModel.swift */; }; D2EF36241CB2A67800033131 /* CustomModelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EF36231CB2A67800033131 /* CustomModelViewController.swift */; }; + FB44620B1AE393EEB9BC3649 /* Pods_INSPhotoGallery.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 043F1D6415A9A5BC7A90DF71 /* Pods_INSPhotoGallery.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -59,7 +59,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 52DA31F96E56812A6C09D2DA /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; + 043F1D6415A9A5BC7A90DF71 /* Pods_INSPhotoGallery.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_INSPhotoGallery.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 46019318530834A492BDDA22 /* Pods-INSPhotoGallery.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-INSPhotoGallery.debug.xcconfig"; path = "Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery.debug.xcconfig"; sourceTree = ""; }; 642427261CDE10740037E8DA /* INSPhotoGalleryFramework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = INSPhotoGalleryFramework.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 642427281CDE10750037E8DA /* INSPhotoGalleryFramework.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = INSPhotoGalleryFramework.h; sourceTree = ""; }; 6424272A1CDE10750037E8DA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -73,7 +74,7 @@ 6424273A1CDE10970037E8DA /* INSPhotoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = INSPhotoViewController.swift; path = ../../INSPhotoGallery/INSPhotoViewController.swift; sourceTree = ""; }; 6424273B1CDE10970037E8DA /* INSScalingImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = INSScalingImageView.swift; path = ../../INSPhotoGallery/INSScalingImageView.swift; sourceTree = ""; }; 6424273C1CDE10970037E8DA /* UIVIew+INSPhotoViewer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIVIew+INSPhotoViewer.swift"; path = "../../INSPhotoGallery/UIVIew+INSPhotoViewer.swift"; sourceTree = ""; }; - 7E0A6F9305E2B70C9951142C /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + A122ACF7A5F3C75B8FB59EED /* Pods-INSPhotoGallery.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-INSPhotoGallery.release.xcconfig"; path = "Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery.release.xcconfig"; sourceTree = ""; }; BFDCCEF4783A040A397D6717 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2EF35F11CB288F300033131 /* INSPhotoGallery.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = INSPhotoGallery.app; sourceTree = BUILT_PRODUCTS_DIR; }; D2EF35F41CB288F300033131 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -103,7 +104,7 @@ buildActionMask = 2147483647; files = ( 6424272D1CDE10750037E8DA /* INSPhotoGalleryFramework.framework in Frameworks */, - CC2C6F6CFCB580C59030916E /* Pods.framework in Frameworks */, + FB44620B1AE393EEB9BC3649 /* Pods_INSPhotoGallery.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -133,15 +134,16 @@ isa = PBXGroup; children = ( BFDCCEF4783A040A397D6717 /* Pods.framework */, + 043F1D6415A9A5BC7A90DF71 /* Pods_INSPhotoGallery.framework */, ); name = Frameworks; sourceTree = ""; }; - 7DC0C144B365E6A5E04F8728 /* Pods */ = { + B884F781A6C59E9DC8012C7C /* Pods */ = { isa = PBXGroup; children = ( - 52DA31F96E56812A6C09D2DA /* Pods.debug.xcconfig */, - 7E0A6F9305E2B70C9951142C /* Pods.release.xcconfig */, + 46019318530834A492BDDA22 /* Pods-INSPhotoGallery.debug.xcconfig */, + A122ACF7A5F3C75B8FB59EED /* Pods-INSPhotoGallery.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -152,8 +154,8 @@ D2EF35F31CB288F300033131 /* INSPhotoGallery */, 642427271CDE10750037E8DA /* INSPhotoGalleryFramework */, D2EF35F21CB288F300033131 /* Products */, - 7DC0C144B365E6A5E04F8728 /* Pods */, 6C22ABAEE0CC1A71AB7B6AF8 /* Frameworks */, + B884F781A6C59E9DC8012C7C /* Pods */, ); sourceTree = ""; }; @@ -229,13 +231,13 @@ isa = PBXNativeTarget; buildConfigurationList = D2EF36031CB288F300033131 /* Build configuration list for PBXNativeTarget "INSPhotoGallery" */; buildPhases = ( - FFD481CC20A7D03E3BFD0A9D /* Check Pods Manifest.lock */, + 430D737234B234F1BDA3BFFA /* [CP] Check Pods Manifest.lock */, D2EF35ED1CB288F300033131 /* Sources */, D2EF35EE1CB288F300033131 /* Frameworks */, D2EF35EF1CB288F300033131 /* Resources */, - 295459D9D817169DE522A196 /* Embed Pods Frameworks */, - C73ED651E9AE9BCD968CB264 /* Copy Pods Resources */, 642427321CDE10750037E8DA /* Embed Frameworks */, + AD54B7D6CFDA54F486B6350B /* [CP] Embed Pods Frameworks */, + 17D7FE4374E94EE2F6A42339 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -254,14 +256,16 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0730; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = "Inspace Labs Sp z o. o. Spółka Komandytowa"; TargetAttributes = { 642427251CDE10740037E8DA = { CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0800; }; D2EF35F01CB288F300033131 = { CreatedOnToolsVersion = 7.3; + LastSwiftMigration = 0800; }; }; }; @@ -307,49 +311,49 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 295459D9D817169DE522A196 /* Embed Pods Frameworks */ = { + 17D7FE4374E94EE2F6A42339 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-resources.sh\"\n"; showEnvVarsInLog = 0; }; - C73ED651E9AE9BCD968CB264 /* Copy Pods Resources */ = { + 430D737234B234F1BDA3BFFA /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; - FFD481CC20A7D03E3BFD0A9D /* Check Pods Manifest.lock */ = { + AD54B7D6CFDA54F486B6350B /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -419,6 +423,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -432,6 +437,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -442,6 +448,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -454,6 +461,7 @@ PRODUCT_BUNDLE_IDENTIFIER = io.inspace.INSPhotoGalleryFramework; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -474,8 +482,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -519,8 +529,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -539,37 +551,40 @@ IPHONEOS_DEPLOYMENT_TARGET = 9.3; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; }; name = Release; }; D2EF36041CB288F300033131 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 52DA31F96E56812A6C09D2DA /* Pods.debug.xcconfig */; + baseConfigurationReference = 46019318530834A492BDDA22 /* Pods-INSPhotoGallery.debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; INFOPLIST_FILE = INSPhotoGallery/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = io.inspace.INSPhotoGallery; PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)_Example"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Debug; }; D2EF36051CB288F300033131 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7E0A6F9305E2B70C9951142C /* Pods.release.xcconfig */; + baseConfigurationReference = A122ACF7A5F3C75B8FB59EED /* Pods-INSPhotoGallery.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; INFOPLIST_FILE = INSPhotoGallery/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = io.inspace.INSPhotoGallery; PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)_Example"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/Example/INSPhotoGallery/AppDelegate.swift b/Example/INSPhotoGallery/AppDelegate.swift index 20267ac..95896c4 100644 --- a/Example/INSPhotoGallery/AppDelegate.swift +++ b/Example/INSPhotoGallery/AppDelegate.swift @@ -14,30 +14,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + private func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: Any]?) -> Bool { // Override point for customization after application launch. return true } - func applicationWillResignActive(application: UIApplication) { + 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) { + 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) { + 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) { + 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) { + func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } diff --git a/Example/INSPhotoGallery/CustomModelViewController.swift b/Example/INSPhotoGallery/CustomModelViewController.swift index 91f0198..de3c63d 100644 --- a/Example/INSPhotoGallery/CustomModelViewController.swift +++ b/Example/INSPhotoGallery/CustomModelViewController.swift @@ -14,12 +14,12 @@ class CustomModelViewController: UIViewController { lazy var photos: [CustomPhotoModel] = { return [ - CustomPhotoModel(imageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/13-3f15416ddd11d38619289335fafd498d.jpg"), thumbnailImage: UIImage(named: "thumbnailImage")!), - CustomPhotoModel(imageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/13-3f15416ddd11d38619289335fafd498d.jpg"), thumbnailImage: UIImage(named: "thumbnailImage")!), + CustomPhotoModel(imageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/13-3f15416ddd11d38619289335fafd498d.jpg"), thumbnailImage: UIImage(named: "thumbnailImage")!), + CustomPhotoModel(imageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/13-3f15416ddd11d38619289335fafd498d.jpg"), thumbnailImage: UIImage(named: "thumbnailImage")!), CustomPhotoModel(image: UIImage(named: "fullSizeImage")!, thumbnailImage: UIImage(named: "thumbnailImage")!), - CustomPhotoModel(imageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg"), thumbnailImageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg")), - CustomPhotoModel(imageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg"), thumbnailImageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg")), - CustomPhotoModel(imageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg"), thumbnailImageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg")) + CustomPhotoModel(imageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg"), thumbnailImageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg") ), + CustomPhotoModel(imageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg"), thumbnailImageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg") ), + CustomPhotoModel(imageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg"), thumbnailImageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg")) ] }() @@ -32,29 +32,29 @@ class CustomModelViewController: UIViewController { } extension CustomModelViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { - func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { - let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ExampleCollectionViewCell", forIndexPath: indexPath) as! ExampleCollectionViewCell - cell.populateWithPhoto(photos[indexPath.row]) + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ExampleCollectionViewCell", for: indexPath) as! ExampleCollectionViewCell + cell.populateWithPhoto(photos[(indexPath as NSIndexPath).row]) return cell } - func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return photos.count } - func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { - let cell = collectionView.cellForItemAtIndexPath(indexPath) as! ExampleCollectionViewCell - let currentPhoto = photos[indexPath.row] + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let cell = collectionView.cellForItem(at: indexPath) as! ExampleCollectionViewCell + let currentPhoto = photos[(indexPath as NSIndexPath).row] let galleryPreview = INSPhotosViewController(photos: photos, initialPhoto: currentPhoto, referenceView: cell) galleryPreview.referenceViewForPhotoWhenDismissingHandler = { [weak self] photo in - if let index = self?.photos.indexOf({$0 === photo}) { - let indexPath = NSIndexPath(forItem: index, inSection: 0) - let cell = collectionView.cellForItemAtIndexPath(indexPath) as? ExampleCollectionViewCell + if let index = self?.photos.index(where: {$0 === photo}) { + let indexPath = IndexPath(item: index, section: 0) + let cell = collectionView.cellForItem(at: indexPath) as? ExampleCollectionViewCell return cell } return nil } - presentViewController(galleryPreview, animated: true, completion: nil) + present(galleryPreview, animated: true, completion: nil) } } diff --git a/Example/INSPhotoGallery/CustomOverlayView.swift b/Example/INSPhotoGallery/CustomOverlayView.swift index a2afda3..8065a20 100644 --- a/Example/INSPhotoGallery/CustomOverlayView.swift +++ b/Example/INSPhotoGallery/CustomOverlayView.swift @@ -14,40 +14,40 @@ class CustomOverlayView: INSNibLoadedView { weak var photosViewController: INSPhotosViewController? // Pass the touches down to other views - override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { - if let hitView = super.hitTest(point, withEvent: event) where hitView != self { + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let hitView = super.hitTest(point, with: event) , hitView != self { return hitView } return nil } - @IBAction func closeButtonTapped(sender: AnyObject) { - photosViewController?.dismissViewControllerAnimated(true, completion: nil) + @IBAction func closeButtonTapped(_ sender: AnyObject) { + photosViewController?.dismiss(animated: true, completion: nil) } } extension CustomOverlayView: INSPhotosOverlayViewable { - func populateWithPhoto(photo: INSPhotoViewable) { + func populateWithPhoto(_ photo: INSPhotoViewable) { } - func setHidden(hidden: Bool, animated: Bool) { - if self.hidden == hidden { + func setHidden(_ hidden: Bool, animated: Bool) { + if self.isHidden == hidden { return } if animated { - self.hidden = false + self.isHidden = false self.alpha = hidden ? 1.0 : 0.0 - UIView.animateWithDuration(0.2, delay: 0.0, options: [.CurveEaseInOut, .AllowAnimatedContent, .AllowUserInteraction], animations: { () -> Void in + UIView.animate(withDuration: 0.2, delay: 0.0, options: [.allowAnimatedContent, .allowUserInteraction], animations: { () -> Void in self.alpha = hidden ? 0.0 : 1.0 }, completion: { result in self.alpha = 1.0 - self.hidden = hidden + self.isHidden = hidden }) } else { - self.hidden = hidden + self.isHidden = hidden } } } diff --git a/Example/INSPhotoGallery/CustomPhotoModel.swift b/Example/INSPhotoGallery/CustomPhotoModel.swift index b2dfdfe..911e603 100644 --- a/Example/INSPhotoGallery/CustomPhotoModel.swift +++ b/Example/INSPhotoGallery/CustomPhotoModel.swift @@ -7,18 +7,18 @@ // import UIKit -import Haneke +import Kingfisher import INSPhotoGalleryFramework class CustomPhotoModel: NSObject, INSPhotoViewable { var image: UIImage? var thumbnailImage: UIImage? - var imageURL: NSURL? - var thumbnailImageURL: NSURL? + var imageURL: URL? + var thumbnailImageURL: URL? var attributedTitle: NSAttributedString? { - return NSAttributedString(string: "Example caption text", attributes: [NSForegroundColorAttributeName: UIColor.whiteColor()]) + return NSAttributedString(string: "Example caption text", attributes: [NSForegroundColorAttributeName: UIColor.white]) } init(image: UIImage?, thumbnailImage: UIImage?) { @@ -26,40 +26,37 @@ class CustomPhotoModel: NSObject, INSPhotoViewable { self.thumbnailImage = thumbnailImage } - init(imageURL: NSURL?, thumbnailImageURL: NSURL?) { + init(imageURL: URL?, thumbnailImageURL: URL?) { self.imageURL = imageURL self.thumbnailImageURL = thumbnailImageURL } - init (imageURL: NSURL?, thumbnailImage: UIImage) { + init (imageURL: URL?, thumbnailImage: UIImage) { self.imageURL = imageURL self.thumbnailImage = thumbnailImage } - func loadImageWithCompletionHandler(completion: (image: UIImage?, error: NSError?) -> ()) { + func loadImageWithCompletionHandler(_ completion: @escaping (_ image: UIImage?, _ error: Error?) -> ()) { if let url = imageURL { - Shared.imageCache.fetch(URL: url).onSuccess({ image in - completion(image: image, error: nil) - }).onFailure({ error in - completion(image: nil, error: error) + + KingfisherManager.shared.retrieveImage(with: ImageResource(downloadURL: url), options: nil, progressBlock: nil, completionHandler: { (image, error, cacheType, url) in + completion(image, error) }) } else { - completion(image: nil, error: NSError(domain: "PhotoDomain", code: -1, userInfo: [ NSLocalizedDescriptionKey: "Couldn't load image"])) + completion(nil, NSError(domain: "PhotoDomain", code: -1, userInfo: [ NSLocalizedDescriptionKey: "Couldn't load image"])) } } - func loadThumbnailImageWithCompletionHandler(completion: (image: UIImage?, error: NSError?) -> ()) { + func loadThumbnailImageWithCompletionHandler(_ completion: @escaping (_ image: UIImage?, _ error: Error?) -> ()) { if let thumbnailImage = thumbnailImage { - completion(image: thumbnailImage, error: nil) + completion(thumbnailImage, nil) return } if let url = thumbnailImageURL { - Shared.imageCache.fetch(URL: url).onSuccess({ image in - completion(image: image, error: nil) - }).onFailure({ error in - completion(image: nil, error: error) + KingfisherManager.shared.retrieveImage(with: ImageResource(downloadURL: url), options: nil, progressBlock: nil, completionHandler: { (image, error, cacheType, url) in + completion(image, error) }) } else { - completion(image: nil, error: NSError(domain: "PhotoDomain", code: -1, userInfo: [ NSLocalizedDescriptionKey: "Couldn't load image"])) + completion(nil, NSError(domain: "PhotoDomain", code: -1, userInfo: [ NSLocalizedDescriptionKey: "Couldn't load image"])) } } -} \ No newline at end of file +} diff --git a/Example/INSPhotoGallery/ExampleCollectionViewCell.swift b/Example/INSPhotoGallery/ExampleCollectionViewCell.swift index 4b2e48b..9e59ae6 100644 --- a/Example/INSPhotoGallery/ExampleCollectionViewCell.swift +++ b/Example/INSPhotoGallery/ExampleCollectionViewCell.swift @@ -12,7 +12,7 @@ import INSPhotoGalleryFramework class ExampleCollectionViewCell: UICollectionViewCell { @IBOutlet weak var imageView: UIImageView! - func populateWithPhoto(photo: INSPhotoViewable) { + func populateWithPhoto(_ photo: INSPhotoViewable) { photo.loadThumbnailImageWithCompletionHandler { [weak photo] (image, error) in if let image = image { if let photo = photo as? INSPhoto { diff --git a/Example/INSPhotoGallery/TableViewController.swift b/Example/INSPhotoGallery/TableViewController.swift index 4e87a65..531577d 100644 --- a/Example/INSPhotoGallery/TableViewController.swift +++ b/Example/INSPhotoGallery/TableViewController.swift @@ -12,9 +12,9 @@ class TableViewController: UITableViewController { // MARK: - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { - if let identifier = segue.identifier where identifier == "customOverlay" { - if let destinationVC = segue.destinationViewController as? ViewController { + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if let identifier = segue.identifier , identifier == "customOverlay" { + if let destinationVC = segue.destination as? ViewController { destinationVC.useCustomOverlay = true } } diff --git a/Example/INSPhotoGallery/ViewController.swift b/Example/INSPhotoGallery/ViewController.swift index 72dc616..97754b7 100644 --- a/Example/INSPhotoGallery/ViewController.swift +++ b/Example/INSPhotoGallery/ViewController.swift @@ -16,12 +16,12 @@ class ViewController: UIViewController { lazy var photos: [INSPhotoViewable] = { return [ - INSPhoto(imageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/13-3f15416ddd11d38619289335fafd498d.jpg"), thumbnailImage: UIImage(named: "thumbnailImage")!), - INSPhoto(imageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/13-3f15416ddd11d38619289335fafd498d.jpg"), thumbnailImage: UIImage(named: "thumbnailImage")!), + INSPhoto(imageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/13-3f15416ddd11d38619289335fafd498d.jpg"), thumbnailImage: UIImage(named: "thumbnailImage")!), + INSPhoto(imageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/13-3f15416ddd11d38619289335fafd498d.jpg"), thumbnailImage: UIImage(named: "thumbnailImage")!), INSPhoto(image: UIImage(named: "fullSizeImage")!, thumbnailImage: UIImage(named: "thumbnailImage")!), - INSPhoto(imageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg"), thumbnailImageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg")), - INSPhoto(imageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg"), thumbnailImageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg")), - INSPhoto(imageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg"), thumbnailImageURL: NSURL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg")) + INSPhoto(imageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg"), thumbnailImageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg")), + INSPhoto(imageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg"), thumbnailImageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg")), + INSPhoto(imageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg"), thumbnailImageURL: URL(string: "http://inspace.io/assets/portfolio/thumb/6-d793b947f57cc3df688eeb1d36b04ddb.jpg")) ] }() @@ -33,38 +33,38 @@ class ViewController: UIViewController { for photo in photos { if let photo = photo as? INSPhoto { - photo.attributedTitle = NSAttributedString(string: "Example caption text\ncaption text", attributes: [NSForegroundColorAttributeName: UIColor.whiteColor()]) + photo.attributedTitle = NSAttributedString(string: "Example caption text\ncaption text", attributes: [NSForegroundColorAttributeName: UIColor.white]) } } } } extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { - func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { - let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ExampleCollectionViewCell", forIndexPath: indexPath) as! ExampleCollectionViewCell - cell.populateWithPhoto(photos[indexPath.row]) + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ExampleCollectionViewCell", for: indexPath) as! ExampleCollectionViewCell + cell.populateWithPhoto(photos[(indexPath as NSIndexPath).row]) return cell } - func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return photos.count } - func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { - let cell = collectionView.cellForItemAtIndexPath(indexPath) as! ExampleCollectionViewCell - let currentPhoto = photos[indexPath.row] + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let cell = collectionView.cellForItem(at: indexPath) as! ExampleCollectionViewCell + let currentPhoto = photos[(indexPath as NSIndexPath).row] let galleryPreview = INSPhotosViewController(photos: photos, initialPhoto: currentPhoto, referenceView: cell) if useCustomOverlay { galleryPreview.overlayView = CustomOverlayView(frame: CGRect.zero) } galleryPreview.referenceViewForPhotoWhenDismissingHandler = { [weak self] photo in - if let index = self?.photos.indexOf({$0 === photo}) { - let indexPath = NSIndexPath(forItem: index, inSection: 0) - return collectionView.cellForItemAtIndexPath(indexPath) as? ExampleCollectionViewCell + if let index = self?.photos.index(where: {$0 === photo}) { + let indexPath = IndexPath(item: index, section: 0) + return collectionView.cellForItem(at: indexPath) as? ExampleCollectionViewCell } return nil } - presentViewController(galleryPreview, animated: true, completion: nil) + present(galleryPreview, animated: true, completion: nil) } -} \ No newline at end of file +} diff --git a/Example/Podfile b/Example/Podfile index 3d754e3..fda60af 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -1,5 +1,16 @@ platform :ios, "8.0" -pod 'INSNibLoading' -pod 'HanekeSwift' -use_frameworks! \ No newline at end of file +target "INSPhotoGallery" do + pod 'INSNibLoading' + pod 'Kingfisher', :git => 'https://github.com/onevcat/Kingfisher.git', :branch => "swift3" +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |configuration| + configuration.build_settings['SWIFT_VERSION'] = "3.0" + end + end +end + +use_frameworks! diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 886e5bf..edf22d2 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,13 +1,25 @@ PODS: - - HanekeSwift (0.10.1) - INSNibLoading (1.0.1) + - Kingfisher (2.5.0) DEPENDENCIES: - - HanekeSwift - INSNibLoading + - Kingfisher (from `https://github.com/onevcat/Kingfisher.git`, branch `swift3`) + +EXTERNAL SOURCES: + Kingfisher: + :branch: swift3 + :git: https://github.com/onevcat/Kingfisher.git + +CHECKOUT OPTIONS: + Kingfisher: + :commit: 609445170d3736b12d01024a953ceb849bc5d071 + :git: https://github.com/onevcat/Kingfisher.git SPEC CHECKSUMS: - HanekeSwift: 08e47d3d0f2899ec58e5d257beba0de619aca9e8 INSNibLoading: 1b8d0ee31b94c124bffe2609e697e0a43117a78f + Kingfisher: d467bd710907ec9aa94aaddecaa5390aa3559819 + +PODFILE CHECKSUM: 6adeabf6c63ce164ecc944fc6b75c1dceb253597 -COCOAPODS: 0.39.0 +COCOAPODS: 1.0.1 diff --git a/Example/Pods/HanekeSwift/Haneke/CGSize+Swift.swift b/Example/Pods/HanekeSwift/Haneke/CGSize+Swift.swift deleted file mode 100644 index 618d14f..0000000 --- a/Example/Pods/HanekeSwift/Haneke/CGSize+Swift.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// CGSize+Swift.swift -// Haneke -// -// Created by Oriol Blanc Gimeno on 09/09/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import UIKit - -extension CGSize { - - func hnk_aspectFillSize(size: CGSize) -> CGSize { - let scaleWidth = size.width / self.width - let scaleHeight = size.height / self.height - let scale = max(scaleWidth, scaleHeight) - - let resultSize = CGSizeMake(self.width * scale, self.height * scale) - return CGSizeMake(ceil(resultSize.width), ceil(resultSize.height)) - } - - func hnk_aspectFitSize(size: CGSize) -> CGSize { - let targetAspect = size.width / size.height - let sourceAspect = self.width / self.height - var resultSize = size - - if (targetAspect > sourceAspect) { - resultSize.width = size.height * sourceAspect - } - else { - resultSize.height = size.width / sourceAspect - } - return CGSizeMake(ceil(resultSize.width), ceil(resultSize.height)) - } -} \ No newline at end of file diff --git a/Example/Pods/HanekeSwift/Haneke/Cache.swift b/Example/Pods/HanekeSwift/Haneke/Cache.swift deleted file mode 100644 index 69e4443..0000000 --- a/Example/Pods/HanekeSwift/Haneke/Cache.swift +++ /dev/null @@ -1,280 +0,0 @@ -// -// Cache.swift -// Haneke -// -// Created by Luis Ascorbe on 23/07/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import UIKit - -// Used to add T to NSCache -class ObjectWrapper : NSObject { - let value: Any - - init(value: Any) { - self.value = value - } -} - -extension HanekeGlobals { - - // It'd be better to define this in the Cache class but Swift doesn't allow statics in a generic type - public struct Cache { - - public static let OriginalFormatName = "original" - - public enum ErrorCode : Int { - case ObjectNotFound = -100 - case FormatNotFound = -101 - } - - } - -} - -public class Cache { - - let name: String - - var memoryWarningObserver : NSObjectProtocol! - - public init(name: String) { - self.name = name - - let notifications = NSNotificationCenter.defaultCenter() - // Using block-based observer to avoid subclassing NSObject - memoryWarningObserver = notifications.addObserverForName(UIApplicationDidReceiveMemoryWarningNotification, - object: nil, - queue: NSOperationQueue.mainQueue(), - usingBlock: { [unowned self] (notification : NSNotification!) -> Void in - self.onMemoryWarning() - } - ) - - let originalFormat = Format(name: HanekeGlobals.Cache.OriginalFormatName) - self.addFormat(originalFormat) - } - - deinit { - let notifications = NSNotificationCenter.defaultCenter() - notifications.removeObserver(memoryWarningObserver, name: UIApplicationDidReceiveMemoryWarningNotification, object: nil) - } - - public func set(value value: T, key: String, formatName: String = HanekeGlobals.Cache.OriginalFormatName, success succeed: ((T) -> ())? = nil) { - if let (format, memoryCache, diskCache) = self.formats[formatName] { - self.format(value: value, format: format) { formattedValue in - let wrapper = ObjectWrapper(value: formattedValue) - memoryCache.setObject(wrapper, forKey: key) - // Value data is sent as @autoclosure to be executed in the disk cache queue. - diskCache.setData(self.dataFromValue(formattedValue, format: format), key: key) - succeed?(formattedValue) - } - } else { - assertionFailure("Can't set value before adding format") - } - } - - public func fetch(key key: String, formatName: String = HanekeGlobals.Cache.OriginalFormatName, failure fail : Fetch.Failer? = nil, success succeed : Fetch.Succeeder? = nil) -> Fetch { - let fetch = Cache.buildFetch(failure: fail, success: succeed) - if let (format, memoryCache, diskCache) = self.formats[formatName] { - if let wrapper = memoryCache.objectForKey(key) as? ObjectWrapper, let result = wrapper.value as? T { - fetch.succeed(result) - diskCache.updateAccessDate(self.dataFromValue(result, format: format), key: key) - return fetch - } - - self.fetchFromDiskCache(diskCache, key: key, memoryCache: memoryCache, failure: { error in - fetch.fail(error) - }) { value in - fetch.succeed(value) - } - - } else { - let localizedFormat = NSLocalizedString("Format %@ not found", comment: "Error description") - let description = String(format:localizedFormat, formatName) - let error = errorWithCode(HanekeGlobals.Cache.ErrorCode.FormatNotFound.rawValue, description: description) - fetch.fail(error) - } - return fetch - } - - public func fetch(fetcher fetcher : Fetcher, formatName: String = HanekeGlobals.Cache.OriginalFormatName, failure fail : Fetch.Failer? = nil, success succeed : Fetch.Succeeder? = nil) -> Fetch { - let key = fetcher.key - let fetch = Cache.buildFetch(failure: fail, success: succeed) - self.fetch(key: key, formatName: formatName, failure: { error in - if error?.code == HanekeGlobals.Cache.ErrorCode.FormatNotFound.rawValue { - fetch.fail(error) - } - - if let (format, _, _) = self.formats[formatName] { - self.fetchAndSet(fetcher, format: format, failure: {error in - fetch.fail(error) - }) {value in - fetch.succeed(value) - } - } - - // Unreachable code. Formats can't be removed from Cache. - }) { value in - fetch.succeed(value) - } - return fetch - } - - public func remove(key key: String, formatName: String = HanekeGlobals.Cache.OriginalFormatName) { - if let (_, memoryCache, diskCache) = self.formats[formatName] { - memoryCache.removeObjectForKey(key) - diskCache.removeData(key) - } - } - - public func removeAll() { - for (_, (_, memoryCache, diskCache)) in self.formats { - memoryCache.removeAllObjects() - diskCache.removeAllData() - } - } - - // MARK: Notifications - - func onMemoryWarning() { - for (_, (_, memoryCache, _)) in self.formats { - memoryCache.removeAllObjects() - } - } - - // MARK: Formats - - var formats : [String : (Format, NSCache, DiskCache)] = [:] - - public func addFormat(format : Format) { - let name = format.name - let formatPath = self.formatPath(formatName: name) - let memoryCache = NSCache() - let diskCache = DiskCache(path: formatPath, capacity : format.diskCapacity) - self.formats[name] = (format, memoryCache, diskCache) - } - - // MARK: Internal - - lazy var cachePath: String = { - let basePath = DiskCache.basePath() - let cachePath = (basePath as NSString).stringByAppendingPathComponent(self.name) - return cachePath - }() - - func formatPath(formatName formatName: String) -> String { - let formatPath = (self.cachePath as NSString).stringByAppendingPathComponent(formatName) - do { - try NSFileManager.defaultManager().createDirectoryAtPath(formatPath, withIntermediateDirectories: true, attributes: nil) - } catch { - Log.error("Failed to create directory \(formatPath)", error as NSError) - } - return formatPath - } - - // MARK: Private - - func dataFromValue(value : T, format : Format) -> NSData? { - if let data = format.convertToData?(value) { - return data - } - return value.asData() - } - - private func fetchFromDiskCache(diskCache : DiskCache, key: String, memoryCache : NSCache, failure fail : ((NSError?) -> ())?, success succeed : (T) -> ()) { - diskCache.fetchData(key: key, failure: { error in - if let block = fail { - if (error?.code == NSFileReadNoSuchFileError) { - let localizedFormat = NSLocalizedString("Object not found for key %@", comment: "Error description") - let description = String(format:localizedFormat, key) - let error = errorWithCode(HanekeGlobals.Cache.ErrorCode.ObjectNotFound.rawValue, description: description) - block(error) - } else { - block(error) - } - } - }) { data in - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { - let value = T.convertFromData(data) - if let value = value { - let descompressedValue = self.decompressedImageIfNeeded(value) - dispatch_async(dispatch_get_main_queue(), { - succeed(descompressedValue) - let wrapper = ObjectWrapper(value: descompressedValue) - memoryCache.setObject(wrapper, forKey: key) - }) - } - }) - } - } - - private func fetchAndSet(fetcher : Fetcher, format : Format, failure fail : ((NSError?) -> ())?, success succeed : (T) -> ()) { - fetcher.fetch(failure: { error in - let _ = fail?(error) - }) { value in - self.set(value: value, key: fetcher.key, formatName: format.name, success: succeed) - } - } - - private func format(value value : T, format : Format, success succeed : (T) -> ()) { - // HACK: Ideally Cache shouldn't treat images differently but I can't think of any other way of doing this that doesn't complicate the API for other types. - if format.isIdentity && !(value is UIImage) { - succeed(value) - } else { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { - var formatted = format.apply(value) - - if let formattedImage = formatted as? UIImage { - let originalImage = value as? UIImage - if formattedImage === originalImage { - formatted = self.decompressedImageIfNeeded(formatted) - } - } - - dispatch_async(dispatch_get_main_queue()) { - succeed(formatted) - } - } - } - } - - private func decompressedImageIfNeeded(value : T) -> T { - if let image = value as? UIImage { - let decompressedImage = image.hnk_decompressedImage() as? T - return decompressedImage! - } - return value - } - - private class func buildFetch(failure fail : Fetch.Failer? = nil, success succeed : Fetch.Succeeder? = nil) -> Fetch { - let fetch = Fetch() - if let succeed = succeed { - fetch.onSuccess(succeed) - } - if let fail = fail { - fetch.onFailure(fail) - } - return fetch - } - - // MARK: Convenience fetch - // Ideally we would put each of these in the respective fetcher file as a Cache extension. Unfortunately, this fails to link when using the framework in a project as of Xcode 6.1. - - public func fetch(key key: String, @autoclosure(escaping) value getValue : () -> T.Result, formatName: String = HanekeGlobals.Cache.OriginalFormatName, success succeed : Fetch.Succeeder? = nil) -> Fetch { - let fetcher = SimpleFetcher(key: key, value: getValue) - return self.fetch(fetcher: fetcher, formatName: formatName, success: succeed) - } - - public func fetch(path path: String, formatName: String = HanekeGlobals.Cache.OriginalFormatName, failure fail : Fetch.Failer? = nil, success succeed : Fetch.Succeeder? = nil) -> Fetch { - let fetcher = DiskFetcher(path: path) - return self.fetch(fetcher: fetcher, formatName: formatName, failure: fail, success: succeed) - } - - public func fetch(URL URL : NSURL, formatName: String = HanekeGlobals.Cache.OriginalFormatName, failure fail : Fetch.Failer? = nil, success succeed : Fetch.Succeeder? = nil) -> Fetch { - let fetcher = NetworkFetcher(URL: URL) - return self.fetch(fetcher: fetcher, formatName: formatName, failure: fail, success: succeed) - } - -} diff --git a/Example/Pods/HanekeSwift/Haneke/CryptoSwiftMD5.swift b/Example/Pods/HanekeSwift/Haneke/CryptoSwiftMD5.swift deleted file mode 100755 index 4eb85b3..0000000 --- a/Example/Pods/HanekeSwift/Haneke/CryptoSwiftMD5.swift +++ /dev/null @@ -1,199 +0,0 @@ -// -// CryptoSwiftMD5.Swif -// -// To date, adding CommonCrypto to a Swift framework is problematic. See: -// http://stackoverflow.com/questions/25248598/importing-commoncrypto-in-a-swift-framework -// We're using a subset of CryptoSwift as a (temporary?) alternative. -// The following is an altered source version that only includes MD5. The original software can be found at: -// https://github.com/krzyzanowskim/CryptoSwift -// This is the original copyright notice: - -/* -Copyright (C) 2014 Marcin Krzyżanowski -This software is provided 'as-is', without any express or implied warranty. - -In no event will the authors be held liable for any damages arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: - -- The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. -- Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -- This notice may not be removed or altered from any source or binary distribution. -*/ - -import Foundation - -/** array of bytes, little-endian representation */ -func arrayOfBytes(value:T, length:Int? = nil) -> [UInt8] { - let totalBytes = length ?? (sizeofValue(value) * 8) - - let valuePointer = UnsafeMutablePointer.alloc(1) - valuePointer.memory = value - - let bytesPointer = UnsafeMutablePointer(valuePointer) - var bytes = [UInt8](count: totalBytes, repeatedValue: 0) - for j in 0.. [UInt8] { - return arrayOfBytes(self, length: totalBytes) - } - -} - -extension NSMutableData { - - /** Convenient way to append bytes */ - internal func appendBytes(arrayOfBytes: [UInt8]) { - self.appendBytes(arrayOfBytes, length: arrayOfBytes.count) - } - -} - -class HashBase { - - var message: NSData - - init(_ message: NSData) { - self.message = message - } - - /** Common part for hash calculation. Prepare header data. */ - func prepare(len: Int = 64) -> NSMutableData { - let tmpMessage: NSMutableData = NSMutableData(data: self.message) - - // Step 1. Append Padding Bits - tmpMessage.appendBytes([0x80]) // append one bit (UInt8 with one bit) to message - - // append "0" bit until message length in bits ≡ 448 (mod 512) - var msgLength = tmpMessage.length - var counter = 0 - while msgLength % len != (len - 8) { - counter++ - msgLength++ - } - let bufZeros = UnsafeMutablePointer(calloc(counter, sizeof(UInt8))) - tmpMessage.appendBytes(bufZeros, length: counter) - - return tmpMessage - } -} - -func rotateLeft(v: UInt32, n: UInt32) -> UInt32 { - return ((v << n) & 0xFFFFFFFF) | (v >> (32 - n)) -} - -class MD5 : HashBase { - - /** specifies the per-round shift amounts */ - private let s: [UInt32] = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, - 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, - 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, - 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21] - - /** binary integer part of the sines of integers (Radians) */ - private let k: [UInt32] = [0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee, - 0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501, - 0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be, - 0x6b901122,0xfd987193,0xa679438e,0x49b40821, - 0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa, - 0xd62f105d,0x2441453,0xd8a1e681,0xe7d3fbc8, - 0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed, - 0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a, - 0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c, - 0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70, - 0x289b7ec6,0xeaa127fa,0xd4ef3085,0x4881d05, - 0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665, - 0xf4292244,0x432aff97,0xab9423a7,0xfc93a039, - 0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1, - 0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1, - 0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391] - - private let h:[UInt32] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476] - - func calculate() -> NSData { - let tmpMessage = prepare() - - // hash values - var hh = h - - // Step 2. Append Length a 64-bit representation of lengthInBits - let lengthInBits = (message.length * 8) - let lengthBytes = lengthInBits.bytes(64 / 8) - tmpMessage.appendBytes(lengthBytes.reverse()) - - // Process the message in successive 512-bit chunks: - let chunkSizeBytes = 512 / 8 // 64 - var leftMessageBytes = tmpMessage.length - for (var i = 0; i < tmpMessage.length; i = i + chunkSizeBytes, leftMessageBytes -= chunkSizeBytes) { - let chunk = tmpMessage.subdataWithRange(NSRange(location: i, length: min(chunkSizeBytes,leftMessageBytes))) - - // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15 - var M:[UInt32] = [UInt32](count: 16, repeatedValue: 0) - let range = NSRange(location:0, length: M.count * sizeof(UInt32)) - chunk.getBytes(UnsafeMutablePointer(M), range: range) - - // Initialize hash value for this chunk: - var A:UInt32 = hh[0] - var B:UInt32 = hh[1] - var C:UInt32 = hh[2] - var D:UInt32 = hh[3] - - var dTemp:UInt32 = 0 - - // Main loop - for j in 0.. () in - var i:UInt32 = item.littleEndian - buf.appendBytes(&i, length: sizeofValue(i)) - }) - - return buf.copy() as! NSData - } -} \ No newline at end of file diff --git a/Example/Pods/HanekeSwift/Haneke/Data.swift b/Example/Pods/HanekeSwift/Haneke/Data.swift deleted file mode 100644 index 55ba6ba..0000000 --- a/Example/Pods/HanekeSwift/Haneke/Data.swift +++ /dev/null @@ -1,127 +0,0 @@ -// -// Data.swift -// Haneke -// -// Created by Hermes Pique on 9/19/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import UIKit - -// See: http://stackoverflow.com/questions/25922152/not-identical-to-self -public protocol DataConvertible { - typealias Result - - static func convertFromData(data:NSData) -> Result? -} - -public protocol DataRepresentable { - - func asData() -> NSData! -} - -private let imageSync = NSLock() - -extension UIImage : DataConvertible, DataRepresentable { - - public typealias Result = UIImage - - // HACK: UIImage data initializer is no longer thread safe. See: https://github.com/AFNetworking/AFNetworking/issues/2572#issuecomment-115854482 - static func safeImageWithData(data:NSData) -> Result? { - imageSync.lock() - let image = UIImage(data:data) - imageSync.unlock() - return image - } - - public class func convertFromData(data: NSData) -> Result? { - let image = UIImage.safeImageWithData(data) - return image - } - - public func asData() -> NSData! { - return self.hnk_data() - } - -} - -extension String : DataConvertible, DataRepresentable { - - public typealias Result = String - - public static func convertFromData(data: NSData) -> Result? { - let string = NSString(data: data, encoding: NSUTF8StringEncoding) - return string as? Result - } - - public func asData() -> NSData! { - return self.dataUsingEncoding(NSUTF8StringEncoding) - } - -} - -extension NSData : DataConvertible, DataRepresentable { - - public typealias Result = NSData - - public class func convertFromData(data: NSData) -> Result? { - return data - } - - public func asData() -> NSData! { - return self - } - -} - -public enum JSON : DataConvertible, DataRepresentable { - public typealias Result = JSON - - case Dictionary([String:AnyObject]) - case Array([AnyObject]) - - public static func convertFromData(data: NSData) -> Result? { - do { - let object : AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) - switch (object) { - case let dictionary as [String:AnyObject]: - return JSON.Dictionary(dictionary) - case let array as [AnyObject]: - return JSON.Array(array) - default: - return nil - } - } catch { - Log.error("Invalid JSON data", error as NSError) - return nil - } - } - - public func asData() -> NSData! { - switch (self) { - case .Dictionary(let dictionary): - return try? NSJSONSerialization.dataWithJSONObject(dictionary, options: NSJSONWritingOptions()) - case .Array(let array): - return try? NSJSONSerialization.dataWithJSONObject(array, options: NSJSONWritingOptions()) - } - } - - public var array : [AnyObject]! { - switch (self) { - case .Dictionary(_): - return nil - case .Array(let array): - return array - } - } - - public var dictionary : [String:AnyObject]! { - switch (self) { - case .Dictionary(let dictionary): - return dictionary - case .Array(_): - return nil - } - } - -} diff --git a/Example/Pods/HanekeSwift/Haneke/DiskCache.swift b/Example/Pods/HanekeSwift/Haneke/DiskCache.swift deleted file mode 100644 index 9f67871..0000000 --- a/Example/Pods/HanekeSwift/Haneke/DiskCache.swift +++ /dev/null @@ -1,221 +0,0 @@ -// -// DiskCache.swift -// Haneke -// -// Created by Hermes Pique on 8/10/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import Foundation - -public class DiskCache { - - public class func basePath() -> String { - let cachesPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] - let hanekePathComponent = HanekeGlobals.Domain - let basePath = (cachesPath as NSString).stringByAppendingPathComponent(hanekePathComponent) - // TODO: Do not recaculate basePath value - return basePath - } - - public let path: String - - public var size : UInt64 = 0 - - public var capacity : UInt64 = 0 { - didSet { - dispatch_async(self.cacheQueue, { - self.controlCapacity() - }) - } - } - - public lazy var cacheQueue : dispatch_queue_t = { - let queueName = HanekeGlobals.Domain + "." + (self.path as NSString).lastPathComponent - let cacheQueue = dispatch_queue_create(queueName, nil) - return cacheQueue - }() - - public init(path: String, capacity: UInt64 = UINT64_MAX) { - self.path = path - self.capacity = capacity - dispatch_async(self.cacheQueue, { - self.calculateSize() - self.controlCapacity() - }) - } - - public func setData(@autoclosure(escaping) getData: () -> NSData?, key: String) { - dispatch_async(cacheQueue, { - if let data = getData() { - self.setDataSync(data, key: key) - } else { - Log.error("Failed to get data for key \(key)") - } - }) - } - - public func fetchData(key key: String, failure fail: ((NSError?) -> ())? = nil, success succeed: (NSData) -> ()) { - dispatch_async(cacheQueue) { - let path = self.pathForKey(key) - do { - let data = try NSData(contentsOfFile: path, options: NSDataReadingOptions()) - dispatch_async(dispatch_get_main_queue()) { - succeed(data) - } - self.updateDiskAccessDateAtPath(path) - } catch { - if let block = fail { - dispatch_async(dispatch_get_main_queue()) { - block(error as NSError) - } - } - } - } - } - - public func removeData(key: String) { - dispatch_async(cacheQueue, { - let path = self.pathForKey(key) - self.removeFileAtPath(path) - }) - } - - public func removeAllData() { - let fileManager = NSFileManager.defaultManager() - let cachePath = self.path - dispatch_async(cacheQueue, { - do { - let contents = try fileManager.contentsOfDirectoryAtPath(cachePath) - for pathComponent in contents { - let path = (cachePath as NSString).stringByAppendingPathComponent(pathComponent) - do { - try fileManager.removeItemAtPath(path) - } catch { - Log.error("Failed to remove path \(path)", error as NSError) - } - } - self.calculateSize() - } catch { - Log.error("Failed to list directory", error as NSError) - } - }) - } - - public func updateAccessDate(@autoclosure(escaping) getData: () -> NSData?, key: String) { - dispatch_async(cacheQueue, { - let path = self.pathForKey(key) - let fileManager = NSFileManager.defaultManager() - if (!(fileManager.fileExistsAtPath(path) && self.updateDiskAccessDateAtPath(path))){ - if let data = getData() { - self.setDataSync(data, key: key) - } else { - Log.error("Failed to get data for key \(key)") - } - } - }) - } - - public func pathForKey(key: String) -> String { - let escapedFilename = key.escapedFilename() - let filename = escapedFilename.characters.count < Int(NAME_MAX) ? escapedFilename : key.MD5Filename() - let keyPath = (self.path as NSString).stringByAppendingPathComponent(filename) - return keyPath - } - - // MARK: Private - - private func calculateSize() { - let fileManager = NSFileManager.defaultManager() - size = 0 - let cachePath = self.path - do { - let contents = try fileManager.contentsOfDirectoryAtPath(cachePath) - for pathComponent in contents { - let path = (cachePath as NSString).stringByAppendingPathComponent(pathComponent) - do { - let attributes : NSDictionary = try fileManager.attributesOfItemAtPath(path) - size += attributes.fileSize() - } catch { - Log.error("Failed to read file size of \(path)", error as NSError) - } - } - - } catch { - Log.error("Failed to list directory", error as NSError) - } - } - - private func controlCapacity() { - if self.size <= self.capacity { return } - - let fileManager = NSFileManager.defaultManager() - let cachePath = self.path - fileManager.enumerateContentsOfDirectoryAtPath(cachePath, orderedByProperty: NSURLContentModificationDateKey, ascending: true) { (URL : NSURL, _, inout stop : Bool) -> Void in - - if let path = URL.path { - self.removeFileAtPath(path) - - stop = self.size <= self.capacity - } - } - } - - private func setDataSync(data: NSData, key: String) { - let path = self.pathForKey(key) - let fileManager = NSFileManager.defaultManager() - let previousAttributes : NSDictionary? = try? fileManager.attributesOfItemAtPath(path) - - do { - try data.writeToFile(path, options: NSDataWritingOptions.AtomicWrite) - } catch { - Log.error("Failed to write key \(key)", error as NSError) - } - - if let attributes = previousAttributes { - self.size -= attributes.fileSize() - } - self.size += UInt64(data.length) - self.controlCapacity() - } - - private func updateDiskAccessDateAtPath(path: String) -> Bool { - let fileManager = NSFileManager.defaultManager() - let now = NSDate() - do { - try fileManager.setAttributes([NSFileModificationDate : now], ofItemAtPath: path) - return true - } catch { - Log.error("Failed to update access date", error as NSError) - return false - } - } - - private func removeFileAtPath(path: String) { - let fileManager = NSFileManager.defaultManager() - do { - let attributes : NSDictionary = try fileManager.attributesOfItemAtPath(path) - let fileSize = attributes.fileSize() - do { - try fileManager.removeItemAtPath(path) - self.size -= fileSize - } catch { - Log.error("Failed to remove file", error as NSError) - } - } catch { - let castedError = error as NSError - if isNoSuchFileError(castedError) { - Log.debug("File not found", castedError) - } else { - Log.error("Failed to remove file", castedError) - } - } - } -} - -private func isNoSuchFileError(error : NSError?) -> Bool { - if let error = error { - return NSCocoaErrorDomain == error.domain && error.code == NSFileReadNoSuchFileError - } - return false -} diff --git a/Example/Pods/HanekeSwift/Haneke/DiskFetcher.swift b/Example/Pods/HanekeSwift/Haneke/DiskFetcher.swift deleted file mode 100644 index 3053c58..0000000 --- a/Example/Pods/HanekeSwift/Haneke/DiskFetcher.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// DiskFetcher.swift -// Haneke -// -// Created by Joan Romano on 9/16/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import Foundation - -extension HanekeGlobals { - - // It'd be better to define this in the DiskFetcher class but Swift doesn't allow to declare an enum in a generic type - public struct DiskFetcher { - - public enum ErrorCode : Int { - case InvalidData = -500 - } - - } - -} - -public class DiskFetcher : Fetcher { - - let path: String - var cancelled = false - - public init(path: String) { - self.path = path - let key = path - super.init(key: key) - } - - // MARK: Fetcher - - public override func fetch(failure fail: ((NSError?) -> ()), success succeed: (T.Result) -> ()) { - self.cancelled = false - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { [weak self] in - if let strongSelf = self { - strongSelf.privateFetch(failure: fail, success: succeed) - } - }) - } - - public override func cancelFetch() { - self.cancelled = true - } - - // MARK: Private - - private func privateFetch(failure fail: ((NSError?) -> ()), success succeed: (T.Result) -> ()) { - if self.cancelled { - return - } - - let data : NSData - do { - data = try NSData(contentsOfFile: self.path, options: NSDataReadingOptions()) - } catch { - dispatch_async(dispatch_get_main_queue()) { - if self.cancelled { - return - } - fail(error as NSError) - } - return - } - - if self.cancelled { - return - } - - guard let value : T.Result = T.convertFromData(data) else { - let localizedFormat = NSLocalizedString("Failed to convert value from data at path %@", comment: "Error description") - let description = String(format:localizedFormat, self.path) - let error = errorWithCode(HanekeGlobals.DiskFetcher.ErrorCode.InvalidData.rawValue, description: description) - dispatch_async(dispatch_get_main_queue()) { - fail(error) - } - return - } - - dispatch_async(dispatch_get_main_queue(), { - if self.cancelled { - return - } - succeed(value) - }) - } -} diff --git a/Example/Pods/HanekeSwift/Haneke/Fetch.swift b/Example/Pods/HanekeSwift/Haneke/Fetch.swift deleted file mode 100644 index ba4d0e6..0000000 --- a/Example/Pods/HanekeSwift/Haneke/Fetch.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// Fetch.swift -// Haneke -// -// Created by Hermes Pique on 9/28/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import Foundation - -enum FetchState { - case Pending - // Using Wrapper as a workaround for error 'unimplemented IR generation feature non-fixed multi-payload enum layout' - // See: http://swiftradar.tumblr.com/post/88314603360/swift-fails-to-compile-enum-with-two-data-cases - // See: http://owensd.io/2014/08/06/fixed-enum-layout.html - case Success(Wrapper) - case Failure(NSError?) -} - -public class Fetch { - - public typealias Succeeder = (T) -> () - - public typealias Failer = (NSError?) -> () - - private var onSuccess : Succeeder? - - private var onFailure : Failer? - - private var state : FetchState = FetchState.Pending - - public init() {} - - public func onSuccess(onSuccess: Succeeder) -> Self { - self.onSuccess = onSuccess - switch self.state { - case FetchState.Success(let wrapper): - onSuccess(wrapper.value) - default: - break - } - return self - } - - public func onFailure(onFailure: Failer) -> Self { - self.onFailure = onFailure - switch self.state { - case FetchState.Failure(let error): - onFailure(error) - default: - break - } - return self - } - - func succeed(value: T) { - self.state = FetchState.Success(Wrapper(value)) - self.onSuccess?(value) - } - - func fail(error: NSError? = nil) { - self.state = FetchState.Failure(error) - self.onFailure?(error) - } - - var hasFailed : Bool { - switch self.state { - case FetchState.Failure(_): - return true - default: - return false - } - } - - var hasSucceeded : Bool { - switch self.state { - case FetchState.Success(_): - return true - default: - return false - } - } - -} - -public class Wrapper { - public let value: T - public init(_ value: T) { self.value = value } -} diff --git a/Example/Pods/HanekeSwift/Haneke/Fetcher.swift b/Example/Pods/HanekeSwift/Haneke/Fetcher.swift deleted file mode 100644 index 8c9db5a..0000000 --- a/Example/Pods/HanekeSwift/Haneke/Fetcher.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// Fetcher.swift -// Haneke -// -// Created by Hermes Pique on 9/9/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import UIKit - -// See: http://stackoverflow.com/questions/25915306/generic-closure-in-protocol -public class Fetcher { - - public let key: String - - public init(key: String) { - self.key = key - } - - public func fetch(failure fail: ((NSError?) -> ()), success succeed: (T.Result) -> ()) {} - - public func cancelFetch() {} -} - -class SimpleFetcher : Fetcher { - - let getValue : () -> T.Result - - init(key: String, @autoclosure(escaping) value getValue : () -> T.Result) { - self.getValue = getValue - super.init(key: key) - } - - override func fetch(failure fail: ((NSError?) -> ()), success succeed: (T.Result) -> ()) { - let value = getValue() - succeed(value) - } - - override func cancelFetch() {} - -} diff --git a/Example/Pods/HanekeSwift/Haneke/Format.swift b/Example/Pods/HanekeSwift/Haneke/Format.swift deleted file mode 100644 index 1e31eb1..0000000 --- a/Example/Pods/HanekeSwift/Haneke/Format.swift +++ /dev/null @@ -1,93 +0,0 @@ -// -// Format.swift -// Haneke -// -// Created by Hermes Pique on 8/27/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import UIKit - -public struct Format { - - public let name: String - - public let diskCapacity : UInt64 - - public var transform : ((T) -> (T))? - - public var convertToData : (T -> NSData)? - - public init(name: String, diskCapacity : UInt64 = UINT64_MAX, transform: ((T) -> (T))? = nil) { - self.name = name - self.diskCapacity = diskCapacity - self.transform = transform - } - - public func apply(value : T) -> T { - var transformed = value - if let transform = self.transform { - transformed = transform(value) - } - return transformed - } - - var isIdentity : Bool { - return self.transform == nil - } - -} - -public struct ImageResizer { - - public enum ScaleMode: String { - case Fill = "fill", AspectFit = "aspectfit", AspectFill = "aspectfill", None = "none" - } - - public typealias T = UIImage - - public let allowUpscaling : Bool - - public let size : CGSize - - public let scaleMode: ScaleMode - - public let compressionQuality : Float - - public init(size: CGSize = CGSizeZero, scaleMode: ScaleMode = .None, allowUpscaling: Bool = true, compressionQuality: Float = 1.0) { - self.size = size - self.scaleMode = scaleMode - self.allowUpscaling = allowUpscaling - self.compressionQuality = compressionQuality - } - - public func resizeImage(image: UIImage) -> UIImage { - var resizeToSize: CGSize - switch self.scaleMode { - case .Fill: - resizeToSize = self.size - case .AspectFit: - resizeToSize = image.size.hnk_aspectFitSize(self.size) - case .AspectFill: - resizeToSize = image.size.hnk_aspectFillSize(self.size) - case .None: - return image - } - assert(self.size.width > 0 && self.size.height > 0, "Expected non-zero size. Use ScaleMode.None to avoid resizing.") - - // If does not allow to scale up the image - if (!self.allowUpscaling) { - if (resizeToSize.width > image.size.width || resizeToSize.height > image.size.height) { - return image - } - } - - // Avoid unnecessary computations - if (resizeToSize.width == image.size.width && resizeToSize.height == image.size.height) { - return image - } - - let resizedImage = image.hnk_imageByScalingToSize(resizeToSize) - return resizedImage - } -} diff --git a/Example/Pods/HanekeSwift/Haneke/Haneke.swift b/Example/Pods/HanekeSwift/Haneke/Haneke.swift deleted file mode 100644 index a087aca..0000000 --- a/Example/Pods/HanekeSwift/Haneke/Haneke.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// Haneke.swift -// Haneke -// -// Created by Hermes Pique on 9/9/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import UIKit - -public struct HanekeGlobals { - - public static let Domain = "io.haneke" - -} - -public struct Shared { - - public static var imageCache : Cache { - struct Static { - static let name = "shared-images" - static let cache = Cache(name: name) - } - return Static.cache - } - - public static var dataCache : Cache { - struct Static { - static let name = "shared-data" - static let cache = Cache(name: name) - } - return Static.cache - } - - public static var stringCache : Cache { - struct Static { - static let name = "shared-strings" - static let cache = Cache(name: name) - } - return Static.cache - } - - public static var JSONCache : Cache { - struct Static { - static let name = "shared-json" - static let cache = Cache(name: name) - } - return Static.cache - } -} - -func errorWithCode(code: Int, description: String) -> NSError { - let userInfo = [NSLocalizedDescriptionKey: description] - return NSError(domain: HanekeGlobals.Domain, code: code, userInfo: userInfo) -} diff --git a/Example/Pods/HanekeSwift/Haneke/Log.swift b/Example/Pods/HanekeSwift/Haneke/Log.swift deleted file mode 100644 index 07095f9..0000000 --- a/Example/Pods/HanekeSwift/Haneke/Log.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// Log.swift -// Haneke -// -// Created by Hermes Pique on 11/10/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import Foundation - -struct Log { - - private static let Tag = "[HANEKE]" - - private enum Level : String { - case Debug = "[DEBUG]" - case Error = "[ERROR]" - } - - private static func log(level: Level, @autoclosure _ message: () -> String, _ error: NSError? = nil) { - if let error = error { - NSLog("%@%@ %@ with error %@", Tag, level.rawValue, message(), error) - } else { - NSLog("%@%@ %@", Tag, level.rawValue, message()) - } - } - - static func debug(@autoclosure message: () -> String, _ error: NSError? = nil) { - #if DEBUG - log(.Debug, message, error) - #endif - } - - static func error(@autoclosure message: () -> String, _ error: NSError? = nil) { - log(.Error, message, error) - } - -} diff --git a/Example/Pods/HanekeSwift/Haneke/NSFileManager+Haneke.swift b/Example/Pods/HanekeSwift/Haneke/NSFileManager+Haneke.swift deleted file mode 100644 index 748f0ce..0000000 --- a/Example/Pods/HanekeSwift/Haneke/NSFileManager+Haneke.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// NSFileManager+Haneke.swift -// Haneke -// -// Created by Hermes Pique on 8/26/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import Foundation - -extension NSFileManager { - - func enumerateContentsOfDirectoryAtPath(path: String, orderedByProperty property: String, ascending: Bool, usingBlock block: (NSURL, Int, inout Bool) -> Void ) { - - let directoryURL = NSURL(fileURLWithPath: path) - do { - let contents = try self.contentsOfDirectoryAtURL(directoryURL, includingPropertiesForKeys: [property], options: NSDirectoryEnumerationOptions()) - let sortedContents = contents.sort({(URL1: NSURL, URL2: NSURL) -> Bool in - - // Maybe there's a better way to do this. See: http://stackoverflow.com/questions/25502914/comparing-anyobject-in-swift - - var value1 : AnyObject? - do { - try URL1.getResourceValue(&value1, forKey: property); - } catch { - return true - } - var value2 : AnyObject? - do { - try URL2.getResourceValue(&value2, forKey: property); - } catch { - return false - } - - if let string1 = value1 as? String, let string2 = value2 as? String { - return ascending ? string1 < string2 : string2 < string1 - } - - if let date1 = value1 as? NSDate, let date2 = value2 as? NSDate { - return ascending ? date1 < date2 : date2 < date1 - } - - if let number1 = value1 as? NSNumber, let number2 = value2 as? NSNumber { - return ascending ? number1 < number2 : number2 < number1 - } - - return false - }) - - for (i, v) in sortedContents.enumerate() { - var stop : Bool = false - block(v, i, &stop) - if stop { break } - } - - } catch { - Log.error("Failed to list directory", error as NSError) - } - } - -} - -func < (lhs: NSDate, rhs: NSDate) -> Bool { - return lhs.compare(rhs) == NSComparisonResult.OrderedAscending -} - -func < (lhs: NSNumber, rhs: NSNumber) -> Bool { - return lhs.compare(rhs) == NSComparisonResult.OrderedAscending -} diff --git a/Example/Pods/HanekeSwift/Haneke/NSHTTPURLResponse+Haneke.swift b/Example/Pods/HanekeSwift/Haneke/NSHTTPURLResponse+Haneke.swift deleted file mode 100644 index 82e80b3..0000000 --- a/Example/Pods/HanekeSwift/Haneke/NSHTTPURLResponse+Haneke.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// NSHTTPURLResponse+Haneke.swift -// Haneke -// -// Created by Hermes Pique on 1/2/16. -// Copyright © 2016 Haneke. All rights reserved. -// - -import Foundation - -extension NSHTTPURLResponse { - - func hnk_isValidStatusCode() -> Bool { - switch self.statusCode { - case 200...201: - return true - default: - return false - } - } - -} \ No newline at end of file diff --git a/Example/Pods/HanekeSwift/Haneke/NSURLResponse+Haneke.swift b/Example/Pods/HanekeSwift/Haneke/NSURLResponse+Haneke.swift deleted file mode 100644 index fbdde4d..0000000 --- a/Example/Pods/HanekeSwift/Haneke/NSURLResponse+Haneke.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// NSHTTPURLResponse+Haneke.swift -// Haneke -// -// Created by Hermes Pique on 9/12/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import Foundation - -extension NSURLResponse { - - func hnk_validateLengthOfData(data: NSData) -> Bool { - let expectedContentLength = self.expectedContentLength - if (expectedContentLength > -1) { - let dataLength = data.length - return Int64(dataLength) >= expectedContentLength - } - return true - } - -} \ No newline at end of file diff --git a/Example/Pods/HanekeSwift/Haneke/NetworkFetcher.swift b/Example/Pods/HanekeSwift/Haneke/NetworkFetcher.swift deleted file mode 100644 index 3448f20..0000000 --- a/Example/Pods/HanekeSwift/Haneke/NetworkFetcher.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// NetworkFetcher.swift -// Haneke -// -// Created by Hermes Pique on 9/12/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import UIKit - -extension HanekeGlobals { - - // It'd be better to define this in the NetworkFetcher class but Swift doesn't allow to declare an enum in a generic type - public struct NetworkFetcher { - - public enum ErrorCode : Int { - case InvalidData = -400 - case MissingData = -401 - case InvalidStatusCode = -402 - } - - } - -} - -public class NetworkFetcher : Fetcher { - - let URL : NSURL - - public init(URL : NSURL) { - self.URL = URL - - let key = URL.absoluteString - super.init(key: key) - } - - public var session : NSURLSession { return NSURLSession.sharedSession() } - - var task : NSURLSessionDataTask? = nil - - var cancelled = false - - // MARK: Fetcher - - public override func fetch(failure fail : ((NSError?) -> ()), success succeed : (T.Result) -> ()) { - self.cancelled = false - self.task = self.session.dataTaskWithURL(self.URL) {[weak self] (data, response, error) -> Void in - if let strongSelf = self { - strongSelf.onReceiveData(data, response: response, error: error, failure: fail, success: succeed) - } - } - self.task?.resume() - } - - public override func cancelFetch() { - self.task?.cancel() - self.cancelled = true - } - - // MARK: Private - - private func onReceiveData(data: NSData!, response: NSURLResponse!, error: NSError!, failure fail: ((NSError?) -> ()), success succeed: (T.Result) -> ()) { - - if cancelled { return } - - let URL = self.URL - - if let error = error { - if (error.domain == NSURLErrorDomain && error.code == NSURLErrorCancelled) { return } - - Log.debug("Request \(URL.absoluteString) failed", error) - dispatch_async(dispatch_get_main_queue(), { fail(error) }) - return - } - - if let httpResponse = response as? NSHTTPURLResponse where !httpResponse.hnk_isValidStatusCode() { - let description = NSHTTPURLResponse.localizedStringForStatusCode(httpResponse.statusCode) - self.failWithCode(.InvalidStatusCode, localizedDescription: description, failure: fail) - return - } - - if !response.hnk_validateLengthOfData(data) { - let localizedFormat = NSLocalizedString("Request expected %ld bytes and received %ld bytes", comment: "Error description") - let description = String(format:localizedFormat, response.expectedContentLength, data.length) - self.failWithCode(.MissingData, localizedDescription: description, failure: fail) - return - } - - guard let value = T.convertFromData(data) else { - let localizedFormat = NSLocalizedString("Failed to convert value from data at URL %@", comment: "Error description") - let description = String(format:localizedFormat, URL.absoluteString) - self.failWithCode(.InvalidData, localizedDescription: description, failure: fail) - return - } - - dispatch_async(dispatch_get_main_queue()) { succeed(value) } - - } - - private func failWithCode(code: HanekeGlobals.NetworkFetcher.ErrorCode, localizedDescription: String, failure fail: ((NSError?) -> ())) { - let error = errorWithCode(code.rawValue, description: localizedDescription) - Log.debug(localizedDescription, error) - dispatch_async(dispatch_get_main_queue()) { fail(error) } - } -} diff --git a/Example/Pods/HanekeSwift/Haneke/String+Haneke.swift b/Example/Pods/HanekeSwift/Haneke/String+Haneke.swift deleted file mode 100644 index 9a13195..0000000 --- a/Example/Pods/HanekeSwift/Haneke/String+Haneke.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// String+Haneke.swift -// Haneke -// -// Created by Hermes Pique on 8/30/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import Foundation - -extension String { - - func escapedFilename() -> String { - let originalString = self as NSString as CFString - let charactersToLeaveUnescaped = " \\" as NSString as CFString // TODO: Add more characters that are valid in paths but not in URLs - let legalURLCharactersToBeEscaped = "/:" as NSString as CFString - let encoding = CFStringBuiltInEncodings.UTF8.rawValue - let escapedPath = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, originalString, charactersToLeaveUnescaped, legalURLCharactersToBeEscaped, encoding) - return escapedPath as NSString as String - } - - func MD5String() -> String { - guard let data = self.dataUsingEncoding(NSUTF8StringEncoding) else { - return self - } - - let MD5Calculator = MD5(data) - let MD5Data = MD5Calculator.calculate() - let resultBytes = UnsafeMutablePointer(MD5Data.bytes) - let resultEnumerator = UnsafeBufferPointer(start: resultBytes, count: MD5Data.length) - let MD5String = NSMutableString() - for c in resultEnumerator { - MD5String.appendFormat("%02x", c) - } - return MD5String as String - } - - func MD5Filename() -> String { - let MD5String = self.MD5String() - let pathExtension = (self as NSString).pathExtension - if pathExtension.characters.count > 0 { - return (MD5String as NSString).stringByAppendingPathExtension(pathExtension) ?? MD5String - } else { - return MD5String - } - } - -} \ No newline at end of file diff --git a/Example/Pods/HanekeSwift/Haneke/UIButton+Haneke.swift b/Example/Pods/HanekeSwift/Haneke/UIButton+Haneke.swift deleted file mode 100644 index 74c6292..0000000 --- a/Example/Pods/HanekeSwift/Haneke/UIButton+Haneke.swift +++ /dev/null @@ -1,234 +0,0 @@ -// -// UIButton+Haneke.swift -// Haneke -// -// Created by Joan Romano on 10/1/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import UIKit - -public extension UIButton { - - public var hnk_imageFormat : Format { - let bounds = self.bounds - assert(bounds.size.width > 0 && bounds.size.height > 0, "[\(Mirror(reflecting: self).description) \(__FUNCTION__)]: UIButton size is zero. Set its frame, call sizeToFit or force layout first. You can also set a custom format with a defined size if you don't want to force layout.") - let contentRect = self.contentRectForBounds(bounds) - let imageInsets = self.imageEdgeInsets - let scaleMode = self.contentHorizontalAlignment != UIControlContentHorizontalAlignment.Fill || self.contentVerticalAlignment != UIControlContentVerticalAlignment.Fill ? ImageResizer.ScaleMode.AspectFit : ImageResizer.ScaleMode.Fill - let imageSize = CGSizeMake(CGRectGetWidth(contentRect) - imageInsets.left - imageInsets.right, CGRectGetHeight(contentRect) - imageInsets.top - imageInsets.bottom) - - return HanekeGlobals.UIKit.formatWithSize(imageSize, scaleMode: scaleMode, allowUpscaling: scaleMode == ImageResizer.ScaleMode.AspectFit ? false : true) - } - - public func hnk_setImageFromURL(URL: NSURL, state: UIControlState = .Normal, placeholder: UIImage? = nil, format: Format? = nil, failure fail: ((NSError?) -> ())? = nil, success succeed: ((UIImage) -> ())? = nil) { - let fetcher = NetworkFetcher(URL: URL) - self.hnk_setImageFromFetcher(fetcher, state: state, placeholder: placeholder, format: format, failure: fail, success: succeed) - } - - public func hnk_setImage(image: UIImage, key: String, state: UIControlState = .Normal, placeholder: UIImage? = nil, format: Format? = nil, success succeed: ((UIImage) -> ())? = nil) { - let fetcher = SimpleFetcher(key: key, value: image) - self.hnk_setImageFromFetcher(fetcher, state: state, placeholder: placeholder, format: format, success: succeed) - } - - public func hnk_setImageFromFile(path: String, state: UIControlState = .Normal, placeholder: UIImage? = nil, format: Format? = nil, failure fail: ((NSError?) -> ())? = nil, success succeed: ((UIImage) -> ())? = nil) { - let fetcher = DiskFetcher(path: path) - self.hnk_setImageFromFetcher(fetcher, state: state, placeholder: placeholder, format: format, failure: fail, success: succeed) - } - - public func hnk_setImageFromFetcher(fetcher: Fetcher, state: UIControlState = .Normal, placeholder: UIImage? = nil, format: Format? = nil, failure fail: ((NSError?) -> ())? = nil, success succeed: ((UIImage) -> ())? = nil){ - self.hnk_cancelSetImage() - self.hnk_imageFetcher = fetcher - - let didSetImage = self.hnk_fetchImageForFetcher(fetcher, state: state, format : format, failure: fail, success: succeed) - - if didSetImage { return } - - if let placeholder = placeholder { - self.setImage(placeholder, forState: state) - } - } - - public func hnk_cancelSetImage() { - if let fetcher = self.hnk_imageFetcher { - fetcher.cancelFetch() - self.hnk_imageFetcher = nil - } - } - - // MARK: Internal Image - - // See: http://stackoverflow.com/questions/25907421/associating-swift-things-with-nsobject-instances - var hnk_imageFetcher : Fetcher! { - get { - let wrapper = objc_getAssociatedObject(self, &HanekeGlobals.UIKit.SetImageFetcherKey) as? ObjectWrapper - let fetcher = wrapper?.value as? Fetcher - return fetcher - } - set (fetcher) { - var wrapper : ObjectWrapper? - if let fetcher = fetcher { - wrapper = ObjectWrapper(value: fetcher) - } - objc_setAssociatedObject(self, &HanekeGlobals.UIKit.SetImageFetcherKey, wrapper, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - } - - func hnk_fetchImageForFetcher(fetcher : Fetcher, state : UIControlState = .Normal, format : Format? = nil, failure fail : ((NSError?) -> ())?, success succeed : ((UIImage) -> ())?) -> Bool { - let format = format ?? self.hnk_imageFormat - let cache = Shared.imageCache - if cache.formats[format.name] == nil { - cache.addFormat(format) - } - var animated = false - let fetch = cache.fetch(fetcher: fetcher, formatName: format.name, failure: {[weak self] error in - if let strongSelf = self { - if strongSelf.hnk_shouldCancelImageForKey(fetcher.key) { return } - - strongSelf.hnk_imageFetcher = nil - - fail?(error) - } - }) { [weak self] image in - if let strongSelf = self { - if strongSelf.hnk_shouldCancelImageForKey(fetcher.key) { return } - - strongSelf.hnk_setImage(image, state: state, animated: animated, success: succeed) - } - } - animated = true - return fetch.hasSucceeded - } - - - func hnk_setImage(image : UIImage, state : UIControlState, animated : Bool, success succeed : ((UIImage) -> ())?) { - self.hnk_imageFetcher = nil - - if let succeed = succeed { - succeed(image) - } else if animated { - UIView.transitionWithView(self, duration: HanekeGlobals.UIKit.SetImageAnimationDuration, options: .TransitionCrossDissolve, animations: { - self.setImage(image, forState: state) - }, completion: nil) - } else { - self.setImage(image, forState: state) - } - } - - func hnk_shouldCancelImageForKey(key:String) -> Bool { - if self.hnk_imageFetcher?.key == key { return false } - - Log.debug("Cancelled set image for \((key as NSString).lastPathComponent)") - return true - } - - // MARK: Background image - - public var hnk_backgroundImageFormat : Format { - let bounds = self.bounds - assert(bounds.size.width > 0 && bounds.size.height > 0, "[\(Mirror(reflecting: self).description) \(__FUNCTION__)]: UIButton size is zero. Set its frame, call sizeToFit or force layout first. You can also set a custom format with a defined size if you don't want to force layout.") - let imageSize = self.backgroundRectForBounds(bounds).size - - return HanekeGlobals.UIKit.formatWithSize(imageSize, scaleMode: .Fill) - } - - public func hnk_setBackgroundImageFromURL(URL : NSURL, state : UIControlState = .Normal, placeholder : UIImage? = nil, format : Format? = nil, failure fail : ((NSError?) -> ())? = nil, success succeed : ((UIImage) -> ())? = nil) { - let fetcher = NetworkFetcher(URL: URL) - self.hnk_setBackgroundImageFromFetcher(fetcher, state: state, placeholder: placeholder, format: format, failure: fail, success: succeed) - } - - public func hnk_setBackgroundImage(image : UIImage, key: String, state : UIControlState = .Normal, placeholder : UIImage? = nil, format : Format? = nil, success succeed : ((UIImage) -> ())? = nil) { - let fetcher = SimpleFetcher(key: key, value: image) - self.hnk_setBackgroundImageFromFetcher(fetcher, state: state, placeholder: placeholder, format: format, success: succeed) - } - - public func hnk_setBackgroundImageFromFile(path: String, state : UIControlState = .Normal, placeholder : UIImage? = nil, format : Format? = nil, failure fail : ((NSError?) -> ())? = nil, success succeed : ((UIImage) -> ())? = nil) { - let fetcher = DiskFetcher(path: path) - self.hnk_setBackgroundImageFromFetcher(fetcher, state: state, placeholder: placeholder, format: format, failure: fail, success: succeed) - } - - public func hnk_setBackgroundImageFromFetcher(fetcher : Fetcher, state : UIControlState = .Normal, placeholder : UIImage? = nil, format : Format? = nil, failure fail : ((NSError?) -> ())? = nil, success succeed : ((UIImage) -> ())? = nil){ - self.hnk_cancelSetBackgroundImage() - self.hnk_backgroundImageFetcher = fetcher - - let didSetImage = self.hnk_fetchBackgroundImageForFetcher(fetcher, state: state, format : format, failure: fail, success: succeed) - - if didSetImage { return } - - if let placeholder = placeholder { - self.setBackgroundImage(placeholder, forState: state) - } - } - - public func hnk_cancelSetBackgroundImage() { - if let fetcher = self.hnk_backgroundImageFetcher { - fetcher.cancelFetch() - self.hnk_backgroundImageFetcher = nil - } - } - - // MARK: Internal Background image - - // See: http://stackoverflow.com/questions/25907421/associating-swift-things-with-nsobject-instances - var hnk_backgroundImageFetcher : Fetcher! { - get { - let wrapper = objc_getAssociatedObject(self, &HanekeGlobals.UIKit.SetBackgroundImageFetcherKey) as? ObjectWrapper - let fetcher = wrapper?.value as? Fetcher - return fetcher - } - set (fetcher) { - var wrapper : ObjectWrapper? - if let fetcher = fetcher { - wrapper = ObjectWrapper(value: fetcher) - } - objc_setAssociatedObject(self, &HanekeGlobals.UIKit.SetBackgroundImageFetcherKey, wrapper, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - } - - func hnk_fetchBackgroundImageForFetcher(fetcher: Fetcher, state: UIControlState = .Normal, format: Format? = nil, failure fail: ((NSError?) -> ())?, success succeed : ((UIImage) -> ())?) -> Bool { - let format = format ?? self.hnk_backgroundImageFormat - let cache = Shared.imageCache - if cache.formats[format.name] == nil { - cache.addFormat(format) - } - var animated = false - let fetch = cache.fetch(fetcher: fetcher, formatName: format.name, failure: {[weak self] error in - if let strongSelf = self { - if strongSelf.hnk_shouldCancelBackgroundImageForKey(fetcher.key) { return } - - strongSelf.hnk_backgroundImageFetcher = nil - - fail?(error) - } - }) { [weak self] image in - if let strongSelf = self { - if strongSelf.hnk_shouldCancelBackgroundImageForKey(fetcher.key) { return } - - strongSelf.hnk_setBackgroundImage(image, state: state, animated: animated, success: succeed) - } - } - animated = true - return fetch.hasSucceeded - } - - func hnk_setBackgroundImage(image: UIImage, state: UIControlState, animated: Bool, success succeed: ((UIImage) -> ())?) { - self.hnk_backgroundImageFetcher = nil - - if let succeed = succeed { - succeed(image) - } else if animated { - UIView.transitionWithView(self, duration: HanekeGlobals.UIKit.SetImageAnimationDuration, options: .TransitionCrossDissolve, animations: { - self.setBackgroundImage(image, forState: state) - }, completion: nil) - } else { - self.setBackgroundImage(image, forState: state) - } - } - - func hnk_shouldCancelBackgroundImageForKey(key: String) -> Bool { - if self.hnk_backgroundImageFetcher?.key == key { return false } - - Log.debug("Cancelled set background image for \((key as NSString).lastPathComponent)") - return true - } -} diff --git a/Example/Pods/HanekeSwift/Haneke/UIImage+Haneke.swift b/Example/Pods/HanekeSwift/Haneke/UIImage+Haneke.swift deleted file mode 100644 index ce6ef9a..0000000 --- a/Example/Pods/HanekeSwift/Haneke/UIImage+Haneke.swift +++ /dev/null @@ -1,81 +0,0 @@ -// -// UIImage+Haneke.swift -// Haneke -// -// Created by Hermes Pique on 8/10/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import UIKit - -extension UIImage { - - func hnk_imageByScalingToSize(toSize: CGSize) -> UIImage { - UIGraphicsBeginImageContextWithOptions(toSize, !hnk_hasAlpha(), 0.0) - drawInRect(CGRectMake(0, 0, toSize.width, toSize.height)) - let resizedImage = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - return resizedImage - } - - func hnk_hasAlpha() -> Bool { - let alpha = CGImageGetAlphaInfo(self.CGImage) - switch alpha { - case .First, .Last, .PremultipliedFirst, .PremultipliedLast, .Only: - return true - case .None, .NoneSkipFirst, .NoneSkipLast: - return false - } - } - - func hnk_data(compressionQuality compressionQuality: Float = 1.0) -> NSData! { - let hasAlpha = self.hnk_hasAlpha() - let data = hasAlpha ? UIImagePNGRepresentation(self) : UIImageJPEGRepresentation(self, CGFloat(compressionQuality)) - return data - } - - func hnk_decompressedImage() -> UIImage! { - let originalImageRef = self.CGImage - let originalBitmapInfo = CGImageGetBitmapInfo(originalImageRef) - let alphaInfo = CGImageGetAlphaInfo(originalImageRef) - - // See: http://stackoverflow.com/questions/23723564/which-cgimagealphainfo-should-we-use - var bitmapInfo = originalBitmapInfo - switch (alphaInfo) { - case .None: - let rawBitmapInfoWithoutAlpha = bitmapInfo.rawValue & ~CGBitmapInfo.AlphaInfoMask.rawValue - let rawBitmapInfo = rawBitmapInfoWithoutAlpha | CGImageAlphaInfo.NoneSkipFirst.rawValue - bitmapInfo = CGBitmapInfo(rawValue: rawBitmapInfo) - case .PremultipliedFirst, .PremultipliedLast, .NoneSkipFirst, .NoneSkipLast: - break - case .Only, .Last, .First: // Unsupported - return self - } - - let colorSpace = CGColorSpaceCreateDeviceRGB() - let pixelSize = CGSizeMake(self.size.width * self.scale, self.size.height * self.scale) - guard let context = CGBitmapContextCreate(nil, Int(ceil(pixelSize.width)), Int(ceil(pixelSize.height)), CGImageGetBitsPerComponent(originalImageRef), 0, colorSpace, bitmapInfo.rawValue) else { - return self - } - - let imageRect = CGRectMake(0, 0, pixelSize.width, pixelSize.height) - UIGraphicsPushContext(context) - - // Flip coordinate system. See: http://stackoverflow.com/questions/506622/cgcontextdrawimage-draws-image-upside-down-when-passed-uiimage-cgimage - CGContextTranslateCTM(context, 0, pixelSize.height) - CGContextScaleCTM(context, 1.0, -1.0) - - // UIImage and drawInRect takes into account image orientation, unlike CGContextDrawImage. - self.drawInRect(imageRect) - UIGraphicsPopContext() - - guard let decompressedImageRef = CGBitmapContextCreateImage(context) else { - return self - } - - let scale = UIScreen.mainScreen().scale - let image = UIImage(CGImage: decompressedImageRef, scale:scale, orientation:UIImageOrientation.Up) - return image - } - -} diff --git a/Example/Pods/HanekeSwift/Haneke/UIImageView+Haneke.swift b/Example/Pods/HanekeSwift/Haneke/UIImageView+Haneke.swift deleted file mode 100644 index f6d6613..0000000 --- a/Example/Pods/HanekeSwift/Haneke/UIImageView+Haneke.swift +++ /dev/null @@ -1,139 +0,0 @@ -// -// UIImageView+Haneke.swift -// Haneke -// -// Created by Hermes Pique on 9/17/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import UIKit - -public extension UIImageView { - - public var hnk_format : Format { - let viewSize = self.bounds.size - assert(viewSize.width > 0 && viewSize.height > 0, "[\(Mirror(reflecting: self).description) \(__FUNCTION__)]: UImageView size is zero. Set its frame, call sizeToFit or force layout first.") - let scaleMode = self.hnk_scaleMode - return HanekeGlobals.UIKit.formatWithSize(viewSize, scaleMode: scaleMode) - } - - public func hnk_setImageFromURL(URL: NSURL, placeholder : UIImage? = nil, format : Format? = nil, failure fail : ((NSError?) -> ())? = nil, success succeed : ((UIImage) -> ())? = nil) { - let fetcher = NetworkFetcher(URL: URL) - self.hnk_setImageFromFetcher(fetcher, placeholder: placeholder, format: format, failure: fail, success: succeed) - } - - public func hnk_setImage(@autoclosure(escaping) image: () -> UIImage, key: String, placeholder : UIImage? = nil, format : Format? = nil, success succeed : ((UIImage) -> ())? = nil) { - let fetcher = SimpleFetcher(key: key, value: image) - self.hnk_setImageFromFetcher(fetcher, placeholder: placeholder, format: format, success: succeed) - } - - public func hnk_setImageFromFile(path: String, placeholder : UIImage? = nil, format : Format? = nil, failure fail : ((NSError?) -> ())? = nil, success succeed : ((UIImage) -> ())? = nil) { - let fetcher = DiskFetcher(path: path) - self.hnk_setImageFromFetcher(fetcher, placeholder: placeholder, format: format, failure: fail, success: succeed) - } - - public func hnk_setImageFromFetcher(fetcher : Fetcher, - placeholder : UIImage? = nil, - format : Format? = nil, - failure fail : ((NSError?) -> ())? = nil, - success succeed : ((UIImage) -> ())? = nil) { - - self.hnk_cancelSetImage() - - self.hnk_fetcher = fetcher - - let didSetImage = self.hnk_fetchImageForFetcher(fetcher, format: format, failure: fail, success: succeed) - - if didSetImage { return } - - if let placeholder = placeholder { - self.image = placeholder - } - } - - public func hnk_cancelSetImage() { - if let fetcher = self.hnk_fetcher { - fetcher.cancelFetch() - self.hnk_fetcher = nil - } - } - - // MARK: Internal - - // See: http://stackoverflow.com/questions/25907421/associating-swift-things-with-nsobject-instances - var hnk_fetcher : Fetcher! { - get { - let wrapper = objc_getAssociatedObject(self, &HanekeGlobals.UIKit.SetImageFetcherKey) as? ObjectWrapper - let fetcher = wrapper?.value as? Fetcher - return fetcher - } - set (fetcher) { - var wrapper : ObjectWrapper? - if let fetcher = fetcher { - wrapper = ObjectWrapper(value: fetcher) - } - objc_setAssociatedObject(self, &HanekeGlobals.UIKit.SetImageFetcherKey, wrapper, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - } - - public var hnk_scaleMode : ImageResizer.ScaleMode { - switch (self.contentMode) { - case .ScaleToFill: - return .Fill - case .ScaleAspectFit: - return .AspectFit - case .ScaleAspectFill: - return .AspectFill - case .Redraw, .Center, .Top, .Bottom, .Left, .Right, .TopLeft, .TopRight, .BottomLeft, .BottomRight: - return .None - } - } - - func hnk_fetchImageForFetcher(fetcher : Fetcher, format : Format? = nil, failure fail : ((NSError?) -> ())?, success succeed : ((UIImage) -> ())?) -> Bool { - let cache = Shared.imageCache - let format = format ?? self.hnk_format - if cache.formats[format.name] == nil { - cache.addFormat(format) - } - var animated = false - let fetch = cache.fetch(fetcher: fetcher, formatName: format.name, failure: {[weak self] error in - if let strongSelf = self { - if strongSelf.hnk_shouldCancelForKey(fetcher.key) { return } - - strongSelf.hnk_fetcher = nil - - fail?(error) - } - }) { [weak self] image in - if let strongSelf = self { - if strongSelf.hnk_shouldCancelForKey(fetcher.key) { return } - - strongSelf.hnk_setImage(image, animated: animated, success: succeed) - } - } - animated = true - return fetch.hasSucceeded - } - - func hnk_setImage(image : UIImage, animated : Bool, success succeed : ((UIImage) -> ())?) { - self.hnk_fetcher = nil - - if let succeed = succeed { - succeed(image) - } else if animated { - UIView.transitionWithView(self, duration: HanekeGlobals.UIKit.SetImageAnimationDuration, options: .TransitionCrossDissolve, animations: { - self.image = image - }, completion: nil) - } else { - self.image = image - } - } - - func hnk_shouldCancelForKey(key:String) -> Bool { - if self.hnk_fetcher?.key == key { return false } - - Log.debug("Cancelled set image for \((key as NSString).lastPathComponent)") - return true - } - -} diff --git a/Example/Pods/HanekeSwift/Haneke/UIView+Haneke.swift b/Example/Pods/HanekeSwift/Haneke/UIView+Haneke.swift deleted file mode 100644 index 2a7bf1b..0000000 --- a/Example/Pods/HanekeSwift/Haneke/UIView+Haneke.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// UIView+Haneke.swift -// Haneke -// -// Created by Joan Romano on 15/10/14. -// Copyright (c) 2014 Haneke. All rights reserved. -// - -import UIKit - -public extension HanekeGlobals { - - public struct UIKit { - - static func formatWithSize(size : CGSize, scaleMode : ImageResizer.ScaleMode, allowUpscaling: Bool = true) -> Format { - let name = "auto-\(size.width)x\(size.height)-\(scaleMode.rawValue)" - let cache = Shared.imageCache - if let (format,_,_) = cache.formats[name] { - return format - } - - var format = Format(name: name, - diskCapacity: HanekeGlobals.UIKit.DefaultFormat.DiskCapacity) { - let resizer = ImageResizer(size:size, - scaleMode: scaleMode, - allowUpscaling: allowUpscaling, - compressionQuality: HanekeGlobals.UIKit.DefaultFormat.CompressionQuality) - return resizer.resizeImage($0) - } - format.convertToData = {(image : UIImage) -> NSData in - image.hnk_data(compressionQuality: HanekeGlobals.UIKit.DefaultFormat.CompressionQuality) - } - return format - } - - public struct DefaultFormat { - - public static let DiskCapacity : UInt64 = 50 * 1024 * 1024 - public static let CompressionQuality : Float = 0.75 - - } - - static var SetImageAnimationDuration = 0.1 - static var SetImageFetcherKey = 0 - static var SetBackgroundImageFetcherKey = 1 - } - -} diff --git a/Example/Pods/HanekeSwift/LICENSE b/Example/Pods/HanekeSwift/LICENSE deleted file mode 100644 index 0e1120f..0000000 --- a/Example/Pods/HanekeSwift/LICENSE +++ /dev/null @@ -1,193 +0,0 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2014 Hermes Pique (@hpique) - 2014 Joan Romano (@joanromano) - 2014 Luis Ascorbe (@lascorbe) - 2014 Oriol Blanc (@oriolblanc) - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/Example/Pods/HanekeSwift/README.md b/Example/Pods/HanekeSwift/README.md deleted file mode 100644 index 53302a5..0000000 --- a/Example/Pods/HanekeSwift/README.md +++ /dev/null @@ -1,249 +0,0 @@ -![Haneke](https://raw.githubusercontent.com/Haneke/HanekeSwift/master/Assets/github-header.png) - -[![Cocoapods Version](https://cocoapod-badges.herokuapp.com/v/HanekeSwift/badge.png)](http://cocoadocs.org/docsets/Haneke) -[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) -[![Platform](https://cocoapod-badges.herokuapp.com/p/HanekeSwift/badge.png)](http://cocoadocs.org/docsets/Haneke) -[![Build Status](https://travis-ci.org/Haneke/HanekeSwift.svg?branch=master)](https://travis-ci.org/Haneke/Haneke) -[![Join the chat at https://gitter.im/Haneke/HanekeSwift](https://badges.gitter.im/Haneke/HanekeSwift.svg)](https://gitter.im/Haneke/HanekeSwift?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -Haneke is a lightweight *generic* cache for iOS written in Swift 2.0. It's designed to be super-simple to use. Here's how you would initalize a JSON cache and fetch objects from a url: - -```swift -let cache = Cache(name: "github") -let URL = NSURL(string: "https://api.github.com/users/haneke")! - -cache.fetch(URL: URL).onSuccess { JSON in - print(JSON.dictionary?["bio"]) -} -``` - -Haneke provides a memory and LRU disk cache for `UIImage`, `NSData`, `JSON`, `String` or any other type that can be read or written as data. - -Particularly, Haneke excels at working with images. It includes a zero-config image cache with automatic resizing. Everything is done in background, allowing for fast, responsive scrolling. Asking Haneke to load, resize, cache and display an *appropriately sized image* is as simple as: - -```swift -imageView.hnk_setImageFromURL(url) -``` - -_Really._ - -## Features - -* Generic cache with out-of-the-box support for `UIImage`, `NSData`, `JSON` and `String` -* First-level memory cache using `NSCache` -* Second-level LRU disk cache using the file system -* Asynchronous [fetching](#fetchers) of original values from network or disk -* All disk access is performed in background -* Thread-safe -* Automatic cache eviction on memory warnings or disk capacity reached -* Comprehensive unit tests -* Extensible by defining [custom formats](#formats), supporting [additional types](#supporting-additional-types) or implementing [custom fetchers](#custom-fetchers) - -For images: - -* Zero-config `UIImageView` and `UIButton` extensions to use the cache, optimized for `UITableView` and `UICollectionView` cell reuse -* Background image resizing and decompression - -## Installation - -Using [CocoaPods](http://cocoapods.org/): - -```ruby -use_frameworks! -pod 'HanekeSwift' -``` - -Using [Carthage](https://github.com/Carthage/Carthage): - -``` -github "Haneke/HanekeSwift" -``` - -Manually: - -1. Drag `Haneke.xcodeproj` to your project in the _Project Navigator_. -2. Select your project and then your app target. Open the _Build Phases_ panel. -3. Expand the _Target Dependencies_ group, and add `Haneke.framework`. -4. Click on the `+` button at the top left of the panel and select _New Copy Files Phase_. Set _Destination_ to _Frameworks_, and add `Haneke.framework`. -5. `import Haneke` whenever you want to use Haneke. - -## Requirements - -- iOS 8.0+ -- Swift 2.0 - -## Using the cache - -Haneke provides shared caches for `UIImage`, `NSData`, `JSON` and `String`. You can also create your own caches. - -The cache is a key-value store. For example, here's how you would cache and then fetch some data. - -```Swift -let cache = Shared.dataCache - -cache.set(value: data, key: "funny-games.mp4") - -// Eventually... - -cache.fetch(key: "funny-games.mp4").onSuccess { data in - // Do something with data -} -``` - -In most cases the value will not be readily available and will have to be fetched from network or disk. Haneke offers convenience `fetch` functions for these cases. Let's go back to the first example, now using a shared cache: - -```Swift -let cache = Shared.JSONCache -let URL = NSURL(string: "https://api.github.com/users/haneke")! - -cache.fetch(URL: URL).onSuccess { JSON in - print(JSON.dictionary?["bio"]) -} -``` - -The above call will first attempt to fetch the required JSON from (in order) memory, disk or `NSURLCache`. If not available, Haneke will fetch the JSON from the source, return it and then cache it. In this case, the URL itself is used as the key. - -Further customization can be achieved by using [formats](#formats), [supporting additional types](#supporting-additional-types) or implementing [custom fetchers](#custom-fetchers). - -## Extra ♡ for images - -Need to cache and display images? Haneke provides convenience methods for `UIImageView` and `UIButton` with optimizations for `UITableView` and `UICollectionView` cell reuse. Images will be resized appropriately and cached in a shared cache. - -```swift -// Setting a remote image -imageView.hnk_setImageFromURL(url) - -// Setting an image manually. Requires you to provide a key. -imageView.hnk_setImage(image, key: key) -``` - -The above lines take care of: - -1. If cached, retrieving an appropriately sized image (based on the `bounds` and `contentMode` of the `UIImageView`) from the memory or disk cache. Disk access is performed in background. -2. If not cached, loading the original image from web/memory and producing an appropriately sized image, both in background. Remote images will be retrieved from the shared `NSURLCache` if available. -3. Setting the image and animating the change if appropriate. -4. Or doing nothing if the `UIImageView` was reused during any of the above steps. -5. Caching the resulting image. -6. If needed, evicting the least recently used images in the cache. - -## Formats - -Formats allow to specify the disk cache size and any transformations to the values before being cached. For example, the `UIImageView` extension uses a format that resizes images to fit or fill the image view as needed. - -You can also use custom formats. Say you want to limit the disk capacity for icons to 10MB and apply rounded corners to the images. This is how it could look like: - -```swift -let cache = Shared.imageCache - -let iconFormat = Format(name: "icons", diskCapacity: 10 * 1024 * 1024) { image in - return imageByRoundingCornersOfImage(image) -} -cache.addFormat(iconFormat) - -let URL = NSURL(string: "http://haneke.io/icon.png")! -cache.fetch(URL: URL, formatName: "icons").onSuccess { image in - // image will be a nice rounded icon -} -``` - -Because we told the cache to use the `"icons"` format Haneke will execute the format transformation in background and return the resulting value. - -Formats can also be used from the `UIKit` extensions: - -```swift -imageView.hnk_setImageFromURL(url, format: iconFormat) -``` - -## Fetchers - -The `fetch` functions for urls and paths are actually convenience methods. Under the hood Haneke uses fetcher objects. To illustrate, here's another way of fetching from a url by explictly using a network fetcher: - -```swift -let URL = NSURL(string: "http://haneke.io/icon.png")! -let fetcher = NetworkFetcher(URL: URL) -cache.fetch(fetcher: fetcher).onSuccess { image in - // Do something with image -} -``` - -Fetching an original value from network or disk is an expensive operation. Fetchers act as a proxy for the value, and allow Haneke to perform the fetch operation only if absolutely necessary. - -In the above example the fetcher will be executed only if there is no value associated with `"http://haneke.io/icon.png"` in the memory or disk cache. If that happens, the fetcher will be responsible from fetching the original value, which will then be cached to avoid further network activity. - -Haneke provides two specialized fetchers: `NetworkFetcher` and `DiskFetcher`. You can also implement your own fetchers by subclassing `Fetcher`. - -### Custom fetchers - -Through custom fetchers you can fetch original values from other sources than network or disk (e.g., Core Data), or even change how Haneke acceses network or disk (e.g., use [Alamofire](https://github.com/Alamofire/Alamofire) for networking instead of `NSURLSession`). A custom fetcher must subclass `Fetcher` and is responsible for: - -* Providing the key (e.g., `NSURL.absoluteString` in the case of `NetworkFetcher`) associated with the value to be fetched -* Fetching the value in background and calling the success or failure closure accordingly, both in the main queue -* Cancelling the fetch on demand, if possible - -Fetchers are generic, and the only restriction on their type is that it must implement `DataConvertible`. - -## Supporting additional types - -Haneke can cache any type that can be read and saved as data. This is indicated to Haneke by implementing the protocols `DataConvertible` and `DataRepresentable`. - -```Swift -public protocol DataConvertible { - typealias Result - - class func convertFromData(data:NSData) -> Result? - -} - -public protocol DataRepresentable { - - func asData() -> NSData! - -} -``` - -This is how one could add support for `NSDictionary`: - -```Swift -extension NSDictionary : DataConvertible, DataRepresentable { - - public typealias Result = NSDictionary - - public class func convertFromData(data:NSData) -> Result? { - return NSKeyedUnarchiver.unarchiveObjectWithData(data) as? NSDictionary - } - - public func asData() -> NSData! { - return NSKeyedArchiver.archivedDataWithRootObject(self) - } - -} -``` - -Then creating a `NSDictionary` cache would be as simple as: - -```swift -let cache = Cache(name: "dictionaries") -``` - -## Roadmap - -Haneke Swift is in initial development and its public API should not be considered stable. - -## License - - Copyright 2014 Hermes Pique ([@hpique](https://twitter.com/hpique)) -                 2014 Joan Romano ([@joanromano](https://twitter.com/joanromano)) -                 2014 Luis Ascorbe ([@lascorbe](https://twitter.com/Lascorbe)) -                 2014 Oriol Blanc ([@oriolblanc](https://twitter.com/oriolblanc)) - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/Example/Pods/Headers/Private/INSNibLoading/UIView+INSNibLoading.h b/Example/Pods/Headers/Private/INSNibLoading/UIView+INSNibLoading.h deleted file mode 120000 index 9f09db9..0000000 --- a/Example/Pods/Headers/Private/INSNibLoading/UIView+INSNibLoading.h +++ /dev/null @@ -1 +0,0 @@ -../../../INSNibLoading/INSNibLoading/iOS/UIView+INSNibLoading.h \ No newline at end of file diff --git a/Example/Pods/Kingfisher/LICENSE b/Example/Pods/Kingfisher/LICENSE new file mode 100644 index 0000000..49ec5dd --- /dev/null +++ b/Example/Pods/Kingfisher/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Wei Wang + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/Example/Pods/Kingfisher/README.md b/Example/Pods/Kingfisher/README.md new file mode 100644 index 0000000..23c3a53 --- /dev/null +++ b/Example/Pods/Kingfisher/README.md @@ -0,0 +1,377 @@ +

+ +Kingfisher + +

+ +

+ + + + + + + + + + + + + +codebeat + + + + +

+ +Kingfisher is a lightweight and pure Swift implemented library for downloading and caching image from the web. This project is heavily inspired by the popular [SDWebImage](https://github.com/rs/SDWebImage). And it provides you a chance to use pure Swift alternative in your next app. + +## Features + +* Everything in Kingfisher is asynchronous, not only downloading, but also caching. That means you never need to worry about blocking your UI thread. +* Multiple-layer cache. Downloaded images will be cached in both memory and disk. So there is no need to download again, this could boost your app's perceptual speed dramatically. +* Cache management. You can set the max duration or size the cache takes. From this, the cache will be cleaned automatically to prevent taking too many resources. +* Modern framework. Kingfisher uses `NSURLSession` and the latest technology of GCD, which makes it a strong and swift framework. It also provides you easy APIs to use. +* Cancelable processing task. You can cancel the downloading process if it is not needed anymore. +* Prefetching. You can prefetch and cache the images which might soon appear in the page. It will bring your users great experience. +* Independent components. You can use the downloader or caching system separately. Or even create your own cache based on Kingfisher's code. +* Options to decompress the image in background before rendering it, which could improve the UI performance. +* Categories over `UIImageView`, `NSImage` and `UIButton` for setting image from a URL directly. Use the same code across all Apple platforms. +* Support GIF seamlessly. You could just download and set your GIF images as the same as you do for PNG/JPEG format using `AnimatedImageView`. +* You could set `Activity Indicator` for your UIImageView or NSImageView to enable the indicator during loading image from web. + +## Requirements + +* iOS 8.0+, tvOS 9.0+, watchOS 2.0+ or OS X 10.10+ +* Xcode 7.3 or above + +If you are upgrading to Kingfisher 2.x from 1.x, please read the [Kingfisher 2.0 Migration Guide](https://github.com/onevcat/Kingfisher/wiki/Kingfisher-2.0-Migration-Guide) for more information. + +Kingfisher is now supporting Swift 2.2. If you need to use Kingfisher in Swift 2.1, you need to pin the version to 2.1.0. + +### Swift 3 + +Kingfisher is now supporting Swift 3 in the [swift3](https://github.com/onevcat/Kingfisher/tree/swift3) branch. It is now under development and not be officially released yet. You could specify to that branch if you are working in a Swift 3 project. However, please reconsider if you want to use it in a releasing orientation product, since more breaking change would be applied later. + +## Installation + +### CocoaPods + +[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command: + +``` bash +$ gem install cocoapods +``` + +To integrate Kingfisher into your Xcode project using CocoaPods, specify it in your `Podfile`: + +``` ruby +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +use_frameworks! + +pod 'Kingfisher', '~> 2.4' +``` +If your CocoaPods Version > 1.0.0, you needs add Target name like this: + +```ruby +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +use_frameworks! + +target ‘’ do + pod 'Kingfisher', '~> 2.4' +end +``` +Then, run the following command: + +``` bash +$ pod install +``` + +You should open the `{Project}.xcworkspace` instead of the `{Project}.xcodeproj` after you installed anything from CocoaPods. + +For more information about how to use CocoaPods, I suggest [this tutorial](http://www.raywenderlich.com/64546/introduction-to-cocoapods-2). + +### Carthage + +[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager for Cocoa application. To install the carthage tool, you can use [Homebrew](http://brew.sh). + +``` bash +$ brew update +$ brew install carthage +``` + +To integrate Kingfisher into your Xcode project using Carthage, specify it in your `Cartfile`: + +``` ogdl +github "onevcat/Kingfisher" ~> 2.4 +``` + +Then, run the following command to build the Kingfisher framework: + +``` bash +$ carthage update + +``` + +At last, you need to set up your Xcode project manually to add the Kingfisher framework. + +On your application targets’ “General” settings tab, in the “Linked Frameworks and Libraries” section, drag and drop each framework you want to use from the Carthage/Build folder on disk. + +On your application targets’ “Build Phases” settings tab, click the “+” icon and choose “New Run Script Phase”. Create a Run Script with the following content: + +``` +/usr/local/bin/carthage copy-frameworks +``` + +and add the paths to the frameworks you want to use under “Input Files”: + +``` +$(SRCROOT)/Carthage/Build/iOS/Kingfisher.framework +``` + +For more information about how to use Carthage, please see its [project page](https://github.com/Carthage/Carthage). + +### Manually + +It is not recommended to install the framework manually, but if you prefer not to use either of the aforementioned dependency managers, you can integrate Kingfisher into your project manually. A regular way to use Kingfisher in your project would be using Embedded Framework. + +- Add Kingfisher as a [submodule](http://git-scm.com/docs/git-submodule). In your favorite terminal, `cd` into your top-level project directory, and entering the following command: + +``` bash +$ git submodule add https://github.com/onevcat/Kingfisher.git +``` + +- Open the `Kingfisher` folder, and drag `Kingfisher.xcodeproj` into the file navigator of your app project, under your app project. +- In Xcode, navigate to the target configuration window by clicking on the blue project icon, and selecting the application target under the "Targets" heading in the sidebar. +- In the tab bar at the top of that window, open the "Build Phases" panel. +- Expand the "Target Dependencies" group, and add `Kingfisher.framework`. +- Click on the `+` button at the top left of "Build Phases" panel and select "New Copy Files Phase". Rename this new phase to "Copy Frameworks", set the "Destination" to "Frameworks", and add `Kingfisher.framework` of the platform you need. + +## Usage + +You can find the full API documentation at [CocoaDocs](http://cocoadocs.org/docsets/Kingfisher/). + +### UIImageView and NSImageView category + +Use Kingfisher in your project is as easy as a pie. You can use the `UIImageView` or `NSImageView` category and trust Kingfisher to manage downloading and cache images. + +#### Basic + +In your source files, add the following code: + +``` swift +import Kingfisher + +imageView.kf_setImageWithURL(NSURL(string: "http://your_image_url.png")!) +``` + +In most cases, Kingfisher is used in a reusable cell. Since the downloading process is asynchronous, the earlier image will be remained during the downloading of newer one. The placeholder version of this API could help: + +``` swift +imageView.kf_setImageWithURL(NSURL(string: "http://your_image_url.png")!, placeholderImage: nil) +``` + +By default, `Kingfisher` will use `absoluteString` of the URL as the key for cache. If you need another key instead of URL's `absoluteString`, there is another set of APIs accepting `Resource` as parameter: + +``` swift +let URL = NSURL(string: "http://your_image_url.png")! +let resource = Resource(downloadURL: URL, cacheKey: "your_customized_key") + +imageView.kf_setImageWithResource(resource) +``` + +It will ask Kingfisher's manager to get the image for the "your_customized_key" from memory and disk first. If the manager does not find it, it will try to download the image at the URL, and store it with `cacheKey` ("your_customized_key" here) for next use. + +#### Options + +Kingfisher will search in cache (both memory and disk) first with the URL, if no image found, it will try to download and store the image in the cache. You can change this behavior by passing an option, to let it ignore the cache. + +``` swift +imageView.kf_setImageWithURL(NSURL(string: "your_image_url")!, + placeholderImage: nil, + optionsInfo: [.ForceRefresh]) +``` + +There are also other options to control the cache level, downloading priority, etc. Take some other examples: + +If you need to cache the downloaded image to a customized cache instead of the default one: + +``` swift +let myCache = ImageCache(name: "my_cache") + +imageView.kf_setImageWithURL(NSURL(string: "your_image_url")!, + placeholderImage: nil, + optionsInfo: [.TargetCache(myCache)]) +``` + +This is useful if you want to use a specified cache for some reasons. + +And if you need to fade in the image to image view during 1 second (image transition only works for iOS platform now): + +``` +imageView.kf_setImageWithURL(NSURL(string: "your_image_url")!, + placeholderImage: nil, + optionsInfo: [.Transition(ImageTransition.Fade(1))]) +``` + +You are also free to combine these options to customize the behavior: + +```swift +let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) +let optionInfo: KingfisherOptionsInfo = [ + .ForceRefresh, + .DownloadPriority(0.5), + .CallbackDispatchQueue(queue), + .Transition(ImageTransition.Fade(1)) +] +``` + +For more information about options, please see the `KingfisherOptionsInfo` in the [documentation](http://cocoadocs.org/docsets/Kingfisher/index.html). + +#### Callbacks + +You can get a chance during Kingfisher downloading images and when the process is done: + +``` swift +imageView.kf_setImageWithURL(NSURL(string: "your_image_url")!, + placeholderImage: nil, + optionsInfo: nil, + progressBlock: { (receivedSize, totalSize) -> () in + print("Download Progress: \(receivedSize)/\(totalSize)") + }, + completionHandler: { (image, error, cacheType, imageURL) -> () in + print("Downloaded and set!") + } +) +``` + +#### Cancel Task + +You can `cancel` the task if the images are not needed anymore. +It could be useful when you use Kingfisher to set an image in a cell of table view or collection view, but users scroll the view and the cells disappeared before downloading finishing. + +``` swift +imageView.kf_setImageWithURL(NSURL(string: "http://your_image_url.png")!) + +// The image retrieving will stop. +imageView.kf_cancelDownloadTask() +``` + +If you need more control and want to do some check before cancelling, all `kf_setImageWithURL` methods return a `RetrieveImageTask` object as well. You can also hold and manage it, then call cancel on the task: + +``` swift +let task = imageView.kf_setImageWithURL(NSURL(string: "http://your_image_url.png")!) + +let urlShouldNotBeCancelled: URL = ... + +if task.downloadTask?.URL != urlShouldNotBeCancelled { + task.cancel() +} +``` + +### Downloader & Cache system + +Kingfisher will use the default downloader and cache if you do not specify them by yourself. You can access them by using `KingfisherManager.sharedManager.downloader` and `KingfisherManager.sharedManager.cache`. You can adjust some parameters to meet your demands: + +``` swift +let downloader = KingfisherManager.sharedManager.downloader + +// Download process will timeout after 5 seconds. Default is 15. +downloader.downloadTimeout = 5 + +// requestModifier will be called before image download request made. +downloader.requestModifier = { + (request: NSMutableURLRequest) in + // Do what you need to modify the download request. Maybe add your HTTP basic authentication for example. +} + +// Hosts in trustedHosts will be ignore the received challenge. +// You can add the host of your self-signed site to it to bypass the SSL. +// (Do not do it unless you know what you are doing) +downloader.trustedHosts = Set(["your_self_signed_host"]) +``` + +``` swift +let cache = KingfisherManager.sharedManager.cache + +// Set max disk cache to 50 mb. Default is no limit. +cache.maxDiskCacheSize = 50 * 1024 * 1024 + +// Set max disk cache to duration to 3 days, Default is 1 week. +cache.maxCachePeriodInSecond = 60 * 60 * 24 * 3 + +// Get the disk size taken by the cache. +cache.calculateDiskCacheSizeWithCompletionHandler { (size) -> () in + print("disk size in bytes: \(size)") +} +``` + +The memory cache will be purged whenever the app switched to background or receiving a memory warning. Disk cache will be cleaned when the conditions are met. You can also clear these caches manually: + +``` swift +// Clear memory cache right away. +cache.clearMemoryCache() + +// Clear disk cache. This is an async operation. +cache.clearDiskCache() + +// Clean expired or size exceeded disk cache. This is an async operation. +cache.cleanExpiredDiskCache() +``` + +### Prefetching + +You could prefetch some images and cache them before you display them on the screen. This is useful when you know a list of image resources you know they would probably be shown later. Since the prefetched images are already in the cache system, there is no need to request them again when you really need to display them in a image view. It will boost your UI and bring your users great experience. + +To prefetch some images, you could use the `ImagePrefetcher`: + +```swift +let urls = ["http://example.com/image1.jpg", "http://example.com/image2.jpg"].map { NSURL(string: $0)! } +let prefetcher = ImagePrefetcher(urls: urls, optionsInfo: nil, progressBlock: nil, completionHandler: { + (skippedResources, failedResources, completedResources) -> () in + print("These resources are prefetched: \(completedResources)") +}) +prefetcher.start() +``` + +You can also stop a prefetch whenever you need: + +```swift +prefetcher.stop() +``` + +After prefetching, you could retrieve image or set the image view with other Kingfisher's methods, with the same `ImageCache` object you used for the prefetching. + +### Animated GIF + +You can load animated GIF by replacing `UIImageView` with `AnimatedImageView`, and then using the same API for a regular image view like this: + +```swift +let imageView = AnimatedImageView() +imageView.kf_setImageWithURL(NSURL(string: "your_animated_gif_image_url")!) +``` + +`AnimatedImageView` will only decode some frames of your GIF image to get a smaller memory footprint. You can set the frame count you need to pre-load by setting the `framePreloadCount` property of an `AnimatedImageView` (default is 10). + +You can also load a GIF file by a regular `UIImageView`. However, all frames will be loaded and decoded into memory. It is probably not suitable if you are loading a large GIF image. For most cases, you may want to use `AnimatedImageView`. The GIF support in Kingfisher's `UIImageView` only fits the small files, and now is mostly serving for back compatibility. + +## Future of Kingfisher + +I want to keep Kingfisher slim. This framework will focus on providing a simple solution for image downloading and caching. But that does not mean the framework will not be improved. Kingfisher is far away from perfect, and necessary and useful features will be added later to make it better. + +## About the logo + +The logo of Kingfisher is inspired by [Tangram (七巧板)](http://en.wikipedia.org/wiki/Tangram), a dissection puzzle consisting of seven flat shapes from China. I believe she's a kingfisher bird instead of a swift, but someone insists that she is a pigeon. I guess I should give her a name. Hi, guys, do you have any suggestion? + +## Contact + +Follow and contact me on [Twitter](http://twitter.com/onevcat) or [Sina Weibo](http://weibo.com/onevcat). If you find an issue, just [open a ticket](https://github.com/onevcat/Kingfisher/issues/new) on it. Pull requests are warmly welcome as well. + +## License + +Kingfisher is released under the MIT license. See LICENSE for details. + + diff --git a/Example/Pods/Kingfisher/Sources/AnimatedImageView.swift b/Example/Pods/Kingfisher/Sources/AnimatedImageView.swift new file mode 100755 index 0000000..d05fc2b --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/AnimatedImageView.swift @@ -0,0 +1,330 @@ +// +// AnimatableImageView.swift +// Kingfisher +// +// Created by bl4ckra1sond3tre on 4/22/16. +// +// The AnimatableImageView, AnimatedFrame and Animator is a modified version of +// some classes from kaishin's Gifu project (https://github.com/kaishin/Gifu) +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Reda Lemeden. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// The name and characters used in the demo of this software are property of their +// respective owners. + +import UIKit +import ImageIO + +/// `AnimatedImageView` is a subclass of `UIImageView` for displaying animated image. +public class AnimatedImageView: UIImageView { + + /// Proxy object for prevending a reference cycle between the CADDisplayLink and AnimatedImageView. + class TargetProxy { + private weak var target: AnimatedImageView? + + init(target: AnimatedImageView) { + self.target = target + } + + @objc func onScreenUpdate() { + target?.updateFrame() + } + } + + // MARK: - Public property + /// Whether automatically play the animation when the view become visible. Default is true. + public var autoPlayAnimatedImage = true + + /// The size of the frame cache. + public var framePreloadCount = 10 + + /// Specifies whether the GIF frames should be pre-scaled to save memory. Default is true. + public var needsPrescaling = true + + /// The animation timer's run loop mode. Default is `NSRunLoopCommonModes`. Set this property to `NSDefaultRunLoopMode` will make the animation pause during UIScrollView scrolling. + public var runLoopMode = RunLoopMode.commonModes { + willSet { + if runLoopMode == newValue { + return + } else { + stopAnimating() + displayLink.remove(from: .main, forMode: runLoopMode) + displayLink.add(to: .main, forMode: newValue) + startAnimating() + } + } + } + + // MARK: - Private property + /// `Animator` instance that holds the frames of a specific image in memory. + private var animator: Animator? + + /// A flag to avoid invalidating the displayLink on deinit if it was never created, because displayLink is so lazy. :D + private var isDisplayLinkInitialized: Bool = false + + /// A display link that keeps calling the `updateFrame` method on every screen refresh. + private lazy var displayLink: CADisplayLink = { + self.isDisplayLinkInitialized = true + let displayLink = CADisplayLink(target: TargetProxy(target: self), selector: #selector(TargetProxy.onScreenUpdate)) + displayLink.add(to: .main, forMode: self.runLoopMode) + displayLink.isPaused = true + return displayLink + }() + + // MARK: - Override + override public var image: Image? { + didSet { + if image != oldValue { + reset() + } + setNeedsDisplay() + layer.setNeedsDisplay() + } + } + + deinit { + if isDisplayLinkInitialized { + displayLink.invalidate() + } + } + + override public var isAnimating: Bool { + if isDisplayLinkInitialized { + return !displayLink.isPaused + } else { + return super.isAnimating + } + } + + /// Starts the animation. + override public func startAnimating() { + if self.isAnimating { + return + } else { + displayLink.isPaused = false + } + } + + /// Stops the animation. + override public func stopAnimating() { + super.stopAnimating() + if isDisplayLinkInitialized { + displayLink.isPaused = true + } + } + + override public func display(_ layer: CALayer) { + if let currentFrame = animator?.currentFrame { + layer.contents = currentFrame.cgImage + } else { + layer.contents = image?.cgImage + } + } + + override public func didMoveToWindow() { + super.didMoveToWindow() + didMove() + } + + override public func didMoveToSuperview() { + super.didMoveToSuperview() + didMove() + } + + // This is for back compatibility that using regular UIImageView to show GIF. + override func shouldPreloadAllGIF() -> Bool { + return false + } + + // MARK: - Private method + /// Reset the animator. + private func reset() { + animator = nil + if let imageSource = image?.kf_imageSource?.imageRef { + animator = Animator(imageSource: imageSource, contentMode: contentMode, size: bounds.size, framePreloadCount: framePreloadCount) + animator?.needsPrescaling = needsPrescaling + animator?.prepareFrames() + } + didMove() + } + + private func didMove() { + if autoPlayAnimatedImage && animator != nil { + if let _ = superview, let _ = window { + startAnimating() + } else { + stopAnimating() + } + } + } + + /// Update the current frame with the displayLink duration. + private func updateFrame() { + if animator?.updateCurrentFrame(duration: displayLink.duration) ?? false { + layer.setNeedsDisplay() + } + } +} + +/// Keeps a reference to an `Image` instance and its duration as a GIF frame. +struct AnimatedFrame { + var image: Image? + let duration: TimeInterval + + static let null: AnimatedFrame = AnimatedFrame(image: .none, duration: 0.0) +} + +// MARK: - Animator +class Animator { + // MARK: Private property + fileprivate let size: CGSize + fileprivate let maxFrameCount: Int + fileprivate let imageSource: CGImageSource + + fileprivate var animatedFrames = [AnimatedFrame]() + fileprivate let maxTimeStep: TimeInterval = 1.0 + fileprivate var frameCount = 0 + fileprivate var currentFrameIndex = 0 + fileprivate var currentPreloadIndex = 0 + fileprivate var timeSinceLastFrameChange: TimeInterval = 0.0 + fileprivate var needsPrescaling = true + + /// Loop count of animatd image. + private var loopCount = 0 + + var currentFrame: UIImage? { + return frame(at: currentFrameIndex) + } + + var contentMode = UIViewContentMode.scaleToFill + + /** + Init an animator with image source reference. + + - parameter imageSource: The reference of animated image. + - parameter contentMode: Content mode of AnimatedImageView. + - parameter size: Size of AnimatedImageView. + - parameter framePreloadCount: Frame cache size. + + - returns: The animator object. + */ + init(imageSource source: CGImageSource, contentMode mode: UIViewContentMode, size: CGSize, framePreloadCount count: Int) { + self.imageSource = source + self.contentMode = mode + self.size = size + self.maxFrameCount = count + } + + func frame(at index: Int) -> Image? { + return animatedFrames[index].image + } + + func prepareFrames() { + frameCount = CGImageSourceGetCount(imageSource) + + if let properties = CGImageSourceCopyProperties(imageSource, nil), + let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary, + let loopCount = gifInfo[kCGImagePropertyGIFLoopCount as String] as? Int + { + self.loopCount = loopCount + } + + let frameToProcess = min(frameCount, maxFrameCount) + animatedFrames.reserveCapacity(frameToProcess) + animatedFrames = (0.. AnimatedFrame { + guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, index, nil) else { + return AnimatedFrame.null + } + + let frameDuration = imageSource.kf_GIFProperties(at: index).flatMap { + gifInfo -> Double? in + + let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as Double? + let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as Double? + let duration = unclampedDelayTime ?? delayTime ?? 0.0 + + /** + http://opensource.apple.com/source/WebCore/WebCore-7600.1.25/platform/graphics/cg/ImageSourceCG.cpp + Many annoying ads specify a 0 duration to make an image flash as quickly as + possible. We follow Safari and Firefox's behavior and use a duration of 100 ms + for any frames that specify a duration of <= 10 ms. + See and for more information. + + See also: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser. + */ + return duration > 0.011 ? duration : 0.100 + } + + let image = Image(cgImage: imageRef) + let scaledImage: Image? + + if needsPrescaling { + scaledImage = image.kf_resize(to: size, for: contentMode) + } else { + scaledImage = image + } + + return AnimatedFrame(image: scaledImage, duration: frameDuration ?? 0.0) + } + + /** + Updates the current frame if necessary using the frame timer and the duration of each frame in `animatedFrames`. + */ + func updateCurrentFrame(duration: CFTimeInterval) -> Bool { + timeSinceLastFrameChange += min(maxTimeStep, duration) + guard let frameDuration = animatedFrames[safe: currentFrameIndex]?.duration, frameDuration <= timeSinceLastFrameChange else { + return false + } + + timeSinceLastFrameChange -= frameDuration + let lastFrameIndex = currentFrameIndex + currentFrameIndex += 1 + currentFrameIndex = currentFrameIndex % animatedFrames.count + + if animatedFrames.count < frameCount { + animatedFrames[lastFrameIndex] = prepareFrame(at: currentPreloadIndex) + currentPreloadIndex += 1 + currentPreloadIndex = currentPreloadIndex % frameCount + } + return true + } +} + +extension CGImageSource { + func kf_GIFProperties(at index: Int) -> [String: Double]? { + let properties = CGImageSourceCopyPropertiesAtIndex(self, index, nil) as Dictionary? + return properties?[kCGImagePropertyGIFDictionary] as? [String: Double] + } +} + +extension Array { + subscript(safe index: Int) -> Element? { + return indices ~= index ? self[index] : nil + } +} + +private func pure(_ value: T) -> [T] { + return [value] +} diff --git a/Example/Pods/Kingfisher/Sources/Filter.swift b/Example/Pods/Kingfisher/Sources/Filter.swift new file mode 100644 index 0000000..6a0bf16 --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/Filter.swift @@ -0,0 +1,128 @@ +// +// Filter.swift +// Kingfisher +// +// Created by Wei Wang on 2016/08/31. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + + +import CoreImage +import Accelerate + +// Reuse the same CI Context for all CI drawing. +private let ciContext = CIContext(options: nil) + +/// Transformer method which will be used in to provide a `Filter`. +public typealias Transformer = (CIImage) -> CIImage? + +/// Supply a filter to create an `ImageProcessor`. +public protocol CIImageProcessor: ImageProcessor { + var filter: Filter { get } +} + +extension CIImageProcessor { + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + return image.kf_apply(filter) + case .data(let data): + return Image.kf_image(data: data, scale: options.scaleFactor, preloadAllGIFData: options.preloadAllGIFData) + } + } +} + +/// Wrapper for a `Transformer` of CIImage filters. +public struct Filter { + + let transform: Transformer + + public init(tranform: @escaping Transformer) { + self.transform = tranform + } + + /// Tint filter which will apply a tint color to images. + public static var tint: (Color) -> Filter = { + color in + Filter { input in + let colorFilter = CIFilter(name: "CIConstantColorGenerator")! + colorFilter.setValue(CIColor(color: color), forKey: kCIInputColorKey) + + let colorImage = colorFilter.outputImage + let filter = CIFilter(name: "CISourceOverCompositing")! + filter.setValue(colorImage, forKey: kCIInputImageKey) + filter.setValue(input, forKey: kCIInputBackgroundImageKey) + return filter.outputImage?.cropping(to: input.extent) + } + } + + public typealias ColorElement = (CGFloat, CGFloat, CGFloat, CGFloat) + + /// Color control filter which will apply color control change to images. + public static var colorControl: (ColorElement) -> Filter = { + brightness, contrast, saturation, inputEV in + Filter { input in + let paramsColor = [kCIInputBrightnessKey: brightness, + kCIInputContrastKey: contrast, + kCIInputSaturationKey: saturation] + + let blackAndWhite = input.applyingFilter("CIColorControls", withInputParameters: paramsColor) + let paramsExposure = [kCIInputEVKey: inputEV] + return blackAndWhite.applyingFilter("CIExposureAdjust", withInputParameters: paramsExposure) + } + + } +} + +public extension Image { + + /// Apply a `Filter` containing `CIImage` transformer to `self`. + /// + /// - parameter filter: The filter used to transform `self`. + /// + /// - returns: A transformed image by input `Filter`. + /// + /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned. + public func kf_apply(_ filter: Filter) -> Image { + + guard let cgImage = cgImage else { + assertionFailure("[Kingfisher] Tint image only works for CG-based image.") + return self + } + + let inputImage = CIImage(cgImage: cgImage) + guard let outputImage = filter.transform(inputImage) else { + return self + } + + guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else { + assertionFailure("[Kingfisher] Can not make an tint image within context.") + return self + } + + #if os(macOS) + return kf_fixedForRetinaPixel(cgImage: result, to: kf_size) + #else + return Image(cgImage: result) + #endif + } +} diff --git a/Example/Pods/Kingfisher/Sources/Image.swift b/Example/Pods/Kingfisher/Sources/Image.swift new file mode 100755 index 0000000..306301e --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/Image.swift @@ -0,0 +1,777 @@ +// +// Image.swift +// Kingfisher +// +// Created by Wei Wang on 16/1/6. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#if os(macOS) +import AppKit +public typealias Image = NSImage +public typealias Color = NSColor + +private var imagesKey: Void? +private var durationKey: Void? +#else +import UIKit +import MobileCoreServices +public typealias Image = UIImage +public typealias Color = UIColor + +private var imageSourceKey: Void? +private var animatedImageDataKey: Void? +#endif + +import ImageIO +import CoreGraphics + +#if !os(watchOS) +import Accelerate +import CoreImage +#endif + +// MARK: - Image Properties +extension Image { +#if os(macOS) + var cgImage: CGImage? { + return cgImage(forProposedRect: nil, context: nil, hints: nil) + } + + var kf_scale: CGFloat { + return 1.0 + } + + fileprivate(set) var kf_images: [Image]? { + get { + return objc_getAssociatedObject(self, &imagesKey) as? [Image] + } + set { + objc_setAssociatedObject(self, &imagesKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + fileprivate(set) var kf_duration: TimeInterval { + get { + return objc_getAssociatedObject(self, &durationKey) as? TimeInterval ?? 0.0 + } + set { + objc_setAssociatedObject(self, &durationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + var kf_size: CGSize { + return representations.reduce(CGSize.zero, { size, rep in + return CGSize(width: max(size.width, CGFloat(rep.pixelsWide)), height: max(size.height, CGFloat(rep.pixelsHigh))) + }) + } + +#else + var kf_scale: CGFloat { + return scale + } + + var kf_images: [Image]? { + return images + } + + var kf_duration: TimeInterval { + return duration + } + + fileprivate(set) var kf_imageSource: ImageSource? { + get { + return objc_getAssociatedObject(self, &imageSourceKey) as? ImageSource + } + set { + objc_setAssociatedObject(self, &imageSourceKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + fileprivate(set) var kf_animatedImageData: Data? { + get { + return objc_getAssociatedObject(self, &animatedImageDataKey) as? Data + } + set { + objc_setAssociatedObject(self, &animatedImageDataKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + var kf_size: CGSize { + return size + } +#endif +} + +// MARK: - Image Conversion +extension Image { +#if os(macOS) + static func kf_image(cgImage: CGImage, scale: CGFloat, refImage: Image?) -> Image { + return Image(cgImage: cgImage, size: CGSize.zero) + } + + /** + Normalize the image. This method does nothing in OS X. + + - returns: The image itself. + */ + public func kf_normalized() -> Image { + return self + } + + static func kf_animatedImage(images: [Image], duration: TimeInterval) -> Image? { + return nil + } +#else + static func kf_image(cgImage: CGImage, scale: CGFloat, refImage: Image?) -> Image { + if let refImage = refImage { + return Image(cgImage: cgImage, scale: scale, orientation: refImage.imageOrientation) + } else { + return Image(cgImage: cgImage, scale: scale, orientation: .up) + } + } + + /** + Normalize the image. This method will try to redraw an image with orientation and scale considered. + + - returns: The normalized image with orientation set to up and correct scale. + */ + public func kf_normalized() -> Image { + // prevent animated image (GIF) lose it's images + guard images == nil else { return self } + // No need to do anything if already up + guard imageOrientation != .up else { return self } + + return draw(cgImage: nil, to: size) { + draw(in: CGRect(origin: CGPoint.zero, size: size)) + } + } + + static func kf_animated(with images: [Image], forDuration duration: TimeInterval) -> Image? { + return .animatedImage(with: images, duration: duration) + } +#endif +} + +// MARK: - Image Representation +extension Image { + // MARK: - PNG + func pngRepresentation() -> Data? { + #if os(macOS) + guard let cgimage = cgImage else { + return nil + } + let rep = NSBitmapImageRep(cgImage: cgimage) + return rep.representation(using: .PNG, properties: [:]) + #else + return UIImagePNGRepresentation(self) + #endif + } + + // MARK: - JPEG + func jpegRepresentation(compressionQuality: CGFloat) -> Data? { + #if os(macOS) + guard let cgImage = cgImage else { + return nil + } + let rep = NSBitmapImageRep(cgImage: cgImage) + return rep.representation(using:.JPEG, properties: [NSImageCompressionFactor: compressionQuality]) + #else + return UIImageJPEGRepresentation(self, compressionQuality) + #endif + } + + // MARK: - GIF + func gifRepresentation() -> Data? { + #if os(macOS) + return gifRepresentation(duration: 0.0, repeatCount: 0) + #else + return kf_animatedImageData + #endif + } + + #if os(macOS) + func gifRepresentation(duration: TimeInterval, repeatCount: Int) -> Data? { + guard let images = kf_images else { + return nil + } + + let frameCount = images.count + let gifDuration = duration <= 0.0 ? kf_duration / Double(frameCount) : duration / Double(frameCount) + + let frameProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: gifDuration]] + let imageProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: repeatCount]] + + let data = NSMutableData() + + guard let destination = CGImageDestinationCreateWithData(data, kUTTypeGIF, frameCount, nil) else { + return nil + } + CGImageDestinationSetProperties(destination, imageProperties as CFDictionary) + + for image in images { + CGImageDestinationAddImage(destination, image.cgImage!, frameProperties as CFDictionary) + } + + return CGImageDestinationFinalize(destination) ? data.copy() as? Data : nil + } + #endif +} + +// MARK: - Create images from data +extension Image { + + static func kf_animated(with data: Data, scale: CGFloat = 1.0, duration: TimeInterval = 0.0, preloadAll: Bool) -> Image? { + + func decode(from imageSource: CGImageSource, for options: NSDictionary) -> ([Image], TimeInterval)? { + + //Calculates frame duration for a gif frame out of the kCGImagePropertyGIFDictionary dictionary + func frameDuration(from gifInfo: NSDictionary) -> Double { + let gifDefaultFrameDuration = 0.100 + + let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as? NSNumber + let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as? NSNumber + let duration = unclampedDelayTime ?? delayTime + + guard let frameDuration = duration else { return gifDefaultFrameDuration } + + return frameDuration.doubleValue > 0.011 ? frameDuration.doubleValue : gifDefaultFrameDuration + } + + let frameCount = CGImageSourceGetCount(imageSource) + var images = [Image]() + var gifDuration = 0.0 + for i in 0 ..< frameCount { + + guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, options) else { + return nil + } + + if frameCount == 1 { + // Single frame + gifDuration = Double.infinity + } else { + // Animated GIF + guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil), + let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary else + { + return nil + } + gifDuration += frameDuration(from: gifInfo) + } + + images.append(Image.kf_image(cgImage: imageRef, scale: scale, refImage: nil)) + } + + return (images, gifDuration) + } + + // Start of kf_animatedImageWithGIFData + let options: NSDictionary = [kCGImageSourceShouldCache as String: true, kCGImageSourceTypeIdentifierHint as String: kUTTypeGIF] + guard let imageSource = CGImageSourceCreateWithData(data as CFData, options) else { + return nil + } + +#if os(macOS) + guard let (images, gifDuration) = decode(from: imageSource, for: options) else { + return nil + } + let image = Image(data: data) + image?.kf_images = images + image?.kf_duration = gifDuration + + return image +#else + + if preloadAll { + guard let (images, gifDuration) = decode(from: imageSource, for: options) else { + return nil + } + let image = Image.kf_animated(with: images, forDuration: duration <= 0.0 ? gifDuration : duration) + image?.kf_animatedImageData = data + return image + } else { + let image = Image(data: data) + image?.kf_animatedImageData = data + image?.kf_imageSource = ImageSource(ref: imageSource) + return image + } +#endif + } + + static func kf_image(data: Data, scale: CGFloat, preloadAllGIFData: Bool) -> Image? { + var image: Image? + #if os(macOS) + switch data.kf_imageFormat { + case .JPEG: image = Image(data: data) + case .PNG: image = Image(data: data) + case .GIF: image = Image.kf_animated(with: data, scale: scale, duration: 0.0, preloadAll: preloadAllGIFData) + case .unknown: image = Image(data: data) + } + #else + switch data.kf_imageFormat { + case .JPEG: image = Image(data: data, scale: scale) + case .PNG: image = Image(data: data, scale: scale) + case .GIF: image = Image.kf_animated(with: data, scale: scale, duration: 0.0, preloadAll: preloadAllGIFData) + case .unknown: image = Image(data: data, scale: scale) + } + #endif + + return image + } +} + +// MARK: - Image Transforming +extension Image { + // MARK: - Round Corner + public func kf_image(withRoundRadius radius: CGFloat, fit size: CGSize, scale: CGFloat) -> Image { + + guard let cgImage = cgImage else { + assertionFailure("[Kingfisher] Round corder image only works for CG-based image.") + return self + } + + let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size) + return draw(cgImage: cgImage, to: size) { + #if os(macOS) + let path = NSBezierPath(roundedRect: rect, xRadius: radius, yRadius: radius) + path.windingRule = .evenOddWindingRule + path.addClip() + draw(in: rect) + #else + guard let context = UIGraphicsGetCurrentContext() else { + assertionFailure("[Kingfisher] Failed to create CG context for image.") + return + } + let path = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: radius, height: radius)).cgPath + context.addPath(path) + context.clip() + draw(in: rect) + #endif + } + } + + #if os(iOS) || os(tvOS) + func kf_resize(to size: CGSize, for contentMode: UIViewContentMode) -> Image { + switch contentMode { + case .scaleAspectFit: + let newSize = self.size.kf_constrained(size) + return kf_resize(to: newSize) + case .scaleAspectFill: + let newSize = self.size.kf_filling(size) + return kf_resize(to: newSize) + default: + return kf_resize(to: size) + } + } + #endif + + // MARK: - Resize + public func kf_resize(to size: CGSize) -> Image { + + guard let cgImage = cgImage?.fixed else { + assertionFailure("[Kingfisher] Resize only works for CG-based image.") + return self + } + + let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size) + return draw(cgImage: cgImage, to: size) { + #if os(macOS) + draw(in: rect, from: NSRect.zero, operation: .copy, fraction: 1.0) + #else + draw(in: rect) + #endif + } + } + + // MARK: - Blur + public func kf_blurred(withRadius radius: CGFloat) -> Image { + #if os(watchOS) + return self + #else + guard let cgImage = cgImage else { + assertionFailure("[Kingfisher] Blur only works for CG-based image.") + return self + } + + // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement + // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5) + // if d is odd, use three box-blurs of size 'd', centered on the output pixel. + let s = max(radius, 2.0) + // We will do blur on a resized image (*0.5), so the blur radius could be half as well. + var targetRadius = floor((Double(s * 3.0) * sqrt(2 * M_PI) / 4.0 + 0.5)) + + if targetRadius.isEven { + targetRadius += 1 + } + + let iterations: Int + if radius < 0.5 { + iterations = 1 + } else if radius < 1.5 { + iterations = 2 + } else { + iterations = 3 + } + + let w = Int(kf_size.width) + let h = Int(kf_size.height) + let rowBytes = Int(CGFloat(cgImage.bytesPerRow)) + + let inDataPointer = UnsafeMutablePointer.allocate(capacity: rowBytes * Int(h)) + inDataPointer.initialize(to: 0) + defer { + inDataPointer.deinitialize() + inDataPointer.deallocate(capacity: rowBytes * Int(h)) + } + + let bitmapInfo = cgImage.bitmapInfo.fixed + guard let context = CGContext(data: inDataPointer, + width: w, + height: h, + bitsPerComponent: cgImage.bitsPerComponent, + bytesPerRow: rowBytes, + space: cgImage.colorSpace ?? CGColorSpaceCreateDeviceRGB(), + bitmapInfo: bitmapInfo.rawValue) else + { + assertionFailure("[Kingfisher] Failed to create CG context for blurring image.") + return self + } + + context.draw(cgImage, in: CGRect(x: 0, y: 0, width: w, height: h)) + + + var inBuffer = vImage_Buffer(data: inDataPointer, height: vImagePixelCount(h), width: vImagePixelCount(w), rowBytes: rowBytes) + + let outDataPointer = UnsafeMutablePointer.allocate(capacity: rowBytes * Int(h)) + outDataPointer.initialize(to: 0) + defer { + outDataPointer.deinitialize() + outDataPointer.deallocate(capacity: rowBytes * Int(h)) + } + + var outBuffer = vImage_Buffer(data: outDataPointer, height: vImagePixelCount(h), width: vImagePixelCount(w), rowBytes: rowBytes) + + for _ in 0 ..< iterations { + vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, nil, 0, 0, UInt32(targetRadius), UInt32(targetRadius), nil, vImage_Flags(kvImageEdgeExtend)) + (inBuffer, outBuffer) = (outBuffer, inBuffer) + } + + guard let outContext = CGContext(data: inDataPointer, + width: w, + height: h, + bitsPerComponent: cgImage.bitsPerComponent, + bytesPerRow: rowBytes, + space: cgImage.colorSpace ?? CGColorSpaceCreateDeviceRGB(), + bitmapInfo: bitmapInfo.rawValue) else + { + assertionFailure("[Kingfisher] Failed to create CG context for blurring image.") + return self + } + + #if os(macOS) + let result = outContext.makeImage().flatMap { kf_fixedForRetinaPixel(cgImage: $0, to: kf_size) } + #else + let result = outContext.makeImage().flatMap { Image(cgImage: $0) } + #endif + guard let blurredImage = result else { + assertionFailure("[Kingfisher] Can not make an blurred image within this context.") + return self + } + + return blurredImage + #endif + } + + // MARK: - Overlay + public func kf_overlaying(with color: Color, fraction: CGFloat) -> Image { + + guard let cgImage = cgImage?.fixed else { + assertionFailure("[Kingfisher] Overlaying only works for CG-based image.") + return self + } + + let rect = CGRect(x: 0, y: 0, width: kf_size.width, height: kf_size.height) + return draw(cgImage: cgImage, to: rect.size) { + #if os(macOS) + draw(in: rect) + if fraction > 0 { + color.withAlphaComponent(1 - fraction).set() + NSRectFillUsingOperation(rect, .sourceAtop) + } + #else + color.set() + UIRectFill(rect) + draw(in: rect, blendMode: .destinationIn, alpha: 1.0) + + if fraction > 0 { + draw(in: rect, blendMode: .sourceAtop, alpha: fraction) + } + #endif + } + } + + // MARK: - Tint + public func kf_tinted(with color: Color) -> Image { + #if os(watchOS) + return self + #else + return kf_apply(.tint(color)) + #endif + } + + // MARK: - Color Control + public func kf_adjusted(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) -> Image { + #if os(watchOS) + return self + #else + return kf_apply(.colorControl(brightness, contrast, saturation, inputEV)) + #endif + } +} + +// MARK: - Decode +extension Image { + func kf_decoded() -> Image? { + return self.kf_decoded(scale: kf_scale) + } + + func kf_decoded(scale: CGFloat) -> Image { + // prevent animated image (GIF) lose it's images +#if os(iOS) + if kf_imageSource != nil { return self } +#else + if kf_images != nil { return self } +#endif + + guard let imageRef = self.cgImage else { + assertionFailure("[Kingfisher] Decoding only works for CG-based image.") + return self + } + let colorSpace = CGColorSpaceCreateDeviceRGB() + let bitmapInfo = imageRef.bitmapInfo.fixed + + guard let context = CGContext(data: nil, width: imageRef.width, height: imageRef.height, bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) else { + assertionFailure("[Kingfisher] Decoding fails to create a valid context.") + return self + } + + let rect = CGRect(x: 0, y: 0, width: imageRef.width, height: imageRef.height) + context.draw(imageRef, in: rect) + let decompressedImageRef = context.makeImage() + return Image.kf_image(cgImage: decompressedImageRef!, scale: scale, refImage: self) + } +} + +/// Reference the source image reference +class ImageSource { + var imageRef: CGImageSource? + init(ref: CGImageSource) { + self.imageRef = ref + } +} + +// MARK: - Image format +private struct ImageHeaderData { + static var PNG: [UInt8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A] + static var JPEG_SOI: [UInt8] = [0xFF, 0xD8] + static var JPEG_IF: [UInt8] = [0xFF] + static var GIF: [UInt8] = [0x47, 0x49, 0x46] +} + +enum ImageFormat { + case unknown, PNG, JPEG, GIF +} + + +// MARK: - Misc Helpers +extension Data { + var kf_imageFormat: ImageFormat { + var buffer = [UInt8](repeating: 0, count: 8) + (self as NSData).getBytes(&buffer, length: 8) + if buffer == ImageHeaderData.PNG { + return .PNG + } else if buffer[0] == ImageHeaderData.JPEG_SOI[0] && + buffer[1] == ImageHeaderData.JPEG_SOI[1] && + buffer[2] == ImageHeaderData.JPEG_IF[0] + { + return .JPEG + } else if buffer[0] == ImageHeaderData.GIF[0] && + buffer[1] == ImageHeaderData.GIF[1] && + buffer[2] == ImageHeaderData.GIF[2] + { + return .GIF + } + + return .unknown + } +} + +extension CGSize { + func kf_constrained(_ size: CGSize) -> CGSize { + let aspectWidth = round(kf_aspectRatio * size.height) + let aspectHeight = round(size.width / kf_aspectRatio) + + return aspectWidth > size.width ? CGSize(width: size.width, height: aspectHeight) : CGSize(width: aspectWidth, height: size.height) + } + + func kf_filling(_ size: CGSize) -> CGSize { + let aspectWidth = round(kf_aspectRatio * size.height) + let aspectHeight = round(size.width / kf_aspectRatio) + + return aspectWidth < size.width ? CGSize(width: size.width, height: aspectHeight) : CGSize(width: aspectWidth, height: size.height) + } + + private var kf_aspectRatio: CGFloat { + return height == 0.0 ? 1.0 : width / height + } +} + +extension CGImage { + var isARGB8888: Bool { + return bitsPerPixel == 32 && bitsPerComponent == 8 && bitmapInfo.contains(.alphaInfoMask) + } + + var fixed: CGImage { + if isARGB8888 { return self } + + // Convert to ARGB if it isn't + guard let context = CGContext.createARGBContext(from: self) else { + assertionFailure("[Kingfisher] Failed to create CG context when converting non ARGB image.") + return self + } + context.draw(self, in: CGRect(x: 0, y: 0, width: width, height: height)) + guard let r = context.makeImage() else { + assertionFailure("[Kingfisher] Failed to create CG image when converting non ARGB image.") + return self + } + return r + } +} + +extension CGBitmapInfo { + var fixed: CGBitmapInfo { + var fixed = self + let alpha = (rawValue & CGBitmapInfo.alphaInfoMask.rawValue) + if alpha == CGImageAlphaInfo.none.rawValue { + fixed.remove(.alphaInfoMask) + fixed = CGBitmapInfo(rawValue: fixed.rawValue | CGImageAlphaInfo.noneSkipFirst.rawValue) + } else if !(alpha == CGImageAlphaInfo.noneSkipFirst.rawValue) || !(alpha == CGImageAlphaInfo.noneSkipLast.rawValue) { + fixed.remove(.alphaInfoMask) + fixed = CGBitmapInfo(rawValue: fixed.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue) + } + return fixed + } +} + + +extension Image { + + func draw(cgImage: CGImage?, to size: CGSize, draw: ()->()) -> Image { + #if os(macOS) + guard let rep = NSBitmapImageRep( + bitmapDataPlanes: nil, + pixelsWide: Int(size.width), + pixelsHigh: Int(size.height), + bitsPerSample: cgImage?.bitsPerComponent ?? 8, + samplesPerPixel: 4, + hasAlpha: true, + isPlanar: false, + colorSpaceName: NSCalibratedRGBColorSpace, + bytesPerRow: 0, + bitsPerPixel: 0) else + { + assertionFailure("[Kingfisher] Image representation cannot be created.") + return self + } + rep.size = size + + NSGraphicsContext.saveGraphicsState() + + let context = NSGraphicsContext(bitmapImageRep: rep) + NSGraphicsContext.setCurrent(context) + draw() + NSGraphicsContext.restoreGraphicsState() + + let outputImage = Image(size: size) + outputImage.addRepresentation(rep) + return outputImage + #else + + UIGraphicsBeginImageContextWithOptions(size, false, scale) + defer { UIGraphicsEndImageContext() } + draw() + return UIGraphicsGetImageFromCurrentImageContext() ?? self + + #endif + } + + #if os(macOS) + func kf_fixedForRetinaPixel(cgImage: CGImage, to size: CGSize) -> Image { + + let image = Image(cgImage: cgImage, size: self.size) + let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size) + + return draw(cgImage: cgImage, to: kf_size) { + image.draw(in: rect, from: NSRect.zero, operation: .copy, fraction: 1.0) + } + } + #endif +} + + +extension CGContext { + static func createARGBContext(from imageRef: CGImage) -> CGContext? { + + let w = imageRef.width + let h = imageRef.height + let bytesPerRow = w * 4 + let colorSpace = CGColorSpaceCreateDeviceRGB() + + let data = malloc(bytesPerRow * h) + defer { + free(data) + } + + let bitmapInfo = imageRef.bitmapInfo.fixed + + // Create the bitmap context. We want pre-multiplied ARGB, 8-bits + // per component. Regardless of what the source image format is + // (CMYK, Grayscale, and so on) it will be converted over to the format + // specified here. + return CGContext(data: data, + width: w, + height: h, + bitsPerComponent: imageRef.bitsPerComponent, + bytesPerRow: bytesPerRow, + space: colorSpace, + bitmapInfo: bitmapInfo.rawValue) + } +} + +extension Double { + var isEven: Bool { + return truncatingRemainder(dividingBy: 2.0) == 0 + } +} + + diff --git a/Example/Pods/Kingfisher/Sources/ImageCache.swift b/Example/Pods/Kingfisher/Sources/ImageCache.swift new file mode 100755 index 0000000..e499cf3 --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/ImageCache.swift @@ -0,0 +1,682 @@ +// +// ImageCache.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/6. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(macOS) +import AppKit +#else +import UIKit +#endif + +public extension Notification.Name { + /** + This notification will be sent when the disk cache got cleaned either there are cached files expired or the total size exceeding the max allowed size. The manually invoking of `clearDiskCache` method will not trigger this notification. + + The `object` of this notification is the `ImageCache` object which sends the notification. + + A list of removed hashes (files) could be retrieved by accessing the array under `KingfisherDiskCacheCleanedHashKey` key in `userInfo` of the notification object you received. By checking the array, you could know the hash codes of files are removed. + + The main purpose of this notification is supplying a chance to maintain some necessary information on the cached files. See [this wiki](https://github.com/onevcat/Kingfisher/wiki/How-to-implement-ETag-based-304-(Not-Modified)-handling-in-Kingfisher) for a use case on it. + */ + public static var KingfisherDidCleanDiskCache = Notification.Name.init("com.onevcat.Kingfisher.KingfisherDidCleanDiskCache") +} + +/** +Key for array of cleaned hashes in `userInfo` of `KingfisherDidCleanDiskCacheNotification`. +*/ +public let KingfisherDiskCacheCleanedHashKey = "com.onevcat.Kingfisher.cleanedHash" + +/// It represents a task of retrieving image. You can call `cancel` on it to stop the process. +public typealias RetrieveImageDiskTask = DispatchWorkItem + +/** +Cache type of a cached image. + +- None: The image is not cached yet when retrieving it. +- Memory: The image is cached in memory. +- Disk: The image is cached in disk. +*/ +public enum CacheType { + case none, memory, disk +} + +/// `ImageCache` represents both the memory and disk cache system of Kingfisher. +/// While a default image cache object will be used if you prefer the extension methods of Kingfisher, +/// you can create your own cache object and configure it as your need. You could use an `ImageCache` +/// object to manipulate memory and disk cache for Kingfisher. +open class ImageCache { + + //Memory + fileprivate let memoryCache = NSCache() + + /// The largest cache cost of memory cache. The total cost is pixel count of + /// all cached images in memory. + /// Default is unlimited. Memory cache will be purged automatically when a + /// memory warning notification is received. + open var maxMemoryCost: UInt = 0 { + didSet { + self.memoryCache.totalCostLimit = Int(maxMemoryCost) + } + } + + //Disk + fileprivate let ioQueue: DispatchQueue + fileprivate var fileManager: FileManager! + + ///The disk cache location. + open let diskCachePath: String + + /// The longest time duration in second of the cache being stored in disk. + /// Default is 1 week (60 * 60 * 24 * 7 seconds). + open var maxCachePeriodInSecond: TimeInterval = 60 * 60 * 24 * 7 //Cache exists for 1 week + + /// The largest disk size can be taken for the cache. It is the total + /// allocated size of cached files in bytes. + /// Default is no limit. + open var maxDiskCacheSize: UInt = 0 + + fileprivate let processQueue: DispatchQueue + + /// The default cache. + public static let `default` = ImageCache(name: "default") + + /** + Init method. Passing a name for the cache. It represents a cache folder in the memory and disk. + + - parameter name: Name of the cache. It will be used as the memory cache name and the disk cache folder name + appending to the cache path. This value should not be an empty string. + - parameter path: Optional - Location of cache path on disk. If `nil` is passed in (the default value), + the `.cachesDirectory` in of your app will be used. + + - returns: The cache object. + */ + public init(name: String, path: String? = nil) { + + if name.isEmpty { + fatalError("[Kingfisher] You should specify a name for the cache. A cache with empty name is not permitted.") + } + + let cacheName = "com.onevcat.Kingfisher.ImageCache.\(name)" + memoryCache.name = cacheName + + let dstPath = path ?? NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first! + diskCachePath = (dstPath as NSString).appendingPathComponent(cacheName) + + let ioQueueName = "com.onevcat.Kingfisher.ImageCache.ioQueue.\(name)" + ioQueue = DispatchQueue(label: ioQueueName) + + let processQueueName = "com.onevcat.Kingfisher.ImageCache.processQueue.\(name)" + processQueue = DispatchQueue(label: processQueueName, attributes: .concurrent) + + ioQueue.sync { fileManager = FileManager() } + +#if !os(macOS) && !os(watchOS) + NotificationCenter.default.addObserver( + self, selector: #selector(clearMemoryCache), name: .UIApplicationDidReceiveMemoryWarning, object: nil) + NotificationCenter.default.addObserver( + self, selector: #selector(cleanExpiredDiskCache_), name: .UIApplicationWillTerminate, object: nil) + NotificationCenter.default.addObserver( + self, selector: #selector(backgroundCleanExpiredDiskCache), name: .UIApplicationDidEnterBackground, object: nil) +#endif + } + + deinit { + NotificationCenter.default.removeObserver(self) + } +} + +// MARK: - Store & Remove +extension ImageCache { + /** + Store an image to cache. It will be saved to both memory and disk. It is an async operation. + + - parameter image: The image to be stored. + - parameter original: The original data of the image. + Kingfisher will use it to check the format of the image and optimize cache size on disk. + If `nil` is supplied, the image data will be saved as a normalized PNG file. + It is strongly suggested to supply it whenever possible, to get a better performance and disk usage. + - parameter key: Key for the image. + - parameter identifier: The identifier of processor used. If you are using a processor for the image, pass the identifier of + processor to it. + This identifier will be used to generate a corresponding key for the combination of `key` and processor. + - parameter toDisk: Whether this image should be cached to disk or not. If false, the image will be only cached in memory. + - parameter completionHandler: Called when store operation completes. + */ + open func store(_ image: Image, + original: Data? = nil, + forKey key: String, + processorIdentifier identifier: String = "", + cacheSerializer serializer: CacheSerializer = DefaultCacheSerializer.default, + toDisk: Bool = true, + completionHandler: (() -> Void)? = nil) + { + + let computedKey = key.computedKey(with: identifier) + memoryCache.setObject(image, forKey: computedKey as NSString, cost: image.kf_imageCost) + + func callHandlerInMainQueue() { + if let handler = completionHandler { + DispatchQueue.main.async { + handler() + } + } + } + + if toDisk { + ioQueue.async { + + if let data = serializer.data(with: image, original: original) { + if !self.fileManager.fileExists(atPath: self.diskCachePath) { + do { + try self.fileManager.createDirectory(atPath: self.diskCachePath, withIntermediateDirectories: true, attributes: nil) + } catch _ {} + } + + self.fileManager.createFile(atPath: self.cachePath(forComputedKey: computedKey), contents: data, attributes: nil) + } + callHandlerInMainQueue() + } + } else { + callHandlerInMainQueue() + } + } + + /** + Remove the image for key for the cache. It will be opted out from both memory and disk. + It is an async operation. + + - parameter key: Key for the image. + - parameter identifier: The identifier of processor used. If you are using a processor for the image, pass the identifier of processor to it. + This identifier will be used to generate a corresponding key for the combination of `key` and processor. + - parameter fromDisk: Whether this image should be removed from disk or not. If false, the image will be only removed from memory. + - parameter completionHandler: Called when removal operation completes. + */ + open func removeImage(forKey key: String, + processorIdentifier identifier: String = "", + fromDisk: Bool = true, + completionHandler: (() -> Void)? = nil) + { + let computedKey = key.computedKey(with: identifier) + memoryCache.removeObject(forKey: computedKey as NSString) + + func callHandlerInMainQueue() { + if let handler = completionHandler { + DispatchQueue.main.async { + handler() + } + } + } + + if fromDisk { + ioQueue.async{ + do { + try self.fileManager.removeItem(atPath: self.cachePath(forComputedKey: computedKey)) + } catch _ {} + callHandlerInMainQueue() + } + } else { + callHandlerInMainQueue() + } + } + +} + +// MARK: - Get data from cache +extension ImageCache { + /** + Get an image for a key from memory or disk. + + - parameter key: Key for the image. + - parameter options: Options of retrieving image. If you need to retrieve an image which was + stored with a specified `ImageProcessor`, pass the processor in the option too. + - parameter completionHandler: Called when getting operation completes with image result and cached type of + this image. If there is no such key cached, the image will be `nil`. + + - returns: The retrieving task. + */ + @discardableResult public func retrieveImage(forKey key: String, + options: KingfisherOptionsInfo?, + completionHandler: ((Image?, CacheType) -> ())?) -> RetrieveImageDiskTask? + { + // No completion handler. Not start working and early return. + guard let completionHandler = completionHandler else { + return nil + } + + var block: RetrieveImageDiskTask? + let options = options ?? KingfisherEmptyOptionsInfo + + if let image = self.retrieveImageInMemoryCache(forKey: key, options: options) { + options.callbackDispatchQueue.safeAsync { + completionHandler(image, .memory) + } + } else { + var sSelf: ImageCache! = self + block = DispatchWorkItem(block: { + // Begin to load image from disk + if let image = sSelf.retrieveImageInDiskCache(forKey: key, options: options) { + if options.backgroundDecode { + sSelf.processQueue.async { + let result = image.kf_decoded(scale: options.scaleFactor) + + sSelf.store(result, + forKey: key, + processorIdentifier: options.processor.identifier, + cacheSerializer: options.cacheSerializer, + toDisk: false, + completionHandler: nil) + + options.callbackDispatchQueue.safeAsync { + completionHandler(result, .memory) + sSelf = nil + } + } + } else { + sSelf.store(image, + forKey: key, + processorIdentifier: options.processor.identifier, + cacheSerializer: options.cacheSerializer, + toDisk: false, + completionHandler: nil + ) + options.callbackDispatchQueue.safeAsync { + completionHandler(image, .disk) + sSelf = nil + } + } + } else { + // No image found from either memory or disk + options.callbackDispatchQueue.safeAsync { + completionHandler(nil, .none) + sSelf = nil + } + } + }) + + sSelf.ioQueue.async(execute: block!) + } + + return block + } + + /** + Get an image for a key from memory. + + - parameter key: Key for the image. + - parameter options: Options of retrieving image. If you need to retrieve an image which was + stored with a specified `ImageProcessor`, pass the processor in the option too. + - returns: The image object if it is cached, or `nil` if there is no such key in the cache. + */ + open func retrieveImageInMemoryCache(forKey key: String, options: KingfisherOptionsInfo? = nil) -> Image? { + + let options = options ?? KingfisherEmptyOptionsInfo + let computedKey = key.computedKey(with: options.processor.identifier) + + return memoryCache.object(forKey: computedKey as NSString) as? Image + } + + /** + Get an image for a key from disk. + + - parameter key: Key for the image. + - parameter options: Options of retrieving image. If you need to retrieve an image which was + stored with a specified `ImageProcessor`, pass the processor in the option too. + + - returns: The image object if it is cached, or `nil` if there is no such key in the cache. + */ + open func retrieveImageInDiskCache(forKey key: String, options: KingfisherOptionsInfo? = nil) -> Image? { + + let options = options ?? KingfisherEmptyOptionsInfo + let computedKey = key.computedKey(with: options.processor.identifier) + + return diskImage(forComputedKey: computedKey, serializer: options.cacheSerializer, options: options) + } +} + +// MARK: - Clear & Clean +extension ImageCache { + /** + Clear memory cache. + */ + @objc public func clearMemoryCache() { + memoryCache.removeAllObjects() + } + + /** + Clear disk cache. + */ + private func clearDiskCache_() { + clearDiskCache(with: nil) + } + + /** + Clear disk cache. This is an async operation. + + - parameter completionHander: Called after the operation completes. + */ + open func clearDiskCache(with completionHander: (()->())? = nil) { + ioQueue.async { + do { + try self.fileManager.removeItem(atPath: self.diskCachePath) + try self.fileManager.createDirectory(atPath: self.diskCachePath, withIntermediateDirectories: true, attributes: nil) + } catch _ { } + + if let completionHander = completionHander { + DispatchQueue.main.async { + completionHander() + } + } + } + } + + /** + Clean expired disk cache. This is an async operation. + */ + @objc fileprivate func cleanExpiredDiskCache_() { + cleanExpiredDiskCache(with: nil) + } + + /** + Clean expired disk cache. This is an async operation. + + - parameter completionHandler: Called after the operation completes. + */ + open func cleanExpiredDiskCache(with completionHandler: (()->())? = nil) { + + // Do things in cocurrent io queue + ioQueue.async { + + var (URLsToDelete, diskCacheSize, cachedFiles) = self.travelCachedFiles(onlyForCacheSize: false) + + for fileURL in URLsToDelete { + do { + try self.fileManager.removeItem(at: fileURL) + } catch _ { } + } + + if self.maxDiskCacheSize > 0 && diskCacheSize > self.maxDiskCacheSize { + let targetSize = self.maxDiskCacheSize / 2 + + // Sort files by last modify date. We want to clean from the oldest files. + let sortedFiles = cachedFiles.keysSortedByValue { + resourceValue1, resourceValue2 -> Bool in + + if let date1 = resourceValue1.contentAccessDate, + let date2 = resourceValue2.contentAccessDate + { + return date1.compare(date2) == .orderedAscending + } + + // Not valid date information. This should not happen. Just in case. + return true + } + + for fileURL in sortedFiles { + + do { + try self.fileManager.removeItem(at: fileURL) + } catch { } + + URLsToDelete.append(fileURL) + + if let fileSize = cachedFiles[fileURL]?.totalFileAllocatedSize { + diskCacheSize -= UInt(fileSize) + } + + if diskCacheSize < targetSize { + break + } + } + } + + DispatchQueue.main.async { + + if URLsToDelete.count != 0 { + let cleanedHashes = URLsToDelete.map { $0.lastPathComponent } + NotificationCenter.default.post(name: .KingfisherDidCleanDiskCache, object: self, userInfo: [KingfisherDiskCacheCleanedHashKey: cleanedHashes]) + } + + completionHandler?() + } + } + } + + fileprivate func travelCachedFiles(onlyForCacheSize: Bool) -> (urlsToDelete: [URL], diskCacheSize: UInt, cachedFiles: [URL: URLResourceValues]) { + + let diskCacheURL = URL(fileURLWithPath: diskCachePath) + let resourceKeys: Set = [.isDirectoryKey, .contentAccessDateKey, .totalFileAllocatedSizeKey] + let expiredDate = Date(timeIntervalSinceNow: -maxCachePeriodInSecond) + + var cachedFiles = [URL: URLResourceValues]() + var urlsToDelete = [URL]() + var diskCacheSize: UInt = 0 + + if let fileEnumerator = self.fileManager.enumerator(at: diskCacheURL, includingPropertiesForKeys: Array(resourceKeys), options: FileManager.DirectoryEnumerationOptions.skipsHiddenFiles, errorHandler: nil), + let urls = fileEnumerator.allObjects as? [URL] + { + for fileUrl in urls { + + do { + let resourceValues = try fileUrl.resourceValues(forKeys: resourceKeys) + // If it is a Directory. Continue to next file URL. + if resourceValues.isDirectory == true { + continue + } + + if !onlyForCacheSize { + // If this file is expired, add it to URLsToDelete + if let lastAccessData = resourceValues.contentAccessDate { + if (lastAccessData as NSDate).laterDate(expiredDate) == expiredDate { + urlsToDelete.append(fileUrl) + continue + } + } + } + + if let fileSize = resourceValues.totalFileAllocatedSize { + diskCacheSize += UInt(fileSize) + if !onlyForCacheSize { + cachedFiles[fileUrl] = resourceValues + } + } + } catch _ { } + } + } + + return (urlsToDelete, diskCacheSize, cachedFiles) + } + +#if !os(macOS) && !os(watchOS) + /** + Clean expired disk cache when app in background. This is an async operation. + In most cases, you should not call this method explicitly. + It will be called automatically when `UIApplicationDidEnterBackgroundNotification` received. + */ + @objc public func backgroundCleanExpiredDiskCache() { + // if 'sharedApplication()' is unavailable, then return + guard let sharedApplication = UIApplication.kf_shared else { return } + + func endBackgroundTask(_ task: inout UIBackgroundTaskIdentifier) { + sharedApplication.endBackgroundTask(task) + task = UIBackgroundTaskInvalid + } + + var backgroundTask: UIBackgroundTaskIdentifier! + backgroundTask = sharedApplication.beginBackgroundTask { + endBackgroundTask(&backgroundTask!) + } + + cleanExpiredDiskCache { + endBackgroundTask(&backgroundTask!) + } + } +#endif +} + + +// MARK: - Check cache status +extension ImageCache { + + /** + * Cache result for checking whether an image is cached for a key. + */ + public struct CacheCheckResult { + public let cached: Bool + public let cacheType: CacheType? + } + + /** + Check whether an image is cached for a key. + + - parameter key: Key for the image. + + - returns: The check result. + */ + open func isImageCached(forKey key: String, processorIdentifier identifier: String = "") -> CacheCheckResult { + + let computedKey = key.computedKey(with: identifier) + + if memoryCache.object(forKey: computedKey as NSString) != nil { + return CacheCheckResult(cached: true, cacheType: .memory) + } + + let filePath = cachePath(forComputedKey: computedKey) + + var diskCached = false + ioQueue.sync { + diskCached = fileManager.fileExists(atPath: filePath) + } + + if diskCached { + return CacheCheckResult(cached: true, cacheType: .disk) + } + + return CacheCheckResult(cached: false, cacheType: nil) + } + + /** + Get the hash for the key. This could be used for matching files. + + - parameter key: The key which is used for caching. + - parameter identifier: The identifier of processor used. If you are using a processor for the image, pass the identifier of processor to it. + + - returns: Corresponding hash. + */ + open func hash(forKey key: String, processorIdentifier identifier: String = "") -> String { + let computedKey = key.computedKey(with: identifier) + return cacheFileName(forComputedKey: computedKey) + } + + /** + Calculate the disk size taken by cache. + It is the total allocated size of the cached files in bytes. + + - parameter completionHandler: Called with the calculated size when finishes. + */ + open func calculateDiskCacheSize(completionHandler: @escaping ((_ size: UInt) -> ())) { + ioQueue.async { + let (_, diskCacheSize, _) = self.travelCachedFiles(onlyForCacheSize: true) + DispatchQueue.main.async { + completionHandler(diskCacheSize) + } + } + } + + /** + Get the cache path for the key. + It is useful for projects with UIWebView or anyone that needs access to the local file path. + + i.e. Replace the `` tag in your HTML. + + - Note: This method does not guarantee there is an image already cached in the path. It just returns the path + that the image should be. + You could use `isImageCached(forKey:)` method to check whether the image is cached under that key. + */ + open func cachePath(forKey key: String, processorIdentifier identifier: String = "") -> String { + let computedKey = key.computedKey(with: identifier) + return cachePath(forComputedKey: computedKey) + } + + func cachePath(forComputedKey key: String) -> String { + let fileName = cacheFileName(forComputedKey: key) + return (diskCachePath as NSString).appendingPathComponent(fileName) + } +} + +// MARK: - Internal Helper +extension ImageCache { + + func diskImage(forComputedKey key: String, serializer: CacheSerializer, options: KingfisherOptionsInfo) -> Image? { + if let data = diskImageData(forComputedKey: key) { + return serializer.image(with: data, options: options) + } else { + return nil + } + } + + func diskImageData(forComputedKey key: String) -> Data? { + let filePath = cachePath(forComputedKey: key) + return (try? Data(contentsOf: URL(fileURLWithPath: filePath))) + } + + func cacheFileName(forComputedKey key: String) -> String { + return key.kf_MD5 + } +} + +extension Image { + var kf_imageCost: Int { + return kf_images == nil ? + Int(size.height * size.width * kf_scale * kf_scale) : + Int(size.height * size.width * kf_scale * kf_scale) * kf_images!.count + } +} + +extension Dictionary { + func keysSortedByValue(_ isOrderedBefore: (Value, Value) -> Bool) -> [Key] { + return Array(self).sorted{ isOrderedBefore($0.1, $1.1) }.map{ $0.0 } + } +} + +#if !os(macOS) && !os(watchOS) +// MARK: - For App Extensions +extension UIApplication { + public static var kf_shared: UIApplication? { + let selector = NSSelectorFromString("sharedApplication") + guard responds(to: selector) else { return nil } + return perform(selector).takeUnretainedValue() as? UIApplication + } +} +#endif + +extension String { + func computedKey(with identifier: String) -> String { + if identifier.isEmpty { + return self + } else { + return appending("@\(identifier)") + } + } +} diff --git a/Example/Pods/Kingfisher/Sources/ImageDownloader.swift b/Example/Pods/Kingfisher/Sources/ImageDownloader.swift new file mode 100755 index 0000000..9b70be7 --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/ImageDownloader.swift @@ -0,0 +1,527 @@ +// +// ImageDownloader.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/6. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(macOS) +import AppKit +#else +import UIKit +#endif + +/// Progress update block of downloader. +public typealias ImageDownloaderProgressBlock = DownloadProgressBlock + +/// Completion block of downloader. +public typealias ImageDownloaderCompletionHandler = ((_ image: Image?, _ error: NSError?, _ url: URL?, _ originalData: Data?) -> ()) + +/// Download task. +public struct RetrieveImageDownloadTask { + let internalTask: URLSessionDataTask + + /// Downloader by which this task is intialized. + public private(set) weak var ownerDownloader: ImageDownloader? + + /** + Cancel this download task. It will trigger the completion handler with an NSURLErrorCancelled error. + */ + public func cancel() { + ownerDownloader?.cancelDownloadingTask(self) + } + + /// The original request URL of this download task. + public var url: URL? { + return internalTask.originalRequest?.url + } + + /// The relative priority of this download task. + /// It represents the `priority` property of the internal `NSURLSessionTask` of this download task. + /// The value for it is between 0.0~1.0. Default priority is value of 0.5. + /// See documentation on `priority` of `NSURLSessionTask` for more about it. + public var priority: Float { + get { + return internalTask.priority + } + set { + internalTask.priority = newValue + } + } +} + +///The code of errors which `ImageDownloader` might encountered. +public enum KingfisherError: Int { + + /// badData: The downloaded data is not an image or the data is corrupted. + case badData = 10000 + + /// notModified: The remote server responsed a 304 code. No image data downloaded. + case notModified = 10001 + + /// The HTTP status code in response is not valid. If an invalid + /// code error received, you could check the value under `KingfisherErrorStatusCodeKey` + /// in `userInfo` to see the code. + case invalidStatusCode = 10002 + + /// notCached: The image rquested is not in cache but .onlyFromCache is activated. + case notCached = 10003 + + /// The URL is invalid. + case invalidURL = 20000 + + /// The downloading task is cancelled before started. + case downloadCanelledBeforeStarting = 30000 +} + +/// Key will be used in the `userInfo` of `.invalidStatusCode` +public let KingfisherErrorStatusCodeKey = "statusCode" + +/// Protocol of `ImageDownloader`. +public protocol ImageDownloaderDelegate: class { + /** + Called when the `ImageDownloader` object successfully downloaded an image from specified URL. + + - parameter downloader: The `ImageDownloader` object finishes the downloading. + - parameter image: Downloaded image. + - parameter url: URL of the original request URL. + - parameter response: The response object of the downloading process. + */ + func imageDownloader(_ downloader: ImageDownloader, didDownload image: Image, for url: URL, with response: URLResponse?) + + + /** + Check if a received HTTP status code is valid or not. + By default, a status code between 200 to 400 (excluded) is considered as valid. + If an invalid code is received, the downloader will raise an .invalidStatusCode error. + It has a `userInfo` which includes this statusCode and localizedString error message. + + - parameter code: The received HTTP status code. + - parameter downloader: The `ImageDownloader` object asking for validate status code. + + - returns: Whether this HTTP status code is valid or not. + + - Note: If the default 200 to 400 valid code does not suit your need, + you can implement this method to change that behavior. + */ + func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool +} + +extension ImageDownloaderDelegate { + public func imageDownloader(_ downloader: ImageDownloader, didDownload image: Image, for url: URL, with response: URLResponse?) {} + + public func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool { + return (200..<400).contains(code) + } +} + +/// Protocol indicates that an authentication challenge could be handled. +public protocol AuthenticationChallengeResponsable: class { + /** + Called when an session level authentication challenge is received. + This method provide a chance to handle and response to the authentication challenge before downloading could start. + + - parameter downloader: The downloader which receives this challenge. + - parameter challenge: An object that contains the request for authentication. + - parameter completionHandler: A handler that your delegate method must call. + + - Note: This method is a forward from `URLSession(:didReceiveChallenge:completionHandler:)`. Please refer to the document of it in `NSURLSessionDelegate`. + */ + func downloader(_ downloader: ImageDownloader, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) +} + +extension AuthenticationChallengeResponsable { + + func downloader(_ downloader: ImageDownloader, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { + + if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { + if let trustedHosts = downloader.trustedHosts, trustedHosts.contains(challenge.protectionSpace.host) { + let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!) + completionHandler(.useCredential, credential) + return + } + } + + completionHandler(.performDefaultHandling, nil) + } +} + +/// `ImageDownloader` represents a downloading manager for requesting the image with a URL from server. +open class ImageDownloader: NSObject { + + class ImageFetchLoad { + var callbacks = [CallbackPair]() + var responseData = NSMutableData() + + var options: KingfisherOptionsInfo? + + var downloadTaskCount = 0 + var downloadTask: RetrieveImageDownloadTask? + } + + // MARK: - Public property + /// This closure will be applied to the image download request before it being sent. + /// You can modify the request for some customizing purpose, like adding auth token to the header, do basic HTTP auth or something like url mapping. + @available(*, unavailable, message: "`requestModifier` is removed. Use 'urlRequest(for:byModifying:)' from the 'ImageDownloaderDelegate' instead") + open var requestModifier: ((inout URLRequest) -> Void)? + + /// The duration before the download is timeout. Default is 15 seconds. + open var downloadTimeout: TimeInterval = 15.0 + + /// A set of trusted hosts when receiving server trust challenges. A challenge with host name contained in this set will be ignored. + /// You can use this set to specify the self-signed site. It only will be used if you don't specify the `authenticationChallengeResponder`. + /// If `authenticationChallengeResponder` is set, this property will be ignored and the implemention of `authenticationChallengeResponder` will be used instead. + open var trustedHosts: Set? + + /// Use this to set supply a configuration for the downloader. By default, NSURLSessionConfiguration.ephemeralSessionConfiguration() will be used. + /// You could change the configuration before a downloaing task starts. A configuration without persistent storage for caches is requsted for downloader working correctly. + open var sessionConfiguration = URLSessionConfiguration.ephemeral { + didSet { + session = URLSession(configuration: sessionConfiguration, delegate: sessionHandler, delegateQueue: OperationQueue.main) + } + } + + /// Whether the download requests should use pipeling or not. Default is false. + open var requestsUsePipeling = false + + fileprivate let sessionHandler: ImageDownloaderSessionHandler + fileprivate var session: URLSession? + + /// Delegate of this `ImageDownloader` object. See `ImageDownloaderDelegate` protocol for more. + open weak var delegate: ImageDownloaderDelegate? + + /// A responder for authentication challenge. + /// Downloader will forward the received authentication challenge for the downloading session to this responder. + open weak var authenticationChallengeResponder: AuthenticationChallengeResponsable? + + // MARK: - Internal property + let barrierQueue: DispatchQueue + let processQueue: DispatchQueue + + typealias CallbackPair = (progressBlock: ImageDownloaderProgressBlock?, completionHander: ImageDownloaderCompletionHandler?) + + var fetchLoads = [URL: ImageFetchLoad]() + + // MARK: - Public method + /// The default downloader. + public static let `default` = ImageDownloader(name: "default") + + /** + Init a downloader with name. + + - parameter name: The name for the downloader. It should not be empty. + + - returns: The downloader object. + */ + public init(name: String) { + if name.isEmpty { + fatalError("[Kingfisher] You should specify a name for the downloader. A downloader with empty name is not permitted.") + } + + barrierQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Barrier.\(name)", attributes: .concurrent) + processQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Process.\(name)", attributes: .concurrent) + + sessionHandler = ImageDownloaderSessionHandler() + + super.init() + + // Provide a default implement for challenge responder. + authenticationChallengeResponder = sessionHandler + session = URLSession(configuration: sessionConfiguration, delegate: sessionHandler, delegateQueue: .main) + } + + func fetchLoad(for url: URL) -> ImageFetchLoad? { + var fetchLoad: ImageFetchLoad? + barrierQueue.sync { fetchLoad = fetchLoads[url] } + return fetchLoad + } +} + +// MARK: - Download method +extension ImageDownloader { + /** + Download an image with a URL. + + - parameter url: Target URL. + - parameter progressBlock: Called when the download progress updated. + - parameter completionHandler: Called when the download progress finishes. + + - returns: A downloading task. You could call `cancel` on it to stop the downloading process. + */ + @discardableResult + open func downloadImage(with url: URL, + progressBlock: ImageDownloaderProgressBlock?, + completionHandler: ImageDownloaderCompletionHandler?) -> RetrieveImageDownloadTask? + { + return downloadImage(with: url, options: nil, progressBlock: progressBlock, completionHandler: completionHandler) + } + + /** + Download an image with a URL and option. + + - parameter url: Target URL. + - parameter options: The options could control download behavior. See `KingfisherOptionsInfo`. + - parameter progressBlock: Called when the download progress updated. + - parameter completionHandler: Called when the download progress finishes. + + - returns: A downloading task. You could call `cancel` on it to stop the downloading process. + */ + @discardableResult + open func downloadImage(with url: URL, + options: KingfisherOptionsInfo?, + progressBlock: ImageDownloaderProgressBlock?, + completionHandler: ImageDownloaderCompletionHandler?) -> RetrieveImageDownloadTask? + { + return downloadImage(with: url, + retrieveImageTask: nil, + options: options, + progressBlock: progressBlock, + completionHandler: completionHandler) + } + + func downloadImage(with url: URL, + retrieveImageTask: RetrieveImageTask?, + options: KingfisherOptionsInfo?, + progressBlock: ImageDownloaderProgressBlock?, + completionHandler: ImageDownloaderCompletionHandler?) -> RetrieveImageDownloadTask? + { + if let retrieveImageTask = retrieveImageTask, retrieveImageTask.cancelledBeforeDownloadStarting { + return nil + } + + let timeout = self.downloadTimeout == 0.0 ? 15.0 : self.downloadTimeout + + // We need to set the URL as the load key. So before setup progress, we need to ask the `requestModifier` for a final URL. + var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: timeout) + request.httpShouldUsePipelining = requestsUsePipeling + + if let modifier = options?.modifier { + guard let r = modifier.modified(for: request) else { + completionHandler?(nil, NSError(domain: KingfisherErrorDomain, code: KingfisherError.downloadCanelledBeforeStarting.rawValue, userInfo: nil), nil, nil) + return nil + } + request = r + } + + // There is a possiblility that request modifier changed the url to `nil` or empty. + guard let url = request.url, !url.absoluteString.isEmpty else { + completionHandler?(nil, NSError(domain: KingfisherErrorDomain, code: KingfisherError.invalidURL.rawValue, userInfo: nil), nil, nil) + return nil + } + + var downloadTask: RetrieveImageDownloadTask? + setup(progressBlock: progressBlock, with: completionHandler, for: url) {(session, fetchLoad) -> Void in + if fetchLoad.downloadTask == nil { + let dataTask = session.dataTask(with: request) + + fetchLoad.downloadTask = RetrieveImageDownloadTask(internalTask: dataTask, ownerDownloader: self) + fetchLoad.options = options + + dataTask.priority = options?.downloadPriority ?? URLSessionTask.defaultPriority + dataTask.resume() + + // Hold self while the task is executing. + self.sessionHandler.downloadHolder = self + } + + fetchLoad.downloadTaskCount += 1 + downloadTask = fetchLoad.downloadTask + + retrieveImageTask?.downloadTask = downloadTask + } + return downloadTask + } + + // A single key may have multiple callbacks. Only download once. + func setup(progressBlock: ImageDownloaderProgressBlock?, with completionHandler: ImageDownloaderCompletionHandler?, for url: URL, started: ((URLSession, ImageFetchLoad) -> Void)) { + + barrierQueue.sync(flags: .barrier) { + let loadObjectForURL = fetchLoads[url] ?? ImageFetchLoad() + let callbackPair = (progressBlock: progressBlock, completionHander: completionHandler) + + loadObjectForURL.callbacks.append(callbackPair) + fetchLoads[url] = loadObjectForURL + + if let session = session { + started(session, loadObjectForURL) + } + } + } + + func cancelDownloadingTask(_ task: RetrieveImageDownloadTask) { + barrierQueue.sync { + if let URL = task.internalTask.originalRequest?.url, let imageFetchLoad = self.fetchLoads[URL] { + imageFetchLoad.downloadTaskCount -= 1 + if imageFetchLoad.downloadTaskCount == 0 { + task.internalTask.cancel() + } + } + } + } + + func clean(for url: URL) { + barrierQueue.sync(flags: .barrier) { + fetchLoads.removeValue(forKey: url) + return + } + } +} + +// MARK: - NSURLSessionDataDelegate + +/// Delegate class for `NSURLSessionTaskDelegate`. +/// The session object will hold its delegate until it gets invalidated. +/// If we use `ImageDownloader` as the session delegate, it will not be released. +/// So we need an additional handler to break the retain cycle. +// See https://github.com/onevcat/Kingfisher/issues/235 +class ImageDownloaderSessionHandler: NSObject, URLSessionDataDelegate, AuthenticationChallengeResponsable { + + // The holder will keep downloader not released while a data task is being executed. + // It will be set when the task started, and reset when the task finished. + var downloadHolder: ImageDownloader? + + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { + + guard let downloader = downloadHolder else { + completionHandler(.cancel) + return + } + + if let statusCode = (response as? HTTPURLResponse)?.statusCode, + let url = dataTask.originalRequest?.url, + !(downloader.delegate ?? downloader).isValidStatusCode(statusCode, for: downloader) + { + callback(with: nil, error: NSError(domain: KingfisherErrorDomain, code: KingfisherError.invalidStatusCode.rawValue, userInfo: [KingfisherErrorStatusCodeKey: statusCode, NSLocalizedDescriptionKey: HTTPURLResponse.localizedString(forStatusCode: statusCode)]), url: url, originalData: nil) + } + + completionHandler(.allow) + } + + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + + guard let downloader = downloadHolder else { + return + } + + if let url = dataTask.originalRequest?.url, let fetchLoad = downloader.fetchLoad(for: url) { + fetchLoad.responseData.append(data) + + if let expectedLength = dataTask.response?.expectedContentLength { + for callbackPair in fetchLoad.callbacks { + DispatchQueue.main.async { + callbackPair.progressBlock?(Int64(fetchLoad.responseData.length), expectedLength) + } + } + } + } + } + + + + func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + + if let url = task.originalRequest?.url { + if let error = error { // Error happened + callback(with: nil, error: error as NSError, url: url, originalData: nil) + } else { //Download finished without error + processImage(for: task, url: url) + } + } + } + + /** + This method is exposed since the compiler requests. Do not call it. + */ + func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { + guard let downloader = downloadHolder else { + return + } + + downloader.authenticationChallengeResponder?.downloader(downloader, didReceive: challenge, completionHandler: completionHandler) + } + + private func callback(with image: Image?, error: NSError?, url: URL, originalData: Data?) { + + guard let downloader = downloadHolder else { + return + } + + if let callbackPairs = downloader.fetchLoad(for: url)?.callbacks { + let options = downloader.fetchLoad(for: url)?.options ?? KingfisherEmptyOptionsInfo + + downloader.clean(for: url) + + for callbackPair in callbackPairs { + options.callbackDispatchQueue.safeAsync { + callbackPair.completionHander?(image, error, url, originalData) + } + } + + if downloader.fetchLoads.isEmpty { + downloadHolder = nil + } + } + } + + private func processImage(for task: URLSessionTask, url: URL) { + + guard let downloader = downloadHolder else { + return + } + + // We are on main queue when receiving this. + downloader.processQueue.async { + + guard let fetchLoad = downloader.fetchLoad(for: url) else { + self.callback(with: nil, error: NSError(domain: KingfisherErrorDomain, code: KingfisherError.badData.rawValue, userInfo: nil), url: url, originalData: nil) + return + } + + let options = fetchLoad.options ?? KingfisherEmptyOptionsInfo + let data = fetchLoad.responseData as Data + + if let image = options.processor.process(item: .data(data), options: options) { + + downloader.delegate?.imageDownloader(downloader, didDownload: image, for: url, with: task.response) + + if options.backgroundDecode { + self.callback(with: image.kf_decoded(scale: options.scaleFactor), error: nil, url: url, originalData: data) + } else { + self.callback(with: image, error: nil, url: url, originalData: data) + } + + } else { + // If server response is 304 (Not Modified), inform the callback handler with NotModified error. + // It should be handled to get an image from cache, which is response of a manager object. + if let res = task.response as? HTTPURLResponse , res.statusCode == 304 { + self.callback(with: nil, error: NSError(domain: KingfisherErrorDomain, code: KingfisherError.notModified.rawValue, userInfo: nil), url: url, originalData: nil) + return + } + + self.callback(with: nil, error: NSError(domain: KingfisherErrorDomain, code: KingfisherError.badData.rawValue, userInfo: nil), url: url, originalData: nil) + } + } + } +} + +// Placeholder. For retrieving extension methods of ImageDownloaderDelegate +extension ImageDownloader: ImageDownloaderDelegate {} diff --git a/Example/Pods/Kingfisher/Sources/ImagePrefetcher.swift b/Example/Pods/Kingfisher/Sources/ImagePrefetcher.swift new file mode 100755 index 0000000..f7adac8 --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/ImagePrefetcher.swift @@ -0,0 +1,267 @@ +// +// ImagePrefetcher.swift +// Kingfisher +// +// Created by Claire Knight on 24/02/2016 +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#if os(macOS) + import AppKit +#else + import UIKit +#endif + + +/// Progress update block of prefetcher. +/// +/// - `skippedResources`: An array of resources that are already cached before the prefetching starting. +/// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all. +/// - `completedResources`: An array of resources that are downloaded and cached successfully. +public typealias PrefetcherProgressBlock = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> ()) + +/// Completion block of prefetcher. +/// +/// - `skippedResources`: An array of resources that are already cached before the prefetching starting. +/// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all. +/// - `completedResources`: An array of resources that are downloaded and cached successfully. +public typealias PrefetcherCompletionHandler = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> ()) + +/// `ImagePrefetcher` represents a downloading manager for requesting many images via URLs, then caching them. +/// This is useful when you know a list of image resources and want to download them before showing. +public class ImagePrefetcher { + + /// The maximum concurrent downloads to use when prefetching images. Default is 5. + public var maxConcurrentDownloads = 5 + + private let prefetchResources: [Resource] + private let optionsInfo: KingfisherOptionsInfo + private var progressBlock: PrefetcherProgressBlock? + private var completionHandler: PrefetcherCompletionHandler? + + private var tasks = [URL: RetrieveImageDownloadTask]() + + private var pendingResources: ArraySlice + private var skippedResources = [Resource]() + private var completedResources = [Resource]() + private var failedResources = [Resource]() + + private var stopped = false + + // The created manager used for prefetch. We will use the helper method in manager. + private let manager: KingfisherManager + + private var finished: Bool { + return failedResources.count + skippedResources.count + completedResources.count == prefetchResources.count && self.tasks.isEmpty + } + + /** + Init an image prefetcher with an array of URLs. + + The prefetcher should be initiated with a list of prefetching targets. The URLs list is immutable. + After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process. + The images already cached will be skipped without downloading again. + + - parameter urls: The URLs which should be prefetched. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called every time an resource is downloaded, skipped or cancelled. + - parameter completionHandler: Called when the whole prefetching process finished. + + - returns: An `ImagePrefetcher` object. + + - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as + the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`. + Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method. + */ + public convenience init(urls: [URL], + options: KingfisherOptionsInfo? = nil, + progressBlock: PrefetcherProgressBlock? = nil, + completionHandler: PrefetcherCompletionHandler? = nil) + { + let resources: [Resource] = urls.map { $0 } + self.init(resources: resources, options: options, progressBlock: progressBlock, completionHandler: completionHandler) + } + + /** + Init an image prefetcher with an array of resources. + + The prefetcher should be initiated with a list of prefetching targets. The resources list is immutable. + After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process. + The images already cached will be skipped without downloading again. + + - parameter resources: The resources which should be prefetched. See `Resource` type for more. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called every time an resource is downloaded, skipped or cancelled. + - parameter completionHandler: Called when the whole prefetching process finished. + + - returns: An `ImagePrefetcher` object. + + - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as + the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`. + Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method. + */ + public init(resources: [Resource], + options: KingfisherOptionsInfo? = nil, + progressBlock: PrefetcherProgressBlock? = nil, + completionHandler: PrefetcherCompletionHandler? = nil) + { + prefetchResources = resources + pendingResources = ArraySlice(resources) + + // We want all callbacks from main queue, so we ignore the call back queue in options + let optionsInfoWithoutQueue = options?.kf_removeAllMatchesIgnoringAssociatedValue(.callbackDispatchQueue(nil)) + self.optionsInfo = optionsInfoWithoutQueue ?? KingfisherEmptyOptionsInfo + + let cache = self.optionsInfo.targetCache + let downloader = self.optionsInfo.downloader + manager = KingfisherManager(downloader: downloader, cache: cache) + + self.progressBlock = progressBlock + self.completionHandler = completionHandler + } + + /** + Start to download the resources and cache them. This can be useful for background downloading + of assets that are required for later use in an app. This code will not try and update any UI + with the results of the process. + */ + public func start() + { + // Since we want to handle the resources cancellation in main thread only. + DispatchQueue.main.safeAsync { + + guard !self.stopped else { + assertionFailure("You can not restart the same prefetcher. Try to create a new prefetcher.") + self.handleComplete() + return + } + + guard self.maxConcurrentDownloads > 0 else { + assertionFailure("There should be concurrent downloads value should be at least 1.") + self.handleComplete() + return + } + + guard self.prefetchResources.count > 0 else { + self.handleComplete() + return + } + + let initialConcurentDownloads = min(self.prefetchResources.count, self.maxConcurrentDownloads) + for _ in 0 ..< initialConcurentDownloads { + if let resource = self.pendingResources.popFirst() { + self.startPrefetching(resource) + } + } + } + } + + + /** + Stop current downloading progress, and cancel any future prefetching activity that might be occuring. + */ + public func stop() { + DispatchQueue.main.safeAsync { + + if self.finished { return } + + self.stopped = true + self.tasks.forEach { (_, task) -> () in + task.cancel() + } + } + } + + func downloadAndCache(_ resource: Resource) { + + let downloadTaskCompletionHandler: CompletionHandler = { (image, error, _, _) -> () in + self.tasks.removeValue(forKey: resource.downloadURL) + if let _ = error { + self.failedResources.append(resource) + } else { + self.completedResources.append(resource) + } + + self.reportProgress() + if self.stopped { + if self.tasks.isEmpty { + self.failedResources.append(contentsOf: self.pendingResources) + self.handleComplete() + } + } else { + self.reportCompletionOrStartNext() + } + } + + let downloadTask = manager.downloadAndCacheImage( + with: resource.downloadURL, + forKey: resource.cacheKey, + retrieveImageTask: RetrieveImageTask(), + progressBlock: nil, + completionHandler: downloadTaskCompletionHandler, + options: optionsInfo) + + if let downloadTask = downloadTask { + tasks[resource.downloadURL] = downloadTask + } + } + + func append(cached resource: Resource) { + skippedResources.append(resource) + + reportProgress() + reportCompletionOrStartNext() + } + + func startPrefetching(_ resource: Resource) + { + if optionsInfo.forceRefresh { + downloadAndCache(resource) + } else { + let alreadyInCache = manager.cache.isImageCached(forKey: resource.cacheKey).cached + if alreadyInCache { + append(cached: resource) + } else { + downloadAndCache(resource) + } + } + } + + func reportProgress() { + progressBlock?(skippedResources, failedResources, completedResources) + } + + func reportCompletionOrStartNext() { + if let resource = pendingResources.popFirst() { + startPrefetching(resource) + } else { + guard tasks.isEmpty else { return } + handleComplete() + } + } + + func handleComplete() { + completionHandler?(skippedResources, failedResources, completedResources) + completionHandler = nil + progressBlock = nil + } +} diff --git a/Example/Pods/Kingfisher/Sources/ImageProcessor.swift b/Example/Pods/Kingfisher/Sources/ImageProcessor.swift new file mode 100644 index 0000000..ffb9e29 --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/ImageProcessor.swift @@ -0,0 +1,367 @@ +// +// ImageProcessor.swift +// Kingfisher +// +// Created by Wei Wang on 2016/08/26. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation +import CoreGraphics + + +/// The item which could be processed by an `ImageProcessor` +/// +/// - image: Input image +/// - data: Input data +public enum ImageProcessItem { + case image(Image) + case data(Data) +} + +/// An `ImageProcessor` would be used to convert some downloaded data to an image. +public protocol ImageProcessor { + /// Identifier of the processor. It will be used to identify the processor when + /// caching and retriving an image. You might want to make sure that processors with + /// same properties/functionality have the same identifiers, so correct processed images + /// could be retrived with proper key. + /// + /// - Note: Do not supply an empty string for a customized processor, which is already taken by + /// the `DefaultImageProcessor`. It is recommended to use a reverse domain name notation + /// string of your own for the identifier. + var identifier: String { get } + + /// Process an input `ImageProcessItem` item to an image for this processor. + /// + /// - parameter item: Input item which will be processed by `self` + /// - parameter options: Options when processing the item. + /// + /// - returns: The processed image. + /// + /// - Note: The return value will be `nil` if processing failed while converting data to image. + /// If input item is already an image and there is any errors in processing, the input + /// image itself will be returned. + /// - Note: Most processor only supports CG-based images. + /// watchOS is not supported for processers containing filter, the input image will be returned directly on watchOS. + func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? +} + +typealias ProcessorImp = ((ImageProcessItem, KingfisherOptionsInfo) -> Image?) + +public extension ImageProcessor { + + /// Append an `ImageProcessor` to another. The identifier of the new `ImageProcessor` + /// will be "\(self.identifier)|>\(another.identifier)>". + /// + /// - parameter another: An `ImageProcessor` you want to append to `self`. + /// + /// - returns: The new `ImageProcessor`. It will process the image in the order + /// of the two processors concatenated. + public func append(another: ImageProcessor) -> ImageProcessor { + let newIdentifier = identifier.appending("|>\(another.identifier)") + return GeneralProcessor(identifier: newIdentifier) { + item, options in + if let image = self.process(item: item, options: options) { + return another.process(item: .image(image), options: options) + } else { + return nil + } + } + } +} + +fileprivate struct GeneralProcessor: ImageProcessor { + let identifier: String + let p: ProcessorImp + func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + return p(item, options) + } +} + +/// The default processor. It convert the input data to a valid image. +/// Images of .PNG, .JPEG and .GIF format are supported. +/// If an image is given, `DefaultImageProcessor` will do nothing on it and just return that image. +public struct DefaultImageProcessor: ImageProcessor { + + /// A default `DefaultImageProcessor` could be used across. + public static let `default` = DefaultImageProcessor() + + public let identifier = "" + + /// Initialize a `DefaultImageProcessor` + /// + /// - returns: An initialized `DefaultImageProcessor`. + public init() {} + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + return image + case .data(let data): + return Image.kf_image(data: data, scale: options.scaleFactor, preloadAllGIFData: options.preloadAllGIFData) + } + } +} + +/// Processor for making round corner images. Only CG-based images are supported in macOS, +/// if a non-CG image passed in, the processor will do nothing. +public struct RoundCornerImageProcessor: ImageProcessor { + public let identifier: String + + /// Corner radius will be applied in processing. + public let cornerRadius: CGFloat + + /// Target size of output image should be. If `nil`, the image will keep its original size after processing. + public let targetSize: CGSize? + + /// Initialize a `RoundCornerImageProcessor` + /// + /// - parameter cornerRadius: Corner radius will be applied in processing. + /// - parameter targetSize: Target size of output image should be. If `nil`, + /// the image will keep its original size after processing. + /// Default is `nil`. + /// + /// - returns: An initialized `RoundCornerImageProcessor`. + public init(cornerRadius: CGFloat, targetSize: CGSize? = nil) { + self.cornerRadius = cornerRadius + self.targetSize = targetSize + if let size = targetSize { + self.identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius)_\(size))" + } else { + self.identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius))" + } + } + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + let size = targetSize ?? image.kf_size + return image.kf_image(withRoundRadius: cornerRadius, fit: size, scale: options.scaleFactor) + case .data(_): + return (DefaultImageProcessor() |> self).process(item: item, options: options) + } + } +} + +/// Processor for resizing images. Only CG-based images are supported in macOS. +public struct ResizingImageProcessor: ImageProcessor { + public let identifier: String + + /// Target size of output image should be. + public let targetSize: CGSize + + /// Initialize a `ResizingImageProcessor` + /// + /// - parameter targetSize: Target size of output image should be. + /// + /// - returns: An initialized `ResizingImageProcessor`. + public init(targetSize: CGSize) { + self.targetSize = targetSize + self.identifier = "com.onevcat.Kingfisher.ResizingImageProcessor(\(targetSize))" + } + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + return image.kf_resize(to: targetSize) + case .data(_): + return (DefaultImageProcessor() |> self).process(item: item, options: options) + } + } +} + +/// Processor for adding blur effect to images. `Accelerate.framework` is used underhood for +/// a better performance. A simulated Gaussian blur with specified blur radius will be applied. +public struct BlurImageProcessor: ImageProcessor { + public let identifier: String + + /// Blur radius for the simulated Gaussian blur. + public let blurRadius: CGFloat + + /// Initialize a `BlurImageProcessor` + /// + /// - parameter blurRadius: Blur radius for the simulated Gaussian blur. + /// + /// - returns: An initialized `BlurImageProcessor`. + public init(blurRadius: CGFloat) { + self.blurRadius = blurRadius + self.identifier = "com.onevcat.Kingfisher.BlurImageProcessor(\(blurRadius))" + } + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + let radius = blurRadius * options.scaleFactor + return image.kf_blurred(withRadius: radius) + case .data(_): + return (DefaultImageProcessor() |> self).process(item: item, options: options) + } + } +} + +/// Processor for adding an overlay to images. Only CG-based images are supported in macOS. +public struct OverlayImageProcessor: ImageProcessor { + + public var identifier: String + + /// Overlay color will be used to overlay the input image. + public let overlay: Color + + /// Fraction will be used when overlay the color to image. + public let fraction: CGFloat + + /// Initialize an `OverlayImageProcessor` + /// + /// - parameter overlay: Overlay color will be used to overlay the input image. + /// - parameter fraction: Fraction will be used when overlay the color to image. + /// From 0.0 to 1.0. 0.0 means solid color, 1.0 means transparent overlay. + /// + /// - returns: An initialized `OverlayImageProcessor`. + public init(overlay: Color, fraction: CGFloat = 0.5) { + self.overlay = overlay + self.fraction = fraction + self.identifier = "com.onevcat.Kingfisher.OverlayImageProcessor(\(overlay.hex)_\(fraction))" + } + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + return image.kf_overlaying(with: overlay, fraction: fraction) + case .data(_): + return (DefaultImageProcessor() |> self).process(item: item, options: options) + } + } +} + +/// Processor for tint images with color. Only CG-based images are supported. +public struct TintImageProcessor: ImageProcessor { + + public let identifier: String + + /// Tint color will be used to tint the input image. + public let tint: Color + + /// Initialize a `TintImageProcessor` + /// + /// - parameter tint: Tint color will be used to tint the input image. + /// + /// - returns: An initialized `TintImageProcessor`. + public init(tint: Color) { + self.tint = tint + self.identifier = "com.onevcat.Kingfisher.TintImageProcessor(\(tint.hex))" + } + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + return image.kf_tinted(with: tint) + case .data(_): + return (DefaultImageProcessor() |> self).process(item: item, options: options) + } + } +} + +/// Processor for applying some color control to images. Only CG-based images are supported. +/// watchOS is not supported. +public struct ColorControlsProcessor: ImageProcessor { + + public let identifier: String + + /// Brightness changing to image. + public let brightness: CGFloat + + /// Contrast changing to image. + public let contrast: CGFloat + + /// Saturation changing to image. + public let saturation: CGFloat + + /// InputEV changing to image. + public let inputEV: CGFloat + + /// Initialize a `ColorControlsProcessor` + /// + /// - parameter brightness: Brightness changing to image. + /// - parameter contrast: Contrast changing to image. + /// - parameter saturation: Saturation changing to image. + /// - parameter inputEV: InputEV changing to image. + /// + /// - returns: An initialized `ColorControlsProcessor` + public init(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) { + self.brightness = brightness + self.contrast = contrast + self.saturation = saturation + self.inputEV = inputEV + self.identifier = "com.onevcat.Kingfisher.ColorControlsProcessor(\(brightness)_\(contrast)_\(saturation)_\(inputEV))" + } + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + return image.kf_adjusted(brightness: brightness, contrast: contrast, saturation: saturation, inputEV: inputEV) + case .data(_): + return (DefaultImageProcessor() |> self).process(item: item, options: options) + } + } +} + +/// Processor for applying black and white effect to images. Only CG-based images are supported. +/// watchOS is not supported. +public struct BlackWhiteProcessor: ImageProcessor { + public let identifier = "com.onevcat.Kingfisher.BlackWhiteProcessor" + + /// Initialize a `BlackWhiteProcessor` + /// + /// - returns: An initialized `BlackWhiteProcessor` + public init() {} + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + return ColorControlsProcessor(brightness: 0.0, contrast: 1.0, saturation: 0.0, inputEV: 0.7) + .process(item: item, options: options) + } +} + +infix operator |>: AdditionPrecedence + +/// Concatenate two `ImageProcessor`s. `ImageProcessor.appen(another:)` is used internally. +/// +/// - parameter left: First processor. +/// - parameter right: Second processor. +/// +/// - returns: The concatenated processor. +public func |>(left: ImageProcessor, right: ImageProcessor) -> ImageProcessor { + return left.append(another: right) +} + +fileprivate extension Color { + var hex: String { + var r: CGFloat = 0 + var g: CGFloat = 0 + var b: CGFloat = 0 + var a: CGFloat = 0 + + getRed(&r, green: &g, blue: &b, alpha: &a) + + let rgba = Int(r * 255) << 24 | Int(g * 255) << 16 | Int(b * 255) << 8 | Int(a * 255) + + return String(format:"#%08x", rgba) + } +} diff --git a/Example/Pods/Kingfisher/Sources/ImageSerializer.swift b/Example/Pods/Kingfisher/Sources/ImageSerializer.swift new file mode 100644 index 0000000..10dc89a --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/ImageSerializer.swift @@ -0,0 +1,85 @@ +// +// ImageSerializer.swift +// Kingfisher +// +// Created by Wei Wang on 2016/09/02. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +/// An `CacheSerializer` would be used to convert some data to an image object for +/// retrieving from disk cache and vice versa for storing to disk cache. +public protocol CacheSerializer { + + /// Get the serialized data from a provided image + /// and optional original data for caching to disk. + /// + /// + /// - parameter image: The image needed to be serialized. + /// - parameter original: The original data which is just downloaded. + /// If the image is retrieved from cache instead of + /// downloaded, it will be `nil`. + /// + /// - returns: A data which will be stored to cache, or `nil` when no valid + /// data could be serialized. + func data(with image: Image, original: Data?) -> Data? + + /// Get an image deserialized from provided data. + /// + /// - parameter data: The data from which an image should be deserialized. + /// - parameter options: Options for deserialization. + /// + /// - returns: An image deserialized or `nil` when no valid image + /// could be deserialized. + func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? +} + + +/// `DefaultCacheSerializer` is a basic `CacheSerializer` used in default cache of +/// Kingfisher. It could serialize and deserialize PNG, JEPG and GIF images. For +/// image other than these formats, a normalized `pngRepresentation` will be used. +public struct DefaultCacheSerializer: CacheSerializer { + + public static let `default` = DefaultCacheSerializer() + private init() {} + + public func data(with image: Image, original: Data?) -> Data? { + let imageFormat = original?.kf_imageFormat ?? .unknown + + let data: Data? + switch imageFormat { + case .PNG: data = image.pngRepresentation() + case .JPEG: data = image.jpegRepresentation(compressionQuality: 1.0) + case .GIF: data = image.gifRepresentation() + case .unknown: data = original ?? image.kf_normalized().pngRepresentation() + } + + return data + } + + public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? { + let scale = (options ?? KingfisherEmptyOptionsInfo).scaleFactor + let preloadAllGIFData = (options ?? KingfisherEmptyOptionsInfo).preloadAllGIFData + + return Image.kf_image(data: data, scale: scale, preloadAllGIFData: preloadAllGIFData) + } +} diff --git a/Example/Pods/Kingfisher/Sources/ImageTransition.swift b/Example/Pods/Kingfisher/Sources/ImageTransition.swift new file mode 100755 index 0000000..9fa9077 --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/ImageTransition.swift @@ -0,0 +1,128 @@ +// +// ImageTransition.swift +// Kingfisher +// +// Created by Wei Wang on 15/9/18. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(macOS) +// Not implemented for macOS and watchOS yet. + +import AppKit + +/// Image transition is not supported on macOS. +public enum ImageTransition { + case none + var duration: TimeInterval { + return 0 + } +} + +#elseif os(watchOS) +import UIKit +/// Image transition is not supported on watchOS. +public enum ImageTransition { + case none + var duration: TimeInterval { + return 0 + } +} +#else +import UIKit + +/** +Transition effect which will be used when an image downloaded and set by `UIImageView` extension API in Kingfisher. +You can assign an enum value with transition duration as an item in `KingfisherOptionsInfo` +to enable the animation transition. + +Apple's UIViewAnimationOptions is used under the hood. +For custom transition, you should specified your own transition options, animations and +comletion handler as well. +*/ +public enum ImageTransition { + /// No animation transistion. + case none + + /// Fade in the loaded image. + case fade(TimeInterval) + + /// Flip from left transition. + case flipFromLeft(TimeInterval) + + /// Flip from right transition. + case flipFromRight(TimeInterval) + + /// Flip from top transition. + case flipFromTop(TimeInterval) + + /// Flip from bottom transition. + case flipFromBottom(TimeInterval) + + /// Custom transition. + case custom(duration: TimeInterval, + options: UIViewAnimationOptions, + animations: ((UIImageView, UIImage) -> Void)?, + completion: ((Bool) -> Void)?) + + var duration: TimeInterval { + switch self { + case .none: return 0 + case .fade(let duration): return duration + + case .flipFromLeft(let duration): return duration + case .flipFromRight(let duration): return duration + case .flipFromTop(let duration): return duration + case .flipFromBottom(let duration): return duration + + case .custom(let duration, _, _, _): return duration + } + } + + var animationOptions: UIViewAnimationOptions { + switch self { + case .none: return [] + case .fade(_): return .transitionCrossDissolve + + case .flipFromLeft(_): return .transitionFlipFromLeft + case .flipFromRight(_): return .transitionFlipFromRight + case .flipFromTop(_): return .transitionFlipFromTop + case .flipFromBottom(_): return .transitionFlipFromBottom + + case .custom(_, let options, _, _): return options + } + } + + var animations: ((UIImageView, UIImage) -> Void)? { + switch self { + case .custom(_, _, let animations, _): return animations + default: return { $0.image = $1 } + } + } + + var completion: ((Bool) -> Void)? { + switch self { + case .custom(_, _, _, let completion): return completion + default: return nil + } + } +} +#endif diff --git a/Example/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift b/Example/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift new file mode 100755 index 0000000..81d3359 --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift @@ -0,0 +1,281 @@ +// +// ImageView+Kingfisher.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/6. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#if os(macOS) +import AppKit +typealias ImageView = NSImageView +public typealias IndicatorView = NSProgressIndicator +#else +import UIKit +typealias ImageView = UIImageView +public typealias IndicatorView = UIActivityIndicatorView +#endif + +// MARK: - Set Images +/** +* Set image to use from web. +*/ +extension ImageView { + + /** + Set an image with a resource, a placeholder image, options, progress handler and completion handler. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. + */ + @discardableResult + public func kf_setImage(with resource: Resource?, + placeholder: Image? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + { + image = placeholder + + guard let resource = resource else { + completionHandler?(nil, nil, .none, nil) + return .emptyTask + } + + let showIndicatorWhenLoading = kf_showIndicatorWhenLoading + var indicator: IndicatorView? = nil + if showIndicatorWhenLoading { + indicator = kf_indicator + indicator?.isHidden = false + indicator?.kf_startAnimating() + } + + kf_setWebURL(resource.downloadURL) + + var options = options ?? KingfisherEmptyOptionsInfo + if shouldPreloadAllGIF() { + options.append(.preloadAllGIFData) + } + + let task = KingfisherManager.shared.retrieveImage(with: resource, options: options, + progressBlock: { receivedSize, totalSize in + if let progressBlock = progressBlock { + progressBlock(receivedSize, totalSize) + } + }, + completionHandler: {[weak self] image, error, cacheType, imageURL in + + DispatchQueue.main.safeAsync { + guard let sSelf = self, imageURL == sSelf.kf_webURL else { + return + } + + sSelf.kf_setImageTask(nil) + + guard let image = image else { + indicator?.kf_stopAnimating() + completionHandler?(nil, error, cacheType, imageURL) + return + } + + guard let transitionItem = options.kf_firstMatchIgnoringAssociatedValue(.transition(.none)), + case .transition(let transition) = transitionItem, ( options.forceTransition || cacheType == .none) else + { + indicator?.kf_stopAnimating() + sSelf.image = image + completionHandler?(image, error, cacheType, imageURL) + return + } + + #if !os(macOS) + UIView.transition(with: sSelf, duration: 0.0, options: [], + animations: { indicator?.kf_stopAnimating() }, + completion: { _ in + UIView.transition(with: sSelf, duration: transition.duration, + options: [transition.animationOptions, .allowUserInteraction], + animations: { + // Set image property in the animation. + transition.animations?(sSelf, image) + }, + completion: { finished in + transition.completion?(finished) + completionHandler?(image, error, cacheType, imageURL) + } + ) + }) + #endif + } + }) + + kf_setImageTask(task) + + return task + } +} + +extension ImageView { + func shouldPreloadAllGIF() -> Bool { + return true + } +} + +extension ImageView { + /** + Cancel the image download task bounded to the image view if it is running. + Nothing will happen if the downloading has already finished. + */ + public func kf_cancelDownloadTask() { + kf_imageTask?.downloadTask?.cancel() + } +} + +// MARK: - Associated Object +private var lastURLKey: Void? +private var indicatorKey: Void? +private var showIndicatorWhenLoadingKey: Void? +private var imageTaskKey: Void? + +extension ImageView { + /// Get the image URL binded to this image view. + public var kf_webURL: URL? { + return objc_getAssociatedObject(self, &lastURLKey) as? URL + } + + fileprivate func kf_setWebURL(_ url: URL) { + objc_setAssociatedObject(self, &lastURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + + /// Whether show an animating indicator when the image view is loading an image or not. + /// Default is false. + public var kf_showIndicatorWhenLoading: Bool { + get { + if let result = objc_getAssociatedObject(self, &showIndicatorWhenLoadingKey) as? NSNumber { + return result.boolValue + } else { + return false + } + } + + set { + if kf_showIndicatorWhenLoading == newValue { + return + } else { + if newValue { + +#if os(macOS) + let indicator = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16)) + indicator.controlSize = .small + indicator.style = .spinningStyle +#else + #if os(tvOS) + let indicatorStyle = UIActivityIndicatorViewStyle.white + #else + let indicatorStyle = UIActivityIndicatorViewStyle.gray + #endif + let indicator = UIActivityIndicatorView(activityIndicatorStyle:indicatorStyle) + indicator.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin, .flexibleTopMargin] +#endif + + indicator.kf_center = CGPoint(x: bounds.midX, y: bounds.midY) + indicator.isHidden = true + + self.addSubview(indicator) + + kf_setIndicator(indicator) + } else { + kf_indicator?.removeFromSuperview() + kf_setIndicator(nil) + } + + objc_setAssociatedObject(self, &showIndicatorWhenLoadingKey, NSNumber(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + } + + /// The indicator view showing when loading. This will be `nil` if `kf_showIndicatorWhenLoading` is false. + /// You may want to use this to set the indicator style or color when you set `kf_showIndicatorWhenLoading` to true. + public var kf_indicator: IndicatorView? { + return objc_getAssociatedObject(self, &indicatorKey) as? IndicatorView + } + + fileprivate func kf_setIndicator(_ indicator: IndicatorView?) { + objc_setAssociatedObject(self, &indicatorKey, indicator, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + + fileprivate var kf_imageTask: RetrieveImageTask? { + return objc_getAssociatedObject(self, &imageTaskKey) as? RetrieveImageTask + } + + fileprivate func kf_setImageTask(_ task: RetrieveImageTask?) { + objc_setAssociatedObject(self, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } +} + + +extension IndicatorView { + func kf_startAnimating() { + #if os(macOS) + startAnimation(nil) + #else + startAnimating() + #endif + isHidden = false + } + + func kf_stopAnimating() { + #if os(macOS) + stopAnimation(nil) + #else + stopAnimating() + #endif + isHidden = true + } + + #if os(macOS) + var kf_center: CGPoint { + get { + return CGPoint(x: frame.origin.x + frame.size.width / 2.0, y: frame.origin.y + frame.size.height / 2.0 ) + } + set { + let newFrame = CGRect(x: newValue.x - frame.size.width / 2.0, y: newValue.y - frame.size.height / 2.0, width: frame.size.width, height: frame.size.height) + frame = newFrame + } + } + #else + var kf_center: CGPoint { + get { + return center + } + set { + center = newValue + } + } + #endif +} diff --git a/Example/Pods/Kingfisher/Sources/Kingfisher.h b/Example/Pods/Kingfisher/Sources/Kingfisher.h new file mode 100644 index 0000000..05350ce --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/Kingfisher.h @@ -0,0 +1,37 @@ +// +// Kingfisher.h +// Kingfisher +// +// Created by Wei Wang on 15/4/6. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +//! Project version number for Kingfisher. +FOUNDATION_EXPORT double KingfisherVersionNumber; + +//! Project version string for Kingfisher. +FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Example/Pods/Kingfisher/Sources/KingfisherManager.swift b/Example/Pods/Kingfisher/Sources/KingfisherManager.swift new file mode 100755 index 0000000..67b8e63 --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/KingfisherManager.swift @@ -0,0 +1,213 @@ +// +// KingfisherManager.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/6. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(macOS) +import AppKit +#else +import UIKit +#endif + +public typealias DownloadProgressBlock = ((_ receivedSize: Int64, _ totalSize: Int64) -> ()) +public typealias CompletionHandler = ((_ image: Image?, _ error: NSError?, _ cacheType: CacheType, _ imageURL: URL?) -> ()) + +/// RetrieveImageTask represents a task of image retrieving process. +/// It contains an async task of getting image from disk and from network. +public class RetrieveImageTask { + + static let emptyTask = RetrieveImageTask() + + // If task is canceled before the download task started (which means the `downloadTask` is nil), + // the download task should not begin. + var cancelledBeforeDownloadStarting: Bool = false + + /// The disk retrieve task in this image task. Kingfisher will try to look up in cache first. This task represent the cache search task. + public var diskRetrieveTask: RetrieveImageDiskTask? + + /// The network retrieve task in this image task. + public var downloadTask: RetrieveImageDownloadTask? + + /** + Cancel current task. If this task does not begin or already done, do nothing. + */ + public func cancel() { + // From Xcode 7 beta 6, the `dispatch_block_cancel` will crash at runtime. + // It fixed in Xcode 7.1. + // See https://github.com/onevcat/Kingfisher/issues/99 for more. + if let diskRetrieveTask = diskRetrieveTask { + diskRetrieveTask.cancel() + } + + if let downloadTask = downloadTask { + downloadTask.cancel() + } else { + cancelledBeforeDownloadStarting = true + } + } +} + +/// Error domain of Kingfisher +public let KingfisherErrorDomain = "com.onevcat.Kingfisher.Error" + +/// Main manager class of Kingfisher. It connects Kingfisher downloader and cache. +/// You can use this class to retrieve an image via a specified URL from web or cache. +public class KingfisherManager { + + /// Shared manager used by the extensions across Kingfisher. + public static let shared = KingfisherManager() + + /// Cache used by this manager + public var cache: ImageCache + + /// Downloader used by this manager + public var downloader: ImageDownloader + + convenience init() { + self.init(downloader: .default, cache: .default) + } + + init(downloader: ImageDownloader, cache: ImageCache) { + self.downloader = downloader + self.cache = cache + } + + /** + Get an image with resource. + If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first. + If not found, it will download the image at `resource.downloadURL` and cache it with `resource.cacheKey`. + These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called every time downloaded data changed. This could be used as a progress UI. + - parameter completionHandler: Called when the whole retrieving process finished. + + - returns: A `RetrieveImageTask` task object. You can use this object to cancel the task. + */ + @discardableResult + public func retrieveImage(with resource: Resource, + options: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + let task = RetrieveImageTask() + + if let options = options, options.forceRefresh { + _ = downloadAndCacheImage( + with: resource.downloadURL, + forKey: resource.cacheKey, + retrieveImageTask: task, + progressBlock: progressBlock, + completionHandler: completionHandler, + options: options) + } else { + tryToRetrieveImageFromCache( + forKey: resource.cacheKey, + with: resource.downloadURL, + retrieveImageTask: task, + progressBlock: progressBlock, + completionHandler: completionHandler, + options: options) + } + + return task + } + + @discardableResult + func downloadAndCacheImage(with url: URL, + forKey key: String, + retrieveImageTask: RetrieveImageTask, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?, + options: KingfisherOptionsInfo?) -> RetrieveImageDownloadTask? + { + let options = options ?? KingfisherEmptyOptionsInfo + let downloader = options.downloader + return downloader.downloadImage(with: url, retrieveImageTask: retrieveImageTask, options: options, + progressBlock: { receivedSize, totalSize in + progressBlock?(receivedSize, totalSize) + }, + completionHandler: { image, error, imageURL, originalData in + + let targetCache = options.targetCache + if let error = error, error.code == KingfisherError.notModified.rawValue { + // Not modified. Try to find the image from cache. + // (The image should be in cache. It should be guaranteed by the framework users.) + targetCache.retrieveImage(forKey: key, options: options, completionHandler: { (cacheImage, cacheType) -> () in + completionHandler?(cacheImage, nil, cacheType, url) + }) + return + } + + if let image = image, let originalData = originalData { + targetCache.store(image, + original: originalData, + forKey: key, + processorIdentifier:options.processor.identifier, + cacheSerializer: options.cacheSerializer, + toDisk: !options.cacheMemoryOnly, + completionHandler: nil) + } + + completionHandler?(image, error, .none, url) + + }) + } + + func tryToRetrieveImageFromCache(forKey key: String, + with url: URL, + retrieveImageTask: RetrieveImageTask, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?, + options: KingfisherOptionsInfo?) + { + let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> () in + // Break retain cycle created inside diskTask closure below + retrieveImageTask.diskRetrieveTask = nil + completionHandler?(image, error, cacheType, imageURL) + } + + let targetCache = options?.targetCache ?? cache + let diskTask = targetCache.retrieveImage(forKey: key, options: options, + completionHandler: { image, cacheType in + if image != nil { + diskTaskCompletionHandler(image, nil, cacheType, url) + } else if let options = options, options.onlyFromCache { + let error = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notCached.rawValue, userInfo: nil) + diskTaskCompletionHandler(nil, error, .none, url) + } else { + self.downloadAndCacheImage( + with: url, + forKey: key, + retrieveImageTask: retrieveImageTask, + progressBlock: progressBlock, + completionHandler: diskTaskCompletionHandler, + options: options) + } + } + ) + retrieveImageTask.diskRetrieveTask = diskTask + } +} diff --git a/Example/Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift b/Example/Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift new file mode 100755 index 0000000..41237bf --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift @@ -0,0 +1,255 @@ +// +// KingfisherOptionsInfo.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/23. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(macOS) +import AppKit +#else +import UIKit +#endif + + +/** +* KingfisherOptionsInfo is a typealias for [KingfisherOptionsInfoItem]. You can use the enum of option item with value to control some behaviors of Kingfisher. +*/ +public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem] +let KingfisherEmptyOptionsInfo = [KingfisherOptionsInfoItem]() + +/** +Items could be added into KingfisherOptionsInfo. +*/ +public enum KingfisherOptionsInfoItem { + /// The associated value of this member should be an ImageCache object. Kingfisher will use the specified + /// cache object when handling related operations, including trying to retrieve the cached images and store + /// the downloaded image to it. + case targetCache(ImageCache) + + /// The associated value of this member should be an ImageDownloader object. Kingfisher will use this + /// downloader to download the images. + case downloader(ImageDownloader) + + /// Member for animation transition when using UIImageView. Kingfisher will use the `ImageTransition` of + /// this enum to animate the image in if it is downloaded from web. The transition will not happen when the + /// image is retrieved from either memory or disk cache by default. If you need to do the transition even when + /// the image being retrieved from cache, set `ForceTransition` as well. + case transition(ImageTransition) + + /// Associated `Float` value will be set as the priority of image download task. The value for it should be + /// between 0.0~1.0. If this option not set, the default value (`NSURLSessionTaskPriorityDefault`) will be used. + case downloadPriority(Float) + + /// If set, `Kingfisher` will ignore the cache and try to fire a download task for the resource. + case forceRefresh + + /// If set, setting the image to an image view will happen with transition even when retrieved from cache. + /// See `Transition` option for more. + case forceTransition + + /// If set, `Kingfisher` will only cache the value in memory but not in disk. + case cacheMemoryOnly + + /// If set, `Kingfisher` will only try to retrieve the image from cache not from network. + case onlyFromCache + + /// Decode the image in background thread before using. + case backgroundDecode + + /// The associated value of this member will be used as the target queue of dispatch callbacks when + /// retrieving images from cache. If not set, `Kingfisher` will use main quese for callbacks. + case callbackDispatchQueue(DispatchQueue?) + + /// The associated value of this member will be used as the scale factor when converting retrieved data to an image. + case scaleFactor(CGFloat) + + /// Whether all the GIF data should be preloaded. Default it false, which means following frames will be + /// loaded on need. If true, all the GIF data will be loaded and decoded into memory. This option is mainly + /// used for back compatibility internally. You should not set it directly. `AnimatedImageView` will not preload + /// all data, while a normal image view (`UIImageView` or `NSImageView`) will load all data. Choose to use + /// corresponding image view type instead of setting this option. + case preloadAllGIFData + + /// The `ImageDownloadRequestModifier` contained will be used to change the request before it being sent. + /// This is the last chance you can modify the request. You can modify the request for some customizing purpose, + /// such as adding auth token to the header, do basic HTTP auth or something like url mapping. The original request + /// will be sent without any modification by default. + case requestModifier(ImageDownloadRequestModifier) + + /// Processor for processing when the downloading finishes, a processor will convert the downloaded data to an image + /// and/or apply some filter on it. If a cache is connected to the downloader (it happenes when you are using + /// KingfisherManager or the image extension methods), the converted image will also be sent to cache as well as the + /// image view. `DefaultImageProcessor.default` will be used by default. + case processor(ImageProcessor) + + /// Supply an `CacheSerializer` to convert some data to an image object for + /// retrieving from disk cache or vice versa for storing to disk cache. + /// `DefaultCacheSerializer.default` will be used by default. + case cacheSerializer(CacheSerializer) +} + +precedencegroup ItemComparisonPrecedence { + associativity: none + higherThan: LogicalConjunctionPrecedence +} + +infix operator <== : ItemComparisonPrecedence + +// This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values. +func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool { + switch (lhs, rhs) { + case (.targetCache(_), .targetCache(_)): return true + case (.downloader(_), .downloader(_)): return true + case (.transition(_), .transition(_)): return true + case (.downloadPriority(_), .downloadPriority(_)): return true + case (.forceRefresh, .forceRefresh): return true + case (.forceTransition, .forceTransition): return true + case (.cacheMemoryOnly, .cacheMemoryOnly): return true + case (.onlyFromCache, .onlyFromCache): return true + case (.backgroundDecode, .backgroundDecode): return true + case (.callbackDispatchQueue(_), .callbackDispatchQueue(_)): return true + case (.scaleFactor(_), .scaleFactor(_)): return true + case (.preloadAllGIFData, .preloadAllGIFData): return true + case (.requestModifier(_), .requestModifier(_)): return true + case (.processor(_), .processor(_)): return true + case (.cacheSerializer(_), .cacheSerializer(_)): return true + default: return false + } +} + +extension Collection where Iterator.Element == KingfisherOptionsInfoItem { + func kf_firstMatchIgnoringAssociatedValue(_ target: Iterator.Element) -> Iterator.Element? { + return index { $0 <== target }.flatMap { self[$0] } + } + + func kf_removeAllMatchesIgnoringAssociatedValue(_ target: Iterator.Element) -> [Iterator.Element] { + return self.filter { !($0 <== target) } + } +} + +extension Collection where Iterator.Element == KingfisherOptionsInfoItem { + var targetCache: ImageCache { + if let item = kf_firstMatchIgnoringAssociatedValue(.targetCache(.default)), + case .targetCache(let cache) = item + { + return cache + } + return ImageCache.default + } + + var downloader: ImageDownloader { + if let item = kf_firstMatchIgnoringAssociatedValue(.downloader(.default)), + case .downloader(let downloader) = item + { + return downloader + } + return ImageDownloader.default + } + + var transition: ImageTransition { + if let item = kf_firstMatchIgnoringAssociatedValue(.transition(.none)), + case .transition(let transition) = item + { + return transition + } + return ImageTransition.none + } + + var downloadPriority: Float { + if let item = kf_firstMatchIgnoringAssociatedValue(.downloadPriority(0)), + case .downloadPriority(let priority) = item + { + return priority + } + return URLSessionTask.defaultPriority + } + + var forceRefresh: Bool { + return contains{ $0 <== .forceRefresh } + } + + var forceTransition: Bool { + return contains{ $0 <== .forceTransition } + } + + var cacheMemoryOnly: Bool { + return contains{ $0 <== .cacheMemoryOnly } + } + + var onlyFromCache: Bool { + return contains{ $0 <== .onlyFromCache } + } + + var backgroundDecode: Bool { + return contains{ $0 <== .backgroundDecode } + } + + var preloadAllGIFData: Bool { + return contains { $0 <== .preloadAllGIFData } + } + + var callbackDispatchQueue: DispatchQueue { + if let item = kf_firstMatchIgnoringAssociatedValue(.callbackDispatchQueue(nil)), + case .callbackDispatchQueue(let queue) = item + { + return queue ?? DispatchQueue.main + } + return DispatchQueue.main + } + + var scaleFactor: CGFloat { + if let item = kf_firstMatchIgnoringAssociatedValue(.scaleFactor(0)), + case .scaleFactor(let scale) = item + { + return scale + } + return 1.0 + } + + var modifier: ImageDownloadRequestModifier { + if let item = kf_firstMatchIgnoringAssociatedValue(.requestModifier(NoModifier.default)), + case .requestModifier(let modifier) = item + { + return modifier + } + return NoModifier.default + } + + var processor: ImageProcessor { + if let item = kf_firstMatchIgnoringAssociatedValue(.processor(DefaultImageProcessor.default)), + case .processor(let processor) = item + { + return processor + } + return DefaultImageProcessor.default + } + + var cacheSerializer: CacheSerializer { + if let item = kf_firstMatchIgnoringAssociatedValue(.cacheSerializer(DefaultCacheSerializer.default)), + case .cacheSerializer(let cacheSerializer) = item + { + return cacheSerializer + } + return DefaultCacheSerializer.default + } +} diff --git a/Example/Pods/Kingfisher/Sources/RequrstModifier.swift b/Example/Pods/Kingfisher/Sources/RequrstModifier.swift new file mode 100644 index 0000000..c1e7ca6 --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/RequrstModifier.swift @@ -0,0 +1,53 @@ +// +// RequrstModifier.swift +// Kingfisher +// +// Created by Wei Wang on 2016/09/05. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +/// Request modifier of image downloader. +public protocol ImageDownloadRequestModifier { + func modified(for request: URLRequest) -> URLRequest? +} + +struct NoModifier: ImageDownloadRequestModifier { + static let `default` = NoModifier() + private init() {} + func modified(for request: URLRequest) -> URLRequest? { + return request + } +} + +public struct AnyModifier: ImageDownloadRequestModifier { + + let block: (URLRequest) -> URLRequest? + + public func modified(for request: URLRequest) -> URLRequest? { + return block(request) + } + + public init(modify: @escaping (URLRequest) -> URLRequest? ) { + block = modify + } +} diff --git a/Example/Pods/Kingfisher/Sources/Resource.swift b/Example/Pods/Kingfisher/Sources/Resource.swift new file mode 100755 index 0000000..875927f --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/Resource.swift @@ -0,0 +1,74 @@ +// +// Resource.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/6. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + + +/// `Resource` protocol defines how to download and cache a resource from network. +public protocol Resource { + /// The key used in cache. + var cacheKey: String { get } + + /// The target image URL. + var downloadURL: URL { get } +} + +/** + ImageResource is a simple combination of `downloadURL` and `cacheKey`. + + When passed to image view set methods, Kingfisher will try to download the target + image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache. + */ +public struct ImageResource: Resource { + /// The key used in cache. + public let cacheKey: String + + /// The target image URL. + public let downloadURL: URL + + /** + Create a resource. + + - parameter downloadURL: The target image URL. + - parameter cacheKey: The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key. + + - returns: A resource. + */ + public init(downloadURL: URL, cacheKey: String? = nil) { + self.downloadURL = downloadURL + self.cacheKey = cacheKey ?? downloadURL.absoluteString + } +} + +/** + URL conforms to `Resource` in Kingfisher. + The `absoluteString` of this URL is used as `cacheKey`. And the URL itself will be used as `downloadURL`. + If you need customize the url and/or cache key, use `ImageResource` instead. + */ +extension URL: Resource { + public var cacheKey: String { return absoluteString } + public var downloadURL: URL { return self } +} diff --git a/Example/Pods/Kingfisher/Sources/String+MD5.swift b/Example/Pods/Kingfisher/Sources/String+MD5.swift new file mode 100755 index 0000000..8d601b2 --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/String+MD5.swift @@ -0,0 +1,273 @@ +// +// String+MD5.swift +// Kingfisher +// +// To date, adding CommonCrypto to a Swift framework is problematic. See: +// http://stackoverflow.com/questions/25248598/importing-commoncrypto-in-a-swift-framework +// We're using a subset and modified version of CryptoSwift as an alternative. +// The following is an altered source version that only includes MD5. The original software can be found at: +// https://github.com/krzyzanowskim/CryptoSwift +// This is the original copyright notice: + +/* +Copyright (C) 2014 Marcin Krzyżanowski +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +- The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +- Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +- This notice may not be removed or altered from any source or binary distribution. +*/ + +import Foundation + +extension String { + var kf_MD5: String { + if let data = data(using: .utf8) { + + let message = data.withUnsafeBytes { bytes -> [UInt8] in + return Array(UnsafeBufferPointer(start: bytes, count: data.count)) + } + + let MD5Calculator = MD5(message) + let MD5Data = MD5Calculator.calculate() + + let MD5String = NSMutableString() + for c in MD5Data { + MD5String.appendFormat("%02x", c) + } + return MD5String as String + + } else { + return self + } + } +} + +/** array of bytes, little-endian representation */ +func arrayOfBytes(_ value: T, length: Int? = nil) -> [UInt8] { + let totalBytes = length ?? (MemoryLayout.size * 8) + + let valuePointer = UnsafeMutablePointer.allocate(capacity: 1) + valuePointer.pointee = value + + let bytes = valuePointer.withMemoryRebound(to: UInt8.self, capacity: totalBytes) { (bytesPointer) -> [UInt8] in + var bytes = [UInt8](repeating: 0, count: totalBytes) + for j in 0...size, totalBytes) { + bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee + } + return bytes + } + + valuePointer.deinitialize() + valuePointer.deallocate(capacity: 1) + + return bytes +} + +extension Int { + /** Array of bytes with optional padding (little-endian) */ + func bytes(_ totalBytes: Int = MemoryLayout.size) -> [UInt8] { + return arrayOfBytes(self, length: totalBytes) + } + +} + +extension NSMutableData { + + /** Convenient way to append bytes */ + func appendBytes(_ arrayOfBytes: [UInt8]) { + append(arrayOfBytes, length: arrayOfBytes.count) + } + +} + +protocol HashProtocol { + var message: Array { get } + + /** Common part for hash calculation. Prepare header data. */ + func prepare(_ len: Int) -> Array +} + +extension HashProtocol { + + func prepare(_ len: Int) -> Array { + var tmpMessage = message + + // Step 1. Append Padding Bits + tmpMessage.append(0x80) // append one bit (UInt8 with one bit) to message + + // append "0" bit until message length in bits ≡ 448 (mod 512) + var msgLength = tmpMessage.count + var counter = 0 + + while msgLength % len != (len - 8) { + counter += 1 + msgLength += 1 + } + + tmpMessage += Array(repeating: 0, count: counter) + return tmpMessage + } +} + +func toUInt32Array(_ slice: ArraySlice) -> Array { + var result = Array() + result.reserveCapacity(16) + + for idx in stride(from: slice.startIndex, to: slice.endIndex, by: MemoryLayout.size) { + let d0 = UInt32(slice[idx.advanced(by: 3)]) << 24 + let d1 = UInt32(slice[idx.advanced(by: 2)]) << 16 + let d2 = UInt32(slice[idx.advanced(by: 1)]) << 8 + let d3 = UInt32(slice[idx]) + let val: UInt32 = d0 | d1 | d2 | d3 + + result.append(val) + } + return result +} + +struct BytesIterator: IteratorProtocol { + + let chunkSize: Int + let data: [UInt8] + + init(chunkSize: Int, data: [UInt8]) { + self.chunkSize = chunkSize + self.data = data + } + + var offset = 0 + + mutating func next() -> ArraySlice? { + let end = min(chunkSize, data.count - offset) + let result = data[offset.. 0 ? result : nil + } +} + +struct BytesSequence: Sequence { + let chunkSize: Int + let data: [UInt8] + + func makeIterator() -> BytesIterator { + return BytesIterator(chunkSize: chunkSize, data: data) + } +} + +func rotateLeft(_ value: UInt32, bits: UInt32) -> UInt32 { + return ((value << bits) & 0xFFFFFFFF) | (value >> (32 - bits)) +} + +class MD5: HashProtocol { + + static let size = 16 // 128 / 8 + let message: [UInt8] + + init (_ message: [UInt8]) { + self.message = message + } + + /** specifies the per-round shift amounts */ + private let shifts: [UInt32] = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21] + + /** binary integer part of the sines of integers (Radians) */ + private let sines: [UInt32] = [0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391] + + private let hashes: [UInt32] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476] + + func calculate() -> [UInt8] { + var tmpMessage = prepare(64) + tmpMessage.reserveCapacity(tmpMessage.count + 4) + + // hash values + var hh = hashes + + // Step 2. Append Length a 64-bit representation of lengthInBits + let lengthInBits = (message.count * 8) + let lengthBytes = lengthInBits.bytes(64 / 8) + tmpMessage += lengthBytes.reversed() + + // Process the message in successive 512-bit chunks: + let chunkSizeBytes = 512 / 8 // 64 + + for chunk in BytesSequence(chunkSize: chunkSizeBytes, data: tmpMessage) { + // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15 + var M = toUInt32Array(chunk) + assert(M.count == 16, "Invalid array") + + // Initialize hash value for this chunk: + var A: UInt32 = hh[0] + var B: UInt32 = hh[1] + var C: UInt32 = hh[2] + var D: UInt32 = hh[3] + + var dTemp: UInt32 = 0 + + // Main loop + for j in 0 ..< sines.count { + var g = 0 + var F: UInt32 = 0 + + switch j { + case 0...15: + F = (B & C) | ((~B) & D) + g = j + break + case 16...31: + F = (D & B) | (~D & C) + g = (5 * j + 1) % 16 + break + case 32...47: + F = B ^ C ^ D + g = (3 * j + 5) % 16 + break + case 48...63: + F = C ^ (B | (~D)) + g = (7 * j) % 16 + break + default: + break + } + dTemp = D + D = C + C = B + B = B &+ rotateLeft((A &+ F &+ sines[j] &+ M[g]), bits: shifts[j]) + A = dTemp + } + + hh[0] = hh[0] &+ A + hh[1] = hh[1] &+ B + hh[2] = hh[2] &+ C + hh[3] = hh[3] &+ D + } + + var result = [UInt8]() + result.reserveCapacity(hh.count / 4) + + hh.forEach { + let itemLE = $0.littleEndian + result += [UInt8(itemLE & 0xff), UInt8((itemLE >> 8) & 0xff), UInt8((itemLE >> 16) & 0xff), UInt8((itemLE >> 24) & 0xff)] + } + return result + } +} diff --git a/Example/Pods/Kingfisher/Sources/ThreadHelper.swift b/Example/Pods/Kingfisher/Sources/ThreadHelper.swift new file mode 100755 index 0000000..afb3c93 --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/ThreadHelper.swift @@ -0,0 +1,40 @@ +// +// ThreadHelper.swift +// Kingfisher +// +// Created by Wei Wang on 15/10/9. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +extension DispatchQueue { + // This method will dispatch the `block` to self. + // If `self` is the main queue, and current thread is main thread, the block + // will be invoked immediately instead of being dispatched. + func safeAsync(_ block: @escaping ()->()) { + if self === DispatchQueue.main && Thread.isMainThread { + block() + } else { + async { block() } + } + } +} diff --git a/Example/Pods/Kingfisher/Sources/UIButton+Kingfisher.swift b/Example/Pods/Kingfisher/Sources/UIButton+Kingfisher.swift new file mode 100755 index 0000000..882e5cc --- /dev/null +++ b/Example/Pods/Kingfisher/Sources/UIButton+Kingfisher.swift @@ -0,0 +1,254 @@ +// +// UIButton+Kingfisher.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/13. +// +// Copyright (c) 2016 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +/** +* Set image to use from web for a specified state. +*/ +extension UIButton { + /** + Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and completion handler. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter state: The state that uses the specified image. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. + */ + @discardableResult + public func kf_setImage(with resource: Resource?, + for state: UIControlState, + placeholder: UIImage? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + { + setImage(placeholder, for: state) + + guard let resource = resource else { + completionHandler?(nil, nil, .none, nil) + return .emptyTask + } + + kf_setWebURL(resource.downloadURL, for: state) + let task = KingfisherManager.shared.retrieveImage(with: resource, options: options, + progressBlock: { receivedSize, totalSize in + if let progressBlock = progressBlock { + progressBlock(receivedSize, totalSize) + } + }, + completionHandler: {[weak self] image, error, cacheType, imageURL in + DispatchQueue.main.safeAsync { + guard let sSelf = self, imageURL == sSelf.kf_webURL(for: state) else { + return + } + + sSelf.kf_setImageTask(nil) + + if image != nil { + sSelf.setImage(image, for: state) + } + + completionHandler?(image, error, cacheType, imageURL) + } + }) + + kf_setImageTask(task) + return task + } +} + +private var lastURLKey: Void? +private var imageTaskKey: Void? + +// MARK: - Runtime for UIButton image +extension UIButton { + /** + Get the image URL binded to this button for a specified state. + + - parameter state: The state that uses the specified image. + + - returns: Current URL for image. + */ + public func kf_webURL(for state: UIControlState) -> URL? { + return kf_webURLs[NSNumber(value:state.rawValue)] as? URL + } + + fileprivate func kf_setWebURL(_ url: URL, for state: UIControlState) { + kf_webURLs[NSNumber(value:state.rawValue)] = url + } + + fileprivate var kf_webURLs: NSMutableDictionary { + var dictionary = objc_getAssociatedObject(self, &lastURLKey) as? NSMutableDictionary + if dictionary == nil { + dictionary = NSMutableDictionary() + kf_setWebURLs(dictionary!) + } + return dictionary! + } + + fileprivate func kf_setWebURLs(_ URLs: NSMutableDictionary) { + objc_setAssociatedObject(self, &lastURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + + fileprivate var kf_imageTask: RetrieveImageTask? { + return objc_getAssociatedObject(self, &imageTaskKey) as? RetrieveImageTask + } + + fileprivate func kf_setImageTask(_ task: RetrieveImageTask?) { + objc_setAssociatedObject(self, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } +} + +/** +* Set background image to use from web for a specified state. +*/ +extension UIButton { + /** + Set the background image to use for a specified state with a resource, + a placeholder image, options progress handler and completion handler. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter state: The state that uses the specified image. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. + */ + @discardableResult + public func kf_setBackgroundImage(with resource: Resource?, + for state: UIControlState, + placeholder: UIImage? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + { + setBackgroundImage(placeholder, for: state) + + guard let resource = resource else { + completionHandler?(nil, nil, .none, nil) + return .emptyTask + } + + kf_setBackgroundWebURL(resource.downloadURL, for: state) + let task = KingfisherManager.shared.retrieveImage(with: resource, options: options, + progressBlock: { receivedSize, totalSize in + if let progressBlock = progressBlock { + progressBlock(receivedSize, totalSize) + } + }, + completionHandler: { [weak self] image, error, cacheType, imageURL in + DispatchQueue.main.safeAsync { + guard let sSelf = self, imageURL == sSelf.kf_backgroundWebURL(for: state) else { + return + } + + sSelf.kf_setBackgroundImageTask(nil) + + if image != nil { + sSelf.setBackgroundImage(image, for: state) + } + completionHandler?(image, error, cacheType, imageURL) + } + }) + + kf_setBackgroundImageTask(task) + return task + } +} + +private var lastBackgroundURLKey: Void? +private var backgroundImageTaskKey: Void? + +// MARK: - Runtime for UIButton background image +extension UIButton { + /** + Get the background image URL binded to this button for a specified state. + + - parameter state: The state that uses the specified background image. + + - returns: Current URL for background image. + */ + public func kf_backgroundWebURL(for state: UIControlState) -> URL? { + return kf_backgroundWebURLs[NSNumber(value:state.rawValue)] as? URL + } + + fileprivate func kf_setBackgroundWebURL(_ url: URL, for state: UIControlState) { + kf_backgroundWebURLs[NSNumber(value:state.rawValue)] = url + } + + fileprivate var kf_backgroundWebURLs: NSMutableDictionary { + var dictionary = objc_getAssociatedObject(self, &lastBackgroundURLKey) as? NSMutableDictionary + if dictionary == nil { + dictionary = NSMutableDictionary() + kf_setBackgroundWebURLs(dictionary!) + } + return dictionary! + } + + fileprivate func kf_setBackgroundWebURLs(_ URLs: NSMutableDictionary) { + objc_setAssociatedObject(self, &lastBackgroundURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + + fileprivate var kf_backgroundImageTask: RetrieveImageTask? { + return objc_getAssociatedObject(self, &backgroundImageTaskKey) as? RetrieveImageTask + } + + fileprivate func kf_setBackgroundImageTask(_ task: RetrieveImageTask?) { + objc_setAssociatedObject(self, &backgroundImageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } +} + +// MARK: - Cancel image download tasks. +extension UIButton { + /** + Cancel the image download task bounded to the image view if it is running. + Nothing will happen if the downloading has already finished. + */ + public func kf_cancelImageDownloadTask() { + kf_imageTask?.downloadTask?.cancel() + } + + /** + Cancel the background image download task bounded to the image view if it is running. + Nothing will happen if the downloading has already finished. + */ + public func kf_cancelBackgroundImageDownloadTask() { + kf_backgroundImageTask?.downloadTask?.cancel() + } +} diff --git a/Example/Pods/Local Podspecs/Kingfisher.podspec.json b/Example/Pods/Local Podspecs/Kingfisher.podspec.json new file mode 100644 index 0000000..e23e1d8 --- /dev/null +++ b/Example/Pods/Local Podspecs/Kingfisher.podspec.json @@ -0,0 +1,55 @@ +{ + "name": "Kingfisher", + "version": "2.5.0", + "summary": "A lightweight and pure Swift implemented library for downloading and cacheing image from the web.", + "description": "Kingfisher is a lightweight and pure Swift implemented library for downloading and cacheing image from the web. It provides you a chance to use pure Swift alternation in your next app.\n\n* Everything in Kingfisher goes asynchronously, not only downloading, but also caching. That means you can never worry about blocking your UI thread.\n* Multiple-layer cache. Downloaded image will be cached in both memory and disk. So there is no need to download it again and this could boost your app dramatically.\n* Cache management. You can set the max duration or size the cache could take. And the cache will also be cleaned automatically to prevent taking too much resource.\n* Modern framework. Kingfisher uses `NSURLSession` and the latest technology of GCD, which makes it a strong and swift framework. It also provides you easy APIs to use.\n* Cancellable processing task. You can cancel the downloading or retriving image process if it is not needed anymore.\n* Independent components. You can use the downloader or caching system separately. Or even create your own cache based on Kingfisher's code.\n* Options to decompress the image in background before render it, which could improve the UI performance.\n* A category over `UIImageView` for setting image from an url directly.", + "homepage": "https://github.com/onevcat/Kingfisher", + "screenshots": "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/logo.png", + "license": { + "type": "MIT", + "file": "LICENSE" + }, + "authors": { + "onevcat": "onevcat@gmail.com" + }, + "social_media_url": "http://twitter.com/onevcat", + "platforms": { + "ios": "8.0", + "tvos": "9.0", + "osx": "10.10", + "watchos": "2.0" + }, + "source": { + "git": "https://github.com/onevcat/Kingfisher.git", + "tag": "2.5.0" + }, + "source_files": [ + "Sources/*.swift", + "Sources/Kingfisher.h" + ], + "public_header_files": [ + "Sources/Kingfisher.h" + ], + "osx": { + "exclude_files": [ + "Sources/AnimatedImageView.swift", + "Sources/UIButton+Kingfisher.swift" + ] + }, + "watchos": { + "exclude_files": [ + "Sources/AnimatedImageView.swift", + "Sources/UIButton+Kingfisher.swift", + "Sources/ImageView+Kingfisher.swift", + "Sources/NSButton+Kingfisher.swift" + ] + }, + "ios": { + "exclude_files": "Sources/NSButton+Kingfisher.swift" + }, + "tvos": { + "exclude_files": "Sources/NSButton+Kingfisher.swift" + }, + "requires_arc": true, + "frameworks": "CFNetwork" +} diff --git a/Example/Pods/Manifest.lock b/Example/Pods/Manifest.lock index 886e5bf..edf22d2 100644 --- a/Example/Pods/Manifest.lock +++ b/Example/Pods/Manifest.lock @@ -1,13 +1,25 @@ PODS: - - HanekeSwift (0.10.1) - INSNibLoading (1.0.1) + - Kingfisher (2.5.0) DEPENDENCIES: - - HanekeSwift - INSNibLoading + - Kingfisher (from `https://github.com/onevcat/Kingfisher.git`, branch `swift3`) + +EXTERNAL SOURCES: + Kingfisher: + :branch: swift3 + :git: https://github.com/onevcat/Kingfisher.git + +CHECKOUT OPTIONS: + Kingfisher: + :commit: 609445170d3736b12d01024a953ceb849bc5d071 + :git: https://github.com/onevcat/Kingfisher.git SPEC CHECKSUMS: - HanekeSwift: 08e47d3d0f2899ec58e5d257beba0de619aca9e8 INSNibLoading: 1b8d0ee31b94c124bffe2609e697e0a43117a78f + Kingfisher: d467bd710907ec9aa94aaddecaa5390aa3559819 + +PODFILE CHECKSUM: 6adeabf6c63ce164ecc944fc6b75c1dceb253597 -COCOAPODS: 0.39.0 +COCOAPODS: 1.0.1 diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index 510d009..658810d 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -1,2000 +1,762 @@ - - - - - archiveVersion - 1 - classes - - objectVersion - 46 - objects - - 036AE6521A6E7996722352A089508A1C - - fileRef - 894E5DA93A9F359521A89826BE6DA777 - isa - PBXBuildFile - - 03E7C268925811617693272D9D15FAEF - - children - - 5A5134CECD97307F3244468DE9FDAC22 - CFD370D53623ACC5E8AB54AEC3F1E2A2 - - isa - PBXGroup - name - Pods - sourceTree - <group> - - 0A4892FAEC68B96EC90BE6E8BE0F5E6A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - INSNibLoading-umbrella.h - sourceTree - <group> - - 0BFDE8332EDC136EB44F2932F239F6A9 - - fileRef - 86FED528491DBD0EACBAD6C3476695D9 - isa - PBXBuildFile - - 0D17A4436138A9C9BF411303B0759829 - - fileRef - 9A2F512B3347AF71CC10F56F2F22F7BA - isa - PBXBuildFile - - 0D58CEC6BC851FA89D18EE5750E1069B - - fileRef - 3E4E89230EF59BC255123B67864ACF77 - isa - PBXBuildFile - - 0D75820A6280DAE2735D774E720B309B - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - HanekeSwift.xcconfig - sourceTree - <group> - - 0E01E74BF92665F0DAF158E3A9B98129 - - fileRef - B3FB686D54C8770C3E80C4C9940B23F1 - isa - PBXBuildFile - - 0EB26E42DDF8F16719A6522CCC717EA3 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - Log.swift - path - Haneke/Log.swift - sourceTree - <group> - - 106418B0ECD90946326D9F121CA56CC1 - - baseConfigurationReference - 787E3BD9CE47EAB1607068B3DE4329CA - buildSettings - - CODE_SIGN_IDENTITY[sdk=iphoneos*] - iPhone Developer - CURRENT_PROJECT_VERSION - 1 - DEFINES_MODULE - YES - DYLIB_COMPATIBILITY_VERSION - 1 - DYLIB_CURRENT_VERSION - 1 - DYLIB_INSTALL_NAME_BASE - @rpath - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/INSNibLoading/INSNibLoading-prefix.pch - INFOPLIST_FILE - Target Support Files/INSNibLoading/Info.plist - INSTALL_PATH - $(LOCAL_LIBRARY_DIR)/Frameworks - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - LD_RUNPATH_SEARCH_PATHS - - $(inherited) - @executable_path/Frameworks - @loader_path/Frameworks - - MODULEMAP_FILE - Target Support Files/INSNibLoading/INSNibLoading.modulemap - MTL_ENABLE_DEBUG_INFO - YES - PRODUCT_NAME - INSNibLoading - SDKROOT - iphoneos - SKIP_INSTALL - YES - TARGETED_DEVICE_FAMILY - 1,2 - VERSIONING_SYSTEM - apple-generic - VERSION_INFO_PREFIX - - - isa - XCBuildConfiguration - name - Debug - - 1A31E27B4DD6C1248B1E2504DC7EE788 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - NetworkFetcher.swift - path - Haneke/NetworkFetcher.swift - sourceTree - <group> - - 1BCF69A7C7225BE2B5BFC4D6249CAD4A - - isa - PBXTargetDependency - name - INSNibLoading - target - 5C8612EDF3EA754CD9BAC5E4397905D3 - targetProxy - A2AE56910517F71CB1A7986678CE45C8 - - 1C32E403CB75EC53CC1930851CF84C8A - - fileRef - 62EF49273DA347A1759C56D3D2445F3B - isa - PBXBuildFile - - 1FACCC5FAA5D24EECA5CA3718AFF61CB - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - HanekeSwift-prefix.pch - sourceTree - <group> - - 206F03E05EF5256A339B6D503D59FA44 - - buildActionMask - 2147483647 - files - - F1617C553638EBD86F64E16166E39730 - - isa - PBXHeadersBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 2167433F2EE38BF01220E327ED8FAE40 - - fileRef - CEF13AA119A18A221FE363936DED8749 - isa - PBXBuildFile - - 25367612FB0EF744CBFEF979453E7239 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - HanekeSwift-umbrella.h - sourceTree - <group> - - 270520E8E72E32AC313AE621D8A080C3 - - fileRef - 2BF25525AA6BFE3AF5F080F4E41BB4BB - isa - PBXBuildFile - - 2B4704ADC002E4A78992966294106BF4 - - children - - 74BBFE93DB83FBA66DF016C7B7F948EC - A38253004DAFE684B5A5BA0268264E5D - 787E3BD9CE47EAB1607068B3DE4329CA - 37BC79A5C688B7212000FBD4BD0D7D16 - AEB47269039E3CDE9FA425B746DA2E2F - 0A4892FAEC68B96EC90BE6E8BE0F5E6A - - isa - PBXGroup - name - Support Files - path - ../Target Support Files/INSNibLoading - sourceTree - <group> - - 2BCC458FDD5F692BBB2BFC64BB5701FC - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - Pods-umbrella.h - sourceTree - <group> - - 2BF25525AA6BFE3AF5F080F4E41BB4BB - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - Fetch.swift - path - Haneke/Fetch.swift - sourceTree - <group> - - 2D8E8EC45A3A1A1D94AE762CB5028504 - - buildConfigurations - - A70CDAD61F90AC503C7D04CC22DA2923 - FB45FFD90572718D82AB9092B750F0CA - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 317EA1F4BB8017E3808A7A4875883225 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIView+INSNibLoading.m - path - INSNibLoading/iOS/UIView+INSNibLoading.m - sourceTree - <group> - - 31E488CFE2EB86809B81BC39E2DEF646 - - fileRef - 5E28364EF21F3837AAA767D1751360D5 - isa - PBXBuildFile - - 347C12AB3EB381A1AC423D665075FD94 - - fileRef - 8511933E3AC3BBD838E51A38068DD5A1 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 37BC79A5C688B7212000FBD4BD0D7D16 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - INSNibLoading-dummy.m - sourceTree - <group> - - 3E4E89230EF59BC255123B67864ACF77 - - isa - PBXFileReference - lastKnownFileType - wrapper.framework - name - Foundation.framework - path - Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/System/Library/Frameworks/Foundation.framework - sourceTree - DEVELOPER_DIR - - 40242B213FD0EB51BD708466C0C55F5B - - isa - PBXTargetDependency - name - HanekeSwift - target - 7DDF79495F24B309AF089ACC99FC1151 - targetProxy - 8F58AB06A791767B586BE05C5D4DC491 - - 43F505F90B66732DA6C1C96FF43DC5FC - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - Fetcher.swift - path - Haneke/Fetcher.swift - sourceTree - <group> - - 4831E083141EA7E4F468D48758928823 - - buildActionMask - 2147483647 - files - - 9A619EACE2B3D09E9C9F4F95D94AA63B - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 488605BD51C85EEA920BE3C7F624842F - - fileRef - A5BF0C82F91912F2BA11ACE085C51E2C - isa - PBXBuildFile - - 4FB7C0C8B5DF61CEA0D21B1E37E70B71 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - CryptoSwiftMD5.swift - path - Haneke/CryptoSwiftMD5.swift - sourceTree - <group> - - 5089FF874E7D848AC09E2B071B772DD7 - - fileRef - 2BCC458FDD5F692BBB2BFC64BB5701FC - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 512D00F3573C77B5281B5A7636B856AB - - explicitFileType - wrapper.framework - includeInIndex - 0 - isa - PBXFileReference - name - INSNibLoading.framework - path - INSNibLoading.framework - sourceTree - BUILT_PRODUCTS_DIR - - 526726400C7D6582C5DA6A6BFAF689F0 - - fileRef - F55B76F0C24D686AA477C950D6B1DF23 - isa - PBXBuildFile - - 55BE6C6283ABBC9AFF1B17C6DD1CC27D - - fileRef - 37BC79A5C688B7212000FBD4BD0D7D16 - isa - PBXBuildFile - - 574734F51A36702D1E4C089CCB196D88 - - buildActionMask - 2147483647 - files - - 036AE6521A6E7996722352A089508A1C - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 582C2D15084E9631A65B53276C970BE9 - - buildActionMask - 2147483647 - files - - 5A0176C0FD391DBB1001F5989831805A - 347C12AB3EB381A1AC423D665075FD94 - - isa - PBXHeadersBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 587E85913CE1EA4777BAA6B78B7D05DD - - baseConfigurationReference - 787E3BD9CE47EAB1607068B3DE4329CA - buildSettings - - CODE_SIGN_IDENTITY[sdk=iphoneos*] - iPhone Developer - CURRENT_PROJECT_VERSION - 1 - DEFINES_MODULE - YES - DYLIB_COMPATIBILITY_VERSION - 1 - DYLIB_CURRENT_VERSION - 1 - DYLIB_INSTALL_NAME_BASE - @rpath - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/INSNibLoading/INSNibLoading-prefix.pch - INFOPLIST_FILE - Target Support Files/INSNibLoading/Info.plist - INSTALL_PATH - $(LOCAL_LIBRARY_DIR)/Frameworks - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - LD_RUNPATH_SEARCH_PATHS - - $(inherited) - @executable_path/Frameworks - @loader_path/Frameworks - - MODULEMAP_FILE - Target Support Files/INSNibLoading/INSNibLoading.modulemap - MTL_ENABLE_DEBUG_INFO - NO - PRODUCT_NAME - INSNibLoading - SDKROOT - iphoneos - SKIP_INSTALL - YES - TARGETED_DEVICE_FAMILY - 1,2 - VERSIONING_SYSTEM - apple-generic - VERSION_INFO_PREFIX - - - isa - XCBuildConfiguration - name - Release - - 589256624065D6CFD4B4D2387DC8E43A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - Haneke.swift - path - Haneke/Haneke.swift - sourceTree - <group> - - 59120F346F4C2F8ADA8BED6A172B94E4 - - fileRef - 43F505F90B66732DA6C1C96FF43DC5FC - isa - PBXBuildFile - - 5A0176C0FD391DBB1001F5989831805A - - fileRef - 0A4892FAEC68B96EC90BE6E8BE0F5E6A - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 5A5134CECD97307F3244468DE9FDAC22 - - children - - 6749D8C8D1166DDEBD371524D8609F3F - 62EF49273DA347A1759C56D3D2445F3B - 4FB7C0C8B5DF61CEA0D21B1E37E70B71 - CEF13AA119A18A221FE363936DED8749 - 5E28364EF21F3837AAA767D1751360D5 - 9A2F512B3347AF71CC10F56F2F22F7BA - 2BF25525AA6BFE3AF5F080F4E41BB4BB - 43F505F90B66732DA6C1C96FF43DC5FC - B3FB686D54C8770C3E80C4C9940B23F1 - 589256624065D6CFD4B4D2387DC8E43A - 0EB26E42DDF8F16719A6522CCC717EA3 - 1A31E27B4DD6C1248B1E2504DC7EE788 - A033E1B23C9D90A551FFED8A185179E7 - 86FED528491DBD0EACBAD6C3476695D9 - A2A133D89DE3BC5EAC12EC00B4D7F6EE - B15F5C661E0C8902258269132EB4117F - 68931E4F96116CEA88102D72089D34D4 - DE71E59CA28E4A13EBBAFAB6E1557F83 - A5BF0C82F91912F2BA11ACE085C51E2C - F55B76F0C24D686AA477C950D6B1DF23 - F1D13D63977120C07946F67B5DF33788 - - isa - PBXGroup - name - HanekeSwift - path - HanekeSwift - sourceTree - <group> - - 5B83C2AD137CF6C7F4C5DBE1397C3390 - - buildConfigurations - - 74B95146461851ACA46CDFFF21AA98DE - D2EB73775E7DBB2BABBB45199E0881EA - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 5C8612EDF3EA754CD9BAC5E4397905D3 - - buildConfigurationList - 82D9DFF399E3ED68419FFDFABBA5A3CC - buildPhases - - 917F2D45717678F5EE29572B94799FFD - 4831E083141EA7E4F468D48758928823 - 582C2D15084E9631A65B53276C970BE9 - - buildRules - - dependencies - - isa - PBXNativeTarget - name - INSNibLoading - productName - INSNibLoading - productReference - 512D00F3573C77B5281B5A7636B856AB - productType - com.apple.product-type.framework - - 5E28364EF21F3837AAA767D1751360D5 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - DiskCache.swift - path - Haneke/DiskCache.swift - sourceTree - <group> - - 62EF49273DA347A1759C56D3D2445F3B - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - CGSize+Swift.swift - path - Haneke/CGSize+Swift.swift - sourceTree - <group> - - 6749D8C8D1166DDEBD371524D8609F3F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - Cache.swift - path - Haneke/Cache.swift - sourceTree - <group> - - 68931E4F96116CEA88102D72089D34D4 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - UIButton+Haneke.swift - path - Haneke/UIButton+Haneke.swift - sourceTree - <group> - - 74B95146461851ACA46CDFFF21AA98DE - - baseConfigurationReference - 0D75820A6280DAE2735D774E720B309B - buildSettings - - CODE_SIGN_IDENTITY[sdk=iphoneos*] - iPhone Developer - CURRENT_PROJECT_VERSION - 1 - DEFINES_MODULE - YES - DYLIB_COMPATIBILITY_VERSION - 1 - DYLIB_CURRENT_VERSION - 1 - DYLIB_INSTALL_NAME_BASE - @rpath - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/HanekeSwift/HanekeSwift-prefix.pch - INFOPLIST_FILE - Target Support Files/HanekeSwift/Info.plist - INSTALL_PATH - $(LOCAL_LIBRARY_DIR)/Frameworks - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - LD_RUNPATH_SEARCH_PATHS - - $(inherited) - @executable_path/Frameworks - @loader_path/Frameworks - - MODULEMAP_FILE - Target Support Files/HanekeSwift/HanekeSwift.modulemap - MTL_ENABLE_DEBUG_INFO - YES - PRODUCT_NAME - Haneke - SDKROOT - iphoneos - SKIP_INSTALL - YES - SWIFT_OPTIMIZATION_LEVEL - -Onone - TARGETED_DEVICE_FAMILY - 1,2 - VERSIONING_SYSTEM - apple-generic - VERSION_INFO_PREFIX - - - isa - XCBuildConfiguration - name - Debug - - 74BBFE93DB83FBA66DF016C7B7F948EC - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.plist.xml - path - Info.plist - sourceTree - <group> - - 75D98FF52E597A11900E131B6C4E1ADA - - children - - E8446514FBAD26C0E18F24A5715AEF67 - 79A9DEDC89FE8336BF5FEDAAF75BF7FC - D0405803033A2A777B8E4DFA0C1800ED - 87B213035BAC5F75386F62D3C75D2342 - 894E5DA93A9F359521A89826BE6DA777 - E7F21354943D9F42A70697D5A5EF72E9 - CBC0F7C552B739C909B650A0F42F7F38 - 2BCC458FDD5F692BBB2BFC64BB5701FC - 977577C045EDA9D9D1F46E2598D19FC7 - DA312349A49333542E6F4B36B329960E - - isa - PBXGroup - name - Pods - path - Target Support Files/Pods - sourceTree - <group> - - 7698B2669B0702DACD6580D3299BA534 - - baseConfigurationReference - 977577C045EDA9D9D1F46E2598D19FC7 - buildSettings - - CODE_SIGN_IDENTITY[sdk=iphoneos*] - iPhone Developer - CURRENT_PROJECT_VERSION - 1 - DEFINES_MODULE - YES - DYLIB_COMPATIBILITY_VERSION - 1 - DYLIB_CURRENT_VERSION - 1 - DYLIB_INSTALL_NAME_BASE - @rpath - ENABLE_STRICT_OBJC_MSGSEND - YES - INFOPLIST_FILE - Target Support Files/Pods/Info.plist - INSTALL_PATH - $(LOCAL_LIBRARY_DIR)/Frameworks - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - LD_RUNPATH_SEARCH_PATHS - - $(inherited) - @executable_path/Frameworks - @loader_path/Frameworks - - MACH_O_TYPE - staticlib - MODULEMAP_FILE - Target Support Files/Pods/Pods.modulemap - MTL_ENABLE_DEBUG_INFO - YES - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PODS_ROOT - $(SRCROOT) - PRODUCT_NAME - Pods - SDKROOT - iphoneos - SKIP_INSTALL - YES - SWIFT_OPTIMIZATION_LEVEL - -Onone - TARGETED_DEVICE_FAMILY - 1,2 - VERSIONING_SYSTEM - apple-generic - VERSION_INFO_PREFIX - - - isa - XCBuildConfiguration - name - Debug - - 77EE457547A35B303FC8BBC8BB63E800 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - HanekeSwift-dummy.m - sourceTree - <group> - - 787E3BD9CE47EAB1607068B3DE4329CA - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - INSNibLoading.xcconfig - sourceTree - <group> - - 79A9DEDC89FE8336BF5FEDAAF75BF7FC - - includeInIndex - 1 - isa - PBXFileReference - path - Pods.modulemap - sourceTree - <group> - - 79CDBE504F2CCA180EC6D5162D54FBAD - - explicitFileType - wrapper.framework - includeInIndex - 0 - isa - PBXFileReference - name - Haneke.framework - path - Haneke.framework - sourceTree - BUILT_PRODUCTS_DIR - - 7B34FAE152A6A3BC4550DC37CC861E61 - - baseConfigurationReference - DA312349A49333542E6F4B36B329960E - buildSettings - - CODE_SIGN_IDENTITY[sdk=iphoneos*] - iPhone Developer - CURRENT_PROJECT_VERSION - 1 - DEFINES_MODULE - YES - DYLIB_COMPATIBILITY_VERSION - 1 - DYLIB_CURRENT_VERSION - 1 - DYLIB_INSTALL_NAME_BASE - @rpath - ENABLE_STRICT_OBJC_MSGSEND - YES - INFOPLIST_FILE - Target Support Files/Pods/Info.plist - INSTALL_PATH - $(LOCAL_LIBRARY_DIR)/Frameworks - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - LD_RUNPATH_SEARCH_PATHS - - $(inherited) - @executable_path/Frameworks - @loader_path/Frameworks - - MACH_O_TYPE - staticlib - MODULEMAP_FILE - Target Support Files/Pods/Pods.modulemap - MTL_ENABLE_DEBUG_INFO - NO - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PODS_ROOT - $(SRCROOT) - PRODUCT_NAME - Pods - SDKROOT - iphoneos - SKIP_INSTALL - YES - TARGETED_DEVICE_FAMILY - 1,2 - VERSIONING_SYSTEM - apple-generic - VERSION_INFO_PREFIX - - - isa - XCBuildConfiguration - name - Release - - 7DB346D0F39D3F0E887471402A8071AB - - children - - BA6428E9F66FD5A23C0A2E06ED26CD2F - BC3CA7F9E30CC8F7E2DD044DD34432FC - 03E7C268925811617693272D9D15FAEF - A0F85F575C09EB63E56A3591768BCF85 - B7B80995527643776607AFFA75B91E24 - - isa - PBXGroup - sourceTree - <group> - - 7DDF79495F24B309AF089ACC99FC1151 - - buildConfigurationList - 5B83C2AD137CF6C7F4C5DBE1397C3390 - buildPhases - - AD75B1F809159184F48646B8441799A0 - DC6F3167BAEEC168056F22481901B03E - 206F03E05EF5256A339B6D503D59FA44 - - buildRules - - dependencies - - isa - PBXNativeTarget - name - HanekeSwift - productName - HanekeSwift - productReference - 79CDBE504F2CCA180EC6D5162D54FBAD - productType - com.apple.product-type.framework - - 80537DC1037798BC187946C450CCA9BE - - fileRef - 589256624065D6CFD4B4D2387DC8E43A - isa - PBXBuildFile - - 82D9DFF399E3ED68419FFDFABBA5A3CC - - buildConfigurations - - 106418B0ECD90946326D9F121CA56CC1 - 587E85913CE1EA4777BAA6B78B7D05DD - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 8511933E3AC3BBD838E51A38068DD5A1 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIView+INSNibLoading.h - path - INSNibLoading/iOS/UIView+INSNibLoading.h - sourceTree - <group> - - 86FED528491DBD0EACBAD6C3476695D9 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - NSHTTPURLResponse+Haneke.swift - path - Haneke/NSHTTPURLResponse+Haneke.swift - sourceTree - <group> - - 87B213035BAC5F75386F62D3C75D2342 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.plist.xml - path - Pods-acknowledgements.plist - sourceTree - <group> - - 894E5DA93A9F359521A89826BE6DA777 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - Pods-dummy.m - sourceTree - <group> - - 8BFADF0B5074404EB86A5BBAEF3D8D86 - - fileRef - 6749D8C8D1166DDEBD371524D8609F3F - isa - PBXBuildFile - - 8F58AB06A791767B586BE05C5D4DC491 - - containerPortal - D41D8CD98F00B204E9800998ECF8427E - isa - PBXContainerItemProxy - proxyType - 1 - remoteGlobalIDString - 7DDF79495F24B309AF089ACC99FC1151 - remoteInfo - HanekeSwift - - 9159C6582C9367D1B12069783908DD09 - - buildActionMask - 2147483647 - files - - F37703286E27571A73398044B52463BD - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 917F2D45717678F5EE29572B94799FFD - - buildActionMask - 2147483647 - files - - 55BE6C6283ABBC9AFF1B17C6DD1CC27D - A364AAAF0282C83035286097C0BD16F5 - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 941B271BAE995C0E2591538599489B7E - - fileRef - DE71E59CA28E4A13EBBAFAB6E1557F83 - isa - PBXBuildFile - - 977577C045EDA9D9D1F46E2598D19FC7 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - Pods.debug.xcconfig - sourceTree - <group> - - 9A2F512B3347AF71CC10F56F2F22F7BA - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - DiskFetcher.swift - path - Haneke/DiskFetcher.swift - sourceTree - <group> - - 9A619EACE2B3D09E9C9F4F95D94AA63B - - fileRef - 3E4E89230EF59BC255123B67864ACF77 - isa - PBXBuildFile - - 9BFEEA1A39C1375D886C6BED990B380C - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.plist.xml - path - Info.plist - sourceTree - <group> - - A033E1B23C9D90A551FFED8A185179E7 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - NSFileManager+Haneke.swift - path - Haneke/NSFileManager+Haneke.swift - sourceTree - <group> - - A0F85F575C09EB63E56A3591768BCF85 - - children - - 79CDBE504F2CCA180EC6D5162D54FBAD - 512D00F3573C77B5281B5A7636B856AB - A98AA3B28E01EE0A785B30DC22AD15F0 - - isa - PBXGroup - name - Products - sourceTree - <group> - - A2A133D89DE3BC5EAC12EC00B4D7F6EE - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - NSURLResponse+Haneke.swift - path - Haneke/NSURLResponse+Haneke.swift - sourceTree - <group> - - A2AE56910517F71CB1A7986678CE45C8 - - containerPortal - D41D8CD98F00B204E9800998ECF8427E - isa - PBXContainerItemProxy - proxyType - 1 - remoteGlobalIDString - 5C8612EDF3EA754CD9BAC5E4397905D3 - remoteInfo - INSNibLoading - - A364AAAF0282C83035286097C0BD16F5 - - fileRef - 317EA1F4BB8017E3808A7A4875883225 - isa - PBXBuildFile - - A38253004DAFE684B5A5BA0268264E5D - - includeInIndex - 1 - isa - PBXFileReference - path - INSNibLoading.modulemap - sourceTree - <group> - - A5BF0C82F91912F2BA11ACE085C51E2C - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - UIImageView+Haneke.swift - path - Haneke/UIImageView+Haneke.swift - sourceTree - <group> - - A70CDAD61F90AC503C7D04CC22DA2923 - - buildSettings - - ALWAYS_SEARCH_USER_PATHS - NO - CLANG_CXX_LANGUAGE_STANDARD - gnu++0x - CLANG_CXX_LIBRARY - libc++ - CLANG_ENABLE_MODULES - YES - CLANG_ENABLE_OBJC_ARC - YES - CLANG_WARN_BOOL_CONVERSION - YES - CLANG_WARN_CONSTANT_CONVERSION - YES - CLANG_WARN_DIRECT_OBJC_ISA_USAGE - YES - CLANG_WARN_EMPTY_BODY - YES - CLANG_WARN_ENUM_CONVERSION - YES - CLANG_WARN_INT_CONVERSION - YES - CLANG_WARN_OBJC_ROOT_CLASS - YES - CLANG_WARN_UNREACHABLE_CODE - YES - CLANG_WARN__DUPLICATE_METHOD_MATCH - YES - COPY_PHASE_STRIP - NO - GCC_C_LANGUAGE_STANDARD - gnu99 - GCC_DYNAMIC_NO_PIC - NO - GCC_OPTIMIZATION_LEVEL - 0 - GCC_PREPROCESSOR_DEFINITIONS - - DEBUG=1 - $(inherited) - - GCC_SYMBOLS_PRIVATE_EXTERN - NO - GCC_WARN_64_TO_32_BIT_CONVERSION - YES - GCC_WARN_ABOUT_RETURN_TYPE - YES - GCC_WARN_UNDECLARED_SELECTOR - YES - GCC_WARN_UNINITIALIZED_AUTOS - YES - GCC_WARN_UNUSED_FUNCTION - YES - GCC_WARN_UNUSED_VARIABLE - YES - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - ONLY_ACTIVE_ARCH - YES - STRIP_INSTALLED_PRODUCT - NO - SYMROOT - ${SRCROOT}/../build - - isa - XCBuildConfiguration - name - Debug - - A98AA3B28E01EE0A785B30DC22AD15F0 - - explicitFileType - wrapper.framework - includeInIndex - 0 - isa - PBXFileReference - name - Pods.framework - path - Pods.framework - sourceTree - BUILT_PRODUCTS_DIR - - AD75B1F809159184F48646B8441799A0 - - buildActionMask - 2147483647 - files - - 8BFADF0B5074404EB86A5BBAEF3D8D86 - 1C32E403CB75EC53CC1930851CF84C8A - F16A2712075F599788B658E9B34EAEEA - 2167433F2EE38BF01220E327ED8FAE40 - 31E488CFE2EB86809B81BC39E2DEF646 - 0D17A4436138A9C9BF411303B0759829 - 270520E8E72E32AC313AE621D8A080C3 - 59120F346F4C2F8ADA8BED6A172B94E4 - 0E01E74BF92665F0DAF158E3A9B98129 - 80537DC1037798BC187946C450CCA9BE - DA0E79C7A898F4F5D8D62B1ADCF67241 - D172803AB7C600E0004EED188448B772 - EBC23CCE8551B2DDF458DCE761A592B3 - B5D0F9080C2735E422221399AA6892F2 - 0BFDE8332EDC136EB44F2932F239F6A9 - FE67C22A54CDF4D547B37B544EB3C596 - C37FCA491E7E9D3CEFDC71E6514AD176 - E5F59EE78A9F139EA2502EEA67B17F85 - 941B271BAE995C0E2591538599489B7E - 488605BD51C85EEA920BE3C7F624842F - 526726400C7D6582C5DA6A6BFAF689F0 - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - AEB47269039E3CDE9FA425B746DA2E2F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - INSNibLoading-prefix.pch - sourceTree - <group> - - AF7A58F6AC515A169BB260AC41C84049 - - buildActionMask - 2147483647 - files - - 5089FF874E7D848AC09E2B071B772DD7 - - isa - PBXHeadersBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - AFB5BDE0F2CA01F2DC1084BAC3519FCF - - includeInIndex - 1 - isa - PBXFileReference - path - HanekeSwift.modulemap - sourceTree - <group> - - B0B66B9CFC4A8445C6C184F3FE42D1EA - - buildConfigurations - - 7698B2669B0702DACD6580D3299BA534 - 7B34FAE152A6A3BC4550DC37CC861E61 - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - B15F5C661E0C8902258269132EB4117F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - String+Haneke.swift - path - Haneke/String+Haneke.swift - sourceTree - <group> - - B3FB686D54C8770C3E80C4C9940B23F1 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - Format.swift - path - Haneke/Format.swift - sourceTree - <group> - - B5D0F9080C2735E422221399AA6892F2 - - fileRef - A033E1B23C9D90A551FFED8A185179E7 - isa - PBXBuildFile - - B7B80995527643776607AFFA75B91E24 - - children - - 75D98FF52E597A11900E131B6C4E1ADA - - isa - PBXGroup - name - Targets Support Files - sourceTree - <group> - - BA6428E9F66FD5A23C0A2E06ED26CD2F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text - name - Podfile - path - ../Podfile - sourceTree - SOURCE_ROOT - xcLanguageSpecificationIdentifier - xcode.lang.ruby - - BC3CA7F9E30CC8F7E2DD044DD34432FC - - children - - BF6342C8B29F4CEEA088EFF7AB4DE362 - - isa - PBXGroup - name - Frameworks - sourceTree - <group> - - BF6342C8B29F4CEEA088EFF7AB4DE362 - - children - - 3E4E89230EF59BC255123B67864ACF77 - - isa - PBXGroup - name - iOS - sourceTree - <group> - - C37FCA491E7E9D3CEFDC71E6514AD176 - - fileRef - B15F5C661E0C8902258269132EB4117F - isa - PBXBuildFile - - CBC0F7C552B739C909B650A0F42F7F38 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.script.sh - path - Pods-resources.sh - sourceTree - <group> - - CEF13AA119A18A221FE363936DED8749 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - Data.swift - path - Haneke/Data.swift - sourceTree - <group> - - CFD370D53623ACC5E8AB54AEC3F1E2A2 - - children - - 8511933E3AC3BBD838E51A38068DD5A1 - 317EA1F4BB8017E3808A7A4875883225 - 2B4704ADC002E4A78992966294106BF4 - - isa - PBXGroup - name - INSNibLoading - path - INSNibLoading - sourceTree - <group> - - D0405803033A2A777B8E4DFA0C1800ED - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text - path - Pods-acknowledgements.markdown - sourceTree - <group> - - D172803AB7C600E0004EED188448B772 - - fileRef - 0EB26E42DDF8F16719A6522CCC717EA3 - isa - PBXBuildFile - - D2EB73775E7DBB2BABBB45199E0881EA - - baseConfigurationReference - 0D75820A6280DAE2735D774E720B309B - buildSettings - - CODE_SIGN_IDENTITY[sdk=iphoneos*] - iPhone Developer - CURRENT_PROJECT_VERSION - 1 - DEFINES_MODULE - YES - DYLIB_COMPATIBILITY_VERSION - 1 - DYLIB_CURRENT_VERSION - 1 - DYLIB_INSTALL_NAME_BASE - @rpath - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/HanekeSwift/HanekeSwift-prefix.pch - INFOPLIST_FILE - Target Support Files/HanekeSwift/Info.plist - INSTALL_PATH - $(LOCAL_LIBRARY_DIR)/Frameworks - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - LD_RUNPATH_SEARCH_PATHS - - $(inherited) - @executable_path/Frameworks - @loader_path/Frameworks - - MODULEMAP_FILE - Target Support Files/HanekeSwift/HanekeSwift.modulemap - MTL_ENABLE_DEBUG_INFO - NO - PRODUCT_NAME - Haneke - SDKROOT - iphoneos - SKIP_INSTALL - YES - TARGETED_DEVICE_FAMILY - 1,2 - VERSIONING_SYSTEM - apple-generic - VERSION_INFO_PREFIX - - - isa - XCBuildConfiguration - name - Release - - D41D8CD98F00B204E9800998ECF8427E - - attributes - - LastSwiftUpdateCheck - 0700 - LastUpgradeCheck - 0700 - - buildConfigurationList - 2D8E8EC45A3A1A1D94AE762CB5028504 - compatibilityVersion - Xcode 3.2 - developmentRegion - English - hasScannedForEncodings - 0 - isa - PBXProject - knownRegions - - en - - mainGroup - 7DB346D0F39D3F0E887471402A8071AB - productRefGroup - A0F85F575C09EB63E56A3591768BCF85 - projectDirPath - - projectReferences - - projectRoot - - targets - - 7DDF79495F24B309AF089ACC99FC1151 - 5C8612EDF3EA754CD9BAC5E4397905D3 - F0BF35A84D99FCF5237D104E4688F00E - - - DA0E79C7A898F4F5D8D62B1ADCF67241 - - fileRef - 77EE457547A35B303FC8BBC8BB63E800 - isa - PBXBuildFile - - DA312349A49333542E6F4B36B329960E - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - Pods.release.xcconfig - sourceTree - <group> - - DC6F3167BAEEC168056F22481901B03E - - buildActionMask - 2147483647 - files - - 0D58CEC6BC851FA89D18EE5750E1069B - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - DE71E59CA28E4A13EBBAFAB6E1557F83 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - UIImage+Haneke.swift - path - Haneke/UIImage+Haneke.swift - sourceTree - <group> - - E5F59EE78A9F139EA2502EEA67B17F85 - - fileRef - 68931E4F96116CEA88102D72089D34D4 - isa - PBXBuildFile - - E7F21354943D9F42A70697D5A5EF72E9 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.script.sh - path - Pods-frameworks.sh - sourceTree - <group> - - E8446514FBAD26C0E18F24A5715AEF67 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.plist.xml - path - Info.plist - sourceTree - <group> - - EBC23CCE8551B2DDF458DCE761A592B3 - - fileRef - 1A31E27B4DD6C1248B1E2504DC7EE788 - isa - PBXBuildFile - - F0BF35A84D99FCF5237D104E4688F00E - - buildConfigurationList - B0B66B9CFC4A8445C6C184F3FE42D1EA - buildPhases - - 574734F51A36702D1E4C089CCB196D88 - 9159C6582C9367D1B12069783908DD09 - AF7A58F6AC515A169BB260AC41C84049 - - buildRules - - dependencies - - 40242B213FD0EB51BD708466C0C55F5B - 1BCF69A7C7225BE2B5BFC4D6249CAD4A - - isa - PBXNativeTarget - name - Pods - productName - Pods - productReference - A98AA3B28E01EE0A785B30DC22AD15F0 - productType - com.apple.product-type.framework - - F1617C553638EBD86F64E16166E39730 - - fileRef - 25367612FB0EF744CBFEF979453E7239 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - F16A2712075F599788B658E9B34EAEEA - - fileRef - 4FB7C0C8B5DF61CEA0D21B1E37E70B71 - isa - PBXBuildFile - - F1D13D63977120C07946F67B5DF33788 - - children - - AFB5BDE0F2CA01F2DC1084BAC3519FCF - 0D75820A6280DAE2735D774E720B309B - 77EE457547A35B303FC8BBC8BB63E800 - 1FACCC5FAA5D24EECA5CA3718AFF61CB - 25367612FB0EF744CBFEF979453E7239 - 9BFEEA1A39C1375D886C6BED990B380C - - isa - PBXGroup - name - Support Files - path - ../Target Support Files/HanekeSwift - sourceTree - <group> - - F37703286E27571A73398044B52463BD - - fileRef - 3E4E89230EF59BC255123B67864ACF77 - isa - PBXBuildFile - - F55B76F0C24D686AA477C950D6B1DF23 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - name - UIView+Haneke.swift - path - Haneke/UIView+Haneke.swift - sourceTree - <group> - - FB45FFD90572718D82AB9092B750F0CA - - buildSettings - - ALWAYS_SEARCH_USER_PATHS - NO - CLANG_CXX_LANGUAGE_STANDARD - gnu++0x - CLANG_CXX_LIBRARY - libc++ - CLANG_ENABLE_MODULES - YES - CLANG_ENABLE_OBJC_ARC - YES - CLANG_WARN_BOOL_CONVERSION - YES - CLANG_WARN_CONSTANT_CONVERSION - YES - CLANG_WARN_DIRECT_OBJC_ISA_USAGE - YES - CLANG_WARN_EMPTY_BODY - YES - CLANG_WARN_ENUM_CONVERSION - YES - CLANG_WARN_INT_CONVERSION - YES - CLANG_WARN_OBJC_ROOT_CLASS - YES - CLANG_WARN_UNREACHABLE_CODE - YES - CLANG_WARN__DUPLICATE_METHOD_MATCH - YES - COPY_PHASE_STRIP - YES - ENABLE_NS_ASSERTIONS - NO - GCC_C_LANGUAGE_STANDARD - gnu99 - GCC_PREPROCESSOR_DEFINITIONS - - RELEASE=1 - - GCC_WARN_64_TO_32_BIT_CONVERSION - YES - GCC_WARN_ABOUT_RETURN_TYPE - YES - GCC_WARN_UNDECLARED_SELECTOR - YES - GCC_WARN_UNINITIALIZED_AUTOS - YES - GCC_WARN_UNUSED_FUNCTION - YES - GCC_WARN_UNUSED_VARIABLE - YES - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - STRIP_INSTALLED_PRODUCT - NO - SYMROOT - ${SRCROOT}/../build - VALIDATE_PRODUCT - YES - - isa - XCBuildConfiguration - name - Release - - FE67C22A54CDF4D547B37B544EB3C596 - - fileRef - A2A133D89DE3BC5EAC12EC00B4D7F6EE - isa - PBXBuildFile - - - rootObject - D41D8CD98F00B204E9800998ECF8427E - - +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 025B37EDBD17E42E22A6DEC474282473 /* ThreadHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0977027AF4CAF6E0E87B16442F15FCFA /* ThreadHelper.swift */; }; + 0DB28100778D573CE4973C09226B8AA2 /* UIButton+Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48220447BF3D2BB8306C87E82196C6C /* UIButton+Kingfisher.swift */; }; + 13D5B6A8D8A3C745C85E0683E914AE25 /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39C59F44CFFB38BDF47FE9365069F579 /* Filter.swift */; }; + 2D7AFAD3574A09FAD6264C6975062770 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29744CA505348D4C2B3180DC9889A24B /* ImageCache.swift */; }; + 329C0819109680CA82B66A833658BF3C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC4DC3D8EFEFCB57DD743BA66A83A5F6 /* Foundation.framework */; }; + 347C12AB3EB381A1AC423D665075FD94 /* UIView+INSNibLoading.h in Headers */ = {isa = PBXBuildFile; fileRef = E45E725D4CF67F6C2D73A10B6D60A629 /* UIView+INSNibLoading.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 55BE6C6283ABBC9AFF1B17C6DD1CC27D /* INSNibLoading-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = A52421B9B4BF256665262F9C8861ABEF /* INSNibLoading-dummy.m */; }; + 57F08D943ACFC824AADD934171DB6EDC /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 232CC40B6BDED99579A8A76AF55C1F9B /* CFNetwork.framework */; }; + 5A0176C0FD391DBB1001F5989831805A /* INSNibLoading-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = DDCB21D603E1C1A2279E2E6265E6CB00 /* INSNibLoading-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5B623EE84988E8D2D0AB89029B45D987 /* ImageView+Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B8537155C45CF0CCDEB5D2E52C4BA1B /* ImageView+Kingfisher.swift */; }; + 67D476AF38587D245A2FD37DE5F2BC9F /* KingfisherManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A222B3EAA5266FFD911A016B1DDA9EF2 /* KingfisherManager.swift */; }; + 7A9F3ED68D241004ABAAFDE6118AA14B /* AnimatedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB1C78D6D4646D6F57F26B9AB22FE43 /* AnimatedImageView.swift */; }; + 7B5206ABAE49381C07AEB9B8B0D97431 /* String+MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9A68EB16488A399233F0EED144A9ACB /* String+MD5.swift */; }; + 93C8D9BCB5AD0B50EE9841210402602C /* Kingfisher-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 08568A5AFCF8F4E03F3D0CF6D2CA25B4 /* Kingfisher-dummy.m */; }; + 955E0C2E59D4707DEAE749A9CFE238E3 /* Kingfisher.h in Headers */ = {isa = PBXBuildFile; fileRef = 01AEE7147F3B5952A7EBE301BD38AEE6 /* Kingfisher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9A80DCC278BB8934095BD58DA3C8E6D8 /* ImageDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = E28E2909992A68BCAB82A2CBB0C1350A /* ImageDownloader.swift */; }; + 9EE10457A25BF8E07C67318F24890709 /* ImagePrefetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DB58F2B6BEC3B7359A2A3D4A3E715A8 /* ImagePrefetcher.swift */; }; + 9F34EC6A7A359B17509AACE0C23101DB /* ImageTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FF6453F09B7688FDD4A62D1EC49972 /* ImageTransition.swift */; }; + 9F9036601CC0D5E1A2F491F0597B5A3B /* Pods-INSPhotoGallery-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = EFF5F1635FA20838A9367B4E53BA9D9F /* Pods-INSPhotoGallery-dummy.m */; }; + A364AAAF0282C83035286097C0BD16F5 /* UIView+INSNibLoading.m in Sources */ = {isa = PBXBuildFile; fileRef = C6D23066A32DC7A909A8DD12709D6234 /* UIView+INSNibLoading.m */; }; + AA1154ED4B2197671262EA37C90D88C7 /* RequrstModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97E4A204F5CEC2A21C25341B3A124122 /* RequrstModifier.swift */; }; + C741AF303672DC64C0EE3C130FF8DB46 /* Resource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A462022AF1DEB71230505BC59FEAA4D /* Resource.swift */; }; + D29AD38EB5945C2094DEF9D131031692 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B95ED53096C0FE9B0C72230FCF23628 /* Image.swift */; }; + D98E34CDEA0AEF6E2C75F03F3979BFAE /* ImageProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A6574A782177C153DCA0CA56FEE15B8 /* ImageProcessor.swift */; }; + DB38CF97B4D9F6150AE163E29A2C6DF2 /* Kingfisher-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 1AA2C52D5220BFD071CCAF1C459D033C /* Kingfisher-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DE9A8FEF64BD04FE192932CF784DC027 /* KingfisherOptionsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24B95A0FAD1F1C7FEF0D86DDDCEC5F50 /* KingfisherOptionsInfo.swift */; }; + DED7DB76A02075D018604E8B20C40BC4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC4DC3D8EFEFCB57DD743BA66A83A5F6 /* Foundation.framework */; }; + E2DCA5F98F5149C58A23E093C1C99E2E /* ImageSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 423629BD559E56008E0DDABF4A00558A /* ImageSerializer.swift */; }; + EE9EBE2A81F15F6713C07CAFDE9F7CF2 /* Pods-INSPhotoGallery-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = EE7049773F95B86BA4E28273D61DABD4 /* Pods-INSPhotoGallery-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FF06A0D3B55AE3C8923319BDF265D310 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC4DC3D8EFEFCB57DD743BA66A83A5F6 /* Foundation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + DACBB1772E136503A8FEC9F47702067A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5C8612EDF3EA754CD9BAC5E4397905D3; + remoteInfo = INSNibLoading; + }; + E9C38E7B70F2B8044C1D152D9903E129 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 409EE933BEEDC0F91907603DF5AA3DA6; + remoteInfo = Kingfisher; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 01AEE7147F3B5952A7EBE301BD38AEE6 /* Kingfisher.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Kingfisher.h; path = Sources/Kingfisher.h; sourceTree = ""; }; + 08568A5AFCF8F4E03F3D0CF6D2CA25B4 /* Kingfisher-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Kingfisher-dummy.m"; sourceTree = ""; }; + 0977027AF4CAF6E0E87B16442F15FCFA /* ThreadHelper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ThreadHelper.swift; path = Sources/ThreadHelper.swift; sourceTree = ""; }; + 1AA2C52D5220BFD071CCAF1C459D033C /* Kingfisher-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Kingfisher-umbrella.h"; sourceTree = ""; }; + 1B8537155C45CF0CCDEB5D2E52C4BA1B /* ImageView+Kingfisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ImageView+Kingfisher.swift"; path = "Sources/ImageView+Kingfisher.swift"; sourceTree = ""; }; + 232CC40B6BDED99579A8A76AF55C1F9B /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; + 24B95A0FAD1F1C7FEF0D86DDDCEC5F50 /* KingfisherOptionsInfo.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KingfisherOptionsInfo.swift; path = Sources/KingfisherOptionsInfo.swift; sourceTree = ""; }; + 29744CA505348D4C2B3180DC9889A24B /* ImageCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageCache.swift; path = Sources/ImageCache.swift; sourceTree = ""; }; + 2A462022AF1DEB71230505BC59FEAA4D /* Resource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Resource.swift; path = Sources/Resource.swift; sourceTree = ""; }; + 39625BE3887DF2F828D81C9116C941F4 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 39C59F44CFFB38BDF47FE9365069F579 /* Filter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Filter.swift; path = Sources/Filter.swift; sourceTree = ""; }; + 3A6574A782177C153DCA0CA56FEE15B8 /* ImageProcessor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageProcessor.swift; path = Sources/ImageProcessor.swift; sourceTree = ""; }; + 3D3B27E05AC10D00CDCFC16F9A397E19 /* INSNibLoading-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "INSNibLoading-prefix.pch"; sourceTree = ""; }; + 423629BD559E56008E0DDABF4A00558A /* ImageSerializer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageSerializer.swift; path = Sources/ImageSerializer.swift; sourceTree = ""; }; + 4B95ED53096C0FE9B0C72230FCF23628 /* Image.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Image.swift; path = Sources/Image.swift; sourceTree = ""; }; + 542131BFD63C89FEE96B918ECDD9BCF6 /* Kingfisher.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = Kingfisher.modulemap; sourceTree = ""; }; + 58FF6453F09B7688FDD4A62D1EC49972 /* ImageTransition.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageTransition.swift; path = Sources/ImageTransition.swift; sourceTree = ""; }; + 59A53372462CB368C34350CFC1178949 /* INSNibLoading.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = INSNibLoading.xcconfig; sourceTree = ""; }; + 68126256D71452AFE3C235AA04D06262 /* Pods-INSPhotoGallery-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-INSPhotoGallery-frameworks.sh"; sourceTree = ""; }; + 6D4FD17A7A989161F079E49AD8F021E5 /* Pods-INSPhotoGallery-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-INSPhotoGallery-resources.sh"; sourceTree = ""; }; + 7050916212AD3B44383B30692E1A9CEC /* Pods-INSPhotoGallery.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = "Pods-INSPhotoGallery.modulemap"; sourceTree = ""; }; + 73FC9B5CA37D5DECA7FCCF67DAE5371B /* INSNibLoading.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = INSNibLoading.modulemap; sourceTree = ""; }; + 757DBF3618DACDEDF2A5E1708FC58395 /* Pods-INSPhotoGallery-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-INSPhotoGallery-acknowledgements.markdown"; sourceTree = ""; }; + 7DB58F2B6BEC3B7359A2A3D4A3E715A8 /* ImagePrefetcher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImagePrefetcher.swift; path = Sources/ImagePrefetcher.swift; sourceTree = ""; }; + 82194D36DC99B7682315FC1C737CE67F /* Pods_INSPhotoGallery.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_INSPhotoGallery.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8B2B79C088788E4D7E7B76FA33F7013A /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 97E4A204F5CEC2A21C25341B3A124122 /* RequrstModifier.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RequrstModifier.swift; path = Sources/RequrstModifier.swift; sourceTree = ""; }; + A222B3EAA5266FFD911A016B1DDA9EF2 /* KingfisherManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KingfisherManager.swift; path = Sources/KingfisherManager.swift; sourceTree = ""; }; + A52421B9B4BF256665262F9C8861ABEF /* INSNibLoading-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "INSNibLoading-dummy.m"; sourceTree = ""; }; + B0E6A0B60A5449C4946CB822CC4F326C /* Kingfisher-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Kingfisher-prefix.pch"; sourceTree = ""; }; + B68E81F9BFF9CF2AA14F7B143EFBC9DE /* Pods-INSPhotoGallery.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-INSPhotoGallery.debug.xcconfig"; sourceTree = ""; }; + BAE34011959C8E24D686B8A0B2DEDE6F /* Kingfisher.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Kingfisher.xcconfig; sourceTree = ""; }; + BC4DC3D8EFEFCB57DD743BA66A83A5F6 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + C48220447BF3D2BB8306C87E82196C6C /* UIButton+Kingfisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIButton+Kingfisher.swift"; path = "Sources/UIButton+Kingfisher.swift"; sourceTree = ""; }; + C6D23066A32DC7A909A8DD12709D6234 /* UIView+INSNibLoading.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+INSNibLoading.m"; path = "INSNibLoading/iOS/UIView+INSNibLoading.m"; sourceTree = ""; }; + CBB1C78D6D4646D6F57F26B9AB22FE43 /* AnimatedImageView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatedImageView.swift; path = Sources/AnimatedImageView.swift; sourceTree = ""; }; + D915E29A6B876C2C6F2BFE46AAF3603A /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D9A68EB16488A399233F0EED144A9ACB /* String+MD5.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "String+MD5.swift"; path = "Sources/String+MD5.swift"; sourceTree = ""; }; + DDCB21D603E1C1A2279E2E6265E6CB00 /* INSNibLoading-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "INSNibLoading-umbrella.h"; sourceTree = ""; }; + E28E2909992A68BCAB82A2CBB0C1350A /* ImageDownloader.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageDownloader.swift; path = Sources/ImageDownloader.swift; sourceTree = ""; }; + E45E725D4CF67F6C2D73A10B6D60A629 /* UIView+INSNibLoading.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+INSNibLoading.h"; path = "INSNibLoading/iOS/UIView+INSNibLoading.h"; sourceTree = ""; }; + E549DE47E89ECCA8A93423128346F57B /* Pods-INSPhotoGallery.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-INSPhotoGallery.release.xcconfig"; sourceTree = ""; }; + EC3B22D5AEA21D531C58B3864EFBBBD3 /* INSNibLoading.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = INSNibLoading.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EE7049773F95B86BA4E28273D61DABD4 /* Pods-INSPhotoGallery-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-INSPhotoGallery-umbrella.h"; sourceTree = ""; }; + EF61C9EE0575929469A57704833E322B /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + EFF5F1635FA20838A9367B4E53BA9D9F /* Pods-INSPhotoGallery-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-INSPhotoGallery-dummy.m"; sourceTree = ""; }; + F6D67D2164BD589D8871FBEF3AFEFC95 /* Pods-INSPhotoGallery-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-INSPhotoGallery-acknowledgements.plist"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4831E083141EA7E4F468D48758928823 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FF06A0D3B55AE3C8923319BDF265D310 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 74D2815B14C550028BD1FAE1B23F8635 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 57F08D943ACFC824AADD934171DB6EDC /* CFNetwork.framework in Frameworks */, + 329C0819109680CA82B66A833658BF3C /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 98A1847063BE5EEB0AA00768D552E61F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DED7DB76A02075D018604E8B20C40BC4 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0C0C0782BECBCFDDD0392ED0BCD9FCA0 /* Pods */ = { + isa = PBXGroup; + children = ( + A0492BD6461D984CD415B4D9F7399AA0 /* INSNibLoading */, + B91BB637C9CAB0D364B809F50C06EFFD /* Kingfisher */, + ); + name = Pods; + sourceTree = ""; + }; + 1B475D18DE564A2FE3BCC52541863480 /* Support Files */ = { + isa = PBXGroup; + children = ( + EF61C9EE0575929469A57704833E322B /* Info.plist */, + 73FC9B5CA37D5DECA7FCCF67DAE5371B /* INSNibLoading.modulemap */, + 59A53372462CB368C34350CFC1178949 /* INSNibLoading.xcconfig */, + A52421B9B4BF256665262F9C8861ABEF /* INSNibLoading-dummy.m */, + 3D3B27E05AC10D00CDCFC16F9A397E19 /* INSNibLoading-prefix.pch */, + DDCB21D603E1C1A2279E2E6265E6CB00 /* INSNibLoading-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/INSNibLoading"; + sourceTree = ""; + }; + 42AD183F0B7DC5A32D03E91F225954F3 /* iOS */ = { + isa = PBXGroup; + children = ( + 232CC40B6BDED99579A8A76AF55C1F9B /* CFNetwork.framework */, + BC4DC3D8EFEFCB57DD743BA66A83A5F6 /* Foundation.framework */, + ); + name = iOS; + sourceTree = ""; + }; + 433CD3331B6C3787F473C941B61FC68F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 42AD183F0B7DC5A32D03E91F225954F3 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + 7DB346D0F39D3F0E887471402A8071AB = { + isa = PBXGroup; + children = ( + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, + 433CD3331B6C3787F473C941B61FC68F /* Frameworks */, + 0C0C0782BECBCFDDD0392ED0BCD9FCA0 /* Pods */, + F10F97F406DCD6F7270C2F70EE7C92AD /* Products */, + CAE6A2CA32ADF46E1BFD71B565B74975 /* Targets Support Files */, + ); + sourceTree = ""; + }; + 9B08542E36CFF87615E48E024BB2D9A3 /* Pods-INSPhotoGallery */ = { + isa = PBXGroup; + children = ( + D915E29A6B876C2C6F2BFE46AAF3603A /* Info.plist */, + 7050916212AD3B44383B30692E1A9CEC /* Pods-INSPhotoGallery.modulemap */, + 757DBF3618DACDEDF2A5E1708FC58395 /* Pods-INSPhotoGallery-acknowledgements.markdown */, + F6D67D2164BD589D8871FBEF3AFEFC95 /* Pods-INSPhotoGallery-acknowledgements.plist */, + EFF5F1635FA20838A9367B4E53BA9D9F /* Pods-INSPhotoGallery-dummy.m */, + 68126256D71452AFE3C235AA04D06262 /* Pods-INSPhotoGallery-frameworks.sh */, + 6D4FD17A7A989161F079E49AD8F021E5 /* Pods-INSPhotoGallery-resources.sh */, + EE7049773F95B86BA4E28273D61DABD4 /* Pods-INSPhotoGallery-umbrella.h */, + B68E81F9BFF9CF2AA14F7B143EFBC9DE /* Pods-INSPhotoGallery.debug.xcconfig */, + E549DE47E89ECCA8A93423128346F57B /* Pods-INSPhotoGallery.release.xcconfig */, + ); + name = "Pods-INSPhotoGallery"; + path = "Target Support Files/Pods-INSPhotoGallery"; + sourceTree = ""; + }; + A0492BD6461D984CD415B4D9F7399AA0 /* INSNibLoading */ = { + isa = PBXGroup; + children = ( + E45E725D4CF67F6C2D73A10B6D60A629 /* UIView+INSNibLoading.h */, + C6D23066A32DC7A909A8DD12709D6234 /* UIView+INSNibLoading.m */, + 1B475D18DE564A2FE3BCC52541863480 /* Support Files */, + ); + path = INSNibLoading; + sourceTree = ""; + }; + B91BB637C9CAB0D364B809F50C06EFFD /* Kingfisher */ = { + isa = PBXGroup; + children = ( + CBB1C78D6D4646D6F57F26B9AB22FE43 /* AnimatedImageView.swift */, + 39C59F44CFFB38BDF47FE9365069F579 /* Filter.swift */, + 4B95ED53096C0FE9B0C72230FCF23628 /* Image.swift */, + 29744CA505348D4C2B3180DC9889A24B /* ImageCache.swift */, + E28E2909992A68BCAB82A2CBB0C1350A /* ImageDownloader.swift */, + 7DB58F2B6BEC3B7359A2A3D4A3E715A8 /* ImagePrefetcher.swift */, + 3A6574A782177C153DCA0CA56FEE15B8 /* ImageProcessor.swift */, + 423629BD559E56008E0DDABF4A00558A /* ImageSerializer.swift */, + 58FF6453F09B7688FDD4A62D1EC49972 /* ImageTransition.swift */, + 1B8537155C45CF0CCDEB5D2E52C4BA1B /* ImageView+Kingfisher.swift */, + 01AEE7147F3B5952A7EBE301BD38AEE6 /* Kingfisher.h */, + A222B3EAA5266FFD911A016B1DDA9EF2 /* KingfisherManager.swift */, + 24B95A0FAD1F1C7FEF0D86DDDCEC5F50 /* KingfisherOptionsInfo.swift */, + 97E4A204F5CEC2A21C25341B3A124122 /* RequrstModifier.swift */, + 2A462022AF1DEB71230505BC59FEAA4D /* Resource.swift */, + D9A68EB16488A399233F0EED144A9ACB /* String+MD5.swift */, + 0977027AF4CAF6E0E87B16442F15FCFA /* ThreadHelper.swift */, + C48220447BF3D2BB8306C87E82196C6C /* UIButton+Kingfisher.swift */, + E6A1E90E2496BD9945465D7E16820987 /* Support Files */, + ); + path = Kingfisher; + sourceTree = ""; + }; + CAE6A2CA32ADF46E1BFD71B565B74975 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + 9B08542E36CFF87615E48E024BB2D9A3 /* Pods-INSPhotoGallery */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + E6A1E90E2496BD9945465D7E16820987 /* Support Files */ = { + isa = PBXGroup; + children = ( + 39625BE3887DF2F828D81C9116C941F4 /* Info.plist */, + 542131BFD63C89FEE96B918ECDD9BCF6 /* Kingfisher.modulemap */, + BAE34011959C8E24D686B8A0B2DEDE6F /* Kingfisher.xcconfig */, + 08568A5AFCF8F4E03F3D0CF6D2CA25B4 /* Kingfisher-dummy.m */, + B0E6A0B60A5449C4946CB822CC4F326C /* Kingfisher-prefix.pch */, + 1AA2C52D5220BFD071CCAF1C459D033C /* Kingfisher-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/Kingfisher"; + sourceTree = ""; + }; + F10F97F406DCD6F7270C2F70EE7C92AD /* Products */ = { + isa = PBXGroup; + children = ( + EC3B22D5AEA21D531C58B3864EFBBBD3 /* INSNibLoading.framework */, + 8B2B79C088788E4D7E7B76FA33F7013A /* Kingfisher.framework */, + 82194D36DC99B7682315FC1C737CE67F /* Pods_INSPhotoGallery.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 582C2D15084E9631A65B53276C970BE9 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 5A0176C0FD391DBB1001F5989831805A /* INSNibLoading-umbrella.h in Headers */, + 347C12AB3EB381A1AC423D665075FD94 /* UIView+INSNibLoading.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 744A390CE2C3BA28F1E4B81828BD60E4 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + EE9EBE2A81F15F6713C07CAFDE9F7CF2 /* Pods-INSPhotoGallery-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7BF89842AD36A02027B3A04C4393CAED /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DB38CF97B4D9F6150AE163E29A2C6DF2 /* Kingfisher-umbrella.h in Headers */, + 955E0C2E59D4707DEAE749A9CFE238E3 /* Kingfisher.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 3765A335B0608D78B6876E3C7D663E78 /* Pods-INSPhotoGallery */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D654883C0170DDD179FE425B9DB4D18 /* Build configuration list for PBXNativeTarget "Pods-INSPhotoGallery" */; + buildPhases = ( + 2DEA7104BB8976C949590D73C26BFA50 /* Sources */, + 98A1847063BE5EEB0AA00768D552E61F /* Frameworks */, + 744A390CE2C3BA28F1E4B81828BD60E4 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + DF575BE5DDDA08F0947832600D260E8E /* PBXTargetDependency */, + E13B447F88508C22BCE1D4D385811285 /* PBXTargetDependency */, + ); + name = "Pods-INSPhotoGallery"; + productName = "Pods-INSPhotoGallery"; + productReference = 82194D36DC99B7682315FC1C737CE67F /* Pods_INSPhotoGallery.framework */; + productType = "com.apple.product-type.framework"; + }; + 409EE933BEEDC0F91907603DF5AA3DA6 /* Kingfisher */ = { + isa = PBXNativeTarget; + buildConfigurationList = E2033E4DDBB46DD9187CECAFE5896798 /* Build configuration list for PBXNativeTarget "Kingfisher" */; + buildPhases = ( + 7E5C667A7BE26CC13CD0954280B22B0A /* Sources */, + 74D2815B14C550028BD1FAE1B23F8635 /* Frameworks */, + 7BF89842AD36A02027B3A04C4393CAED /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Kingfisher; + productName = Kingfisher; + productReference = 8B2B79C088788E4D7E7B76FA33F7013A /* Kingfisher.framework */; + productType = "com.apple.product-type.framework"; + }; + 5C8612EDF3EA754CD9BAC5E4397905D3 /* INSNibLoading */ = { + isa = PBXNativeTarget; + buildConfigurationList = 82D9DFF399E3ED68419FFDFABBA5A3CC /* Build configuration list for PBXNativeTarget "INSNibLoading" */; + buildPhases = ( + 917F2D45717678F5EE29572B94799FFD /* Sources */, + 4831E083141EA7E4F468D48758928823 /* Frameworks */, + 582C2D15084E9631A65B53276C970BE9 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = INSNibLoading; + productName = INSNibLoading; + productReference = EC3B22D5AEA21D531C58B3864EFBBBD3 /* INSNibLoading.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0730; + LastUpgradeCheck = 0700; + }; + buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 7DB346D0F39D3F0E887471402A8071AB; + productRefGroup = F10F97F406DCD6F7270C2F70EE7C92AD /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 5C8612EDF3EA754CD9BAC5E4397905D3 /* INSNibLoading */, + 409EE933BEEDC0F91907603DF5AA3DA6 /* Kingfisher */, + 3765A335B0608D78B6876E3C7D663E78 /* Pods-INSPhotoGallery */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 2DEA7104BB8976C949590D73C26BFA50 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9F9036601CC0D5E1A2F491F0597B5A3B /* Pods-INSPhotoGallery-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7E5C667A7BE26CC13CD0954280B22B0A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A9F3ED68D241004ABAAFDE6118AA14B /* AnimatedImageView.swift in Sources */, + 13D5B6A8D8A3C745C85E0683E914AE25 /* Filter.swift in Sources */, + D29AD38EB5945C2094DEF9D131031692 /* Image.swift in Sources */, + 2D7AFAD3574A09FAD6264C6975062770 /* ImageCache.swift in Sources */, + 9A80DCC278BB8934095BD58DA3C8E6D8 /* ImageDownloader.swift in Sources */, + 9EE10457A25BF8E07C67318F24890709 /* ImagePrefetcher.swift in Sources */, + D98E34CDEA0AEF6E2C75F03F3979BFAE /* ImageProcessor.swift in Sources */, + E2DCA5F98F5149C58A23E093C1C99E2E /* ImageSerializer.swift in Sources */, + 9F34EC6A7A359B17509AACE0C23101DB /* ImageTransition.swift in Sources */, + 5B623EE84988E8D2D0AB89029B45D987 /* ImageView+Kingfisher.swift in Sources */, + 93C8D9BCB5AD0B50EE9841210402602C /* Kingfisher-dummy.m in Sources */, + 67D476AF38587D245A2FD37DE5F2BC9F /* KingfisherManager.swift in Sources */, + DE9A8FEF64BD04FE192932CF784DC027 /* KingfisherOptionsInfo.swift in Sources */, + AA1154ED4B2197671262EA37C90D88C7 /* RequrstModifier.swift in Sources */, + C741AF303672DC64C0EE3C130FF8DB46 /* Resource.swift in Sources */, + 7B5206ABAE49381C07AEB9B8B0D97431 /* String+MD5.swift in Sources */, + 025B37EDBD17E42E22A6DEC474282473 /* ThreadHelper.swift in Sources */, + 0DB28100778D573CE4973C09226B8AA2 /* UIButton+Kingfisher.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 917F2D45717678F5EE29572B94799FFD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 55BE6C6283ABBC9AFF1B17C6DD1CC27D /* INSNibLoading-dummy.m in Sources */, + A364AAAF0282C83035286097C0BD16F5 /* UIView+INSNibLoading.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + DF575BE5DDDA08F0947832600D260E8E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = INSNibLoading; + target = 5C8612EDF3EA754CD9BAC5E4397905D3 /* INSNibLoading */; + targetProxy = DACBB1772E136503A8FEC9F47702067A /* PBXContainerItemProxy */; + }; + E13B447F88508C22BCE1D4D385811285 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Kingfisher; + target = 409EE933BEEDC0F91907603DF5AA3DA6 /* Kingfisher */; + targetProxy = E9C38E7B70F2B8044C1D152D9903E129 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 168C2078C0F2F32AA1CE5346345CD3E8 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BAE34011959C8E24D686B8A0B2DEDE6F /* Kingfisher.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/Kingfisher/Kingfisher-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Kingfisher/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Kingfisher/Kingfisher.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = Kingfisher; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 3E84D6065A2A6AA771A49B8716D2E2EA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 59A53372462CB368C34350CFC1178949 /* INSNibLoading.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/INSNibLoading/INSNibLoading-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/INSNibLoading/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/INSNibLoading/INSNibLoading.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = INSNibLoading; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 47BEF9D903506B003EA5C2B249729489 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + ONLY_ACTIVE_ARCH = YES; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + 47D5D21E9F925CB4E4BE6A5AFC4AF227 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E549DE47E89ECCA8A93423128346F57B /* Pods-INSPhotoGallery.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "Target Support Files/Pods-INSPhotoGallery/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = Pods_INSPhotoGallery; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 510D7C7EA6F78781A621478C67207EF7 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BAE34011959C8E24D686B8A0B2DEDE6F /* Kingfisher.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/Kingfisher/Kingfisher-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Kingfisher/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Kingfisher/Kingfisher.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = Kingfisher; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + AAF678CED40D3499169D10F63CA0719E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + B00B5949242F1A909716AEC868884705 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B68E81F9BFF9CF2AA14F7B143EFBC9DE /* Pods-INSPhotoGallery.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "Target Support Files/Pods-INSPhotoGallery/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = Pods_INSPhotoGallery; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + CDD98168A3562A5C4105E17E1CA6FEB4 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 59A53372462CB368C34350CFC1178949 /* INSNibLoading.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/INSNibLoading/INSNibLoading-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/INSNibLoading/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/INSNibLoading/INSNibLoading.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = INSNibLoading; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D654883C0170DDD179FE425B9DB4D18 /* Build configuration list for PBXNativeTarget "Pods-INSPhotoGallery" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B00B5949242F1A909716AEC868884705 /* Debug */, + 47D5D21E9F925CB4E4BE6A5AFC4AF227 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 47BEF9D903506B003EA5C2B249729489 /* Debug */, + AAF678CED40D3499169D10F63CA0719E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 82D9DFF399E3ED68419FFDFABBA5A3CC /* Build configuration list for PBXNativeTarget "INSNibLoading" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CDD98168A3562A5C4105E17E1CA6FEB4 /* Debug */, + 3E84D6065A2A6AA771A49B8716D2E2EA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E2033E4DDBB46DD9187CECAFE5896798 /* Build configuration list for PBXNativeTarget "Kingfisher" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 510D7C7EA6F78781A621478C67207EF7 /* Debug */, + 168C2078C0F2F32AA1CE5346345CD3E8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; +} diff --git a/Example/Pods/Target Support Files/HanekeSwift/HanekeSwift-dummy.m b/Example/Pods/Target Support Files/HanekeSwift/HanekeSwift-dummy.m deleted file mode 100644 index 073edf1..0000000 --- a/Example/Pods/Target Support Files/HanekeSwift/HanekeSwift-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_HanekeSwift : NSObject -@end -@implementation PodsDummy_HanekeSwift -@end diff --git a/Example/Pods/Target Support Files/HanekeSwift/HanekeSwift-umbrella.h b/Example/Pods/Target Support Files/HanekeSwift/HanekeSwift-umbrella.h deleted file mode 100644 index b75028d..0000000 --- a/Example/Pods/Target Support Files/HanekeSwift/HanekeSwift-umbrella.h +++ /dev/null @@ -1,6 +0,0 @@ -#import - - -FOUNDATION_EXPORT double HanekeVersionNumber; -FOUNDATION_EXPORT const unsigned char HanekeVersionString[]; - diff --git a/Example/Pods/Target Support Files/HanekeSwift/HanekeSwift.modulemap b/Example/Pods/Target Support Files/HanekeSwift/HanekeSwift.modulemap deleted file mode 100644 index 6b5d0f2..0000000 --- a/Example/Pods/Target Support Files/HanekeSwift/HanekeSwift.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Haneke { - umbrella header "HanekeSwift-umbrella.h" - - export * - module * { export * } -} diff --git a/Example/Pods/Target Support Files/HanekeSwift/HanekeSwift.xcconfig b/Example/Pods/Target Support Files/HanekeSwift/HanekeSwift.xcconfig deleted file mode 100644 index c3745af..0000000 --- a/Example/Pods/Target Support Files/HanekeSwift/HanekeSwift.xcconfig +++ /dev/null @@ -1,5 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/HanekeSwift" "${PODS_ROOT}/Headers/Public" -OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" -PODS_ROOT = ${SRCROOT} -SKIP_INSTALL = YES \ No newline at end of file diff --git a/Example/Pods/Target Support Files/INSNibLoading/INSNibLoading.xcconfig b/Example/Pods/Target Support Files/INSNibLoading/INSNibLoading.xcconfig index cddb6ee..f6a7df4 100644 --- a/Example/Pods/Target Support Files/INSNibLoading/INSNibLoading.xcconfig +++ b/Example/Pods/Target Support Files/INSNibLoading/INSNibLoading.xcconfig @@ -1,4 +1,8 @@ +CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/INSNibLoading GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/INSNibLoading" "${PODS_ROOT}/Headers/Public" +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_ROOT = ${SRCROOT} -SKIP_INSTALL = YES \ No newline at end of file +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Example/Pods/Target Support Files/INSNibLoading/Info.plist b/Example/Pods/Target Support Files/INSNibLoading/Info.plist index 513ed9d..3c175b6 100644 --- a/Example/Pods/Target Support Files/INSNibLoading/Info.plist +++ b/Example/Pods/Target Support Files/INSNibLoading/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - org.cocoapods.${PRODUCT_NAME:rfc1034identifier} + ${PRODUCT_BUNDLE_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/Example/Pods/Target Support Files/HanekeSwift/Info.plist b/Example/Pods/Target Support Files/Kingfisher/Info.plist similarity index 89% rename from Example/Pods/Target Support Files/HanekeSwift/Info.plist rename to Example/Pods/Target Support Files/Kingfisher/Info.plist index a1dcc56..3e85049 100644 --- a/Example/Pods/Target Support Files/HanekeSwift/Info.plist +++ b/Example/Pods/Target Support Files/Kingfisher/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - org.cocoapods.${PRODUCT_NAME:rfc1034identifier} + ${PRODUCT_BUNDLE_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.10.1 + 2.5.0 CFBundleSignature ???? CFBundleVersion diff --git a/Example/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m b/Example/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m new file mode 100644 index 0000000..1b89d0e --- /dev/null +++ b/Example/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Kingfisher : NSObject +@end +@implementation PodsDummy_Kingfisher +@end diff --git a/Example/Pods/Target Support Files/HanekeSwift/HanekeSwift-prefix.pch b/Example/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch similarity index 100% rename from Example/Pods/Target Support Files/HanekeSwift/HanekeSwift-prefix.pch rename to Example/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch diff --git a/Example/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h b/Example/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h new file mode 100644 index 0000000..9fe5254 --- /dev/null +++ b/Example/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h @@ -0,0 +1,7 @@ +#import + +#import "Kingfisher.h" + +FOUNDATION_EXPORT double KingfisherVersionNumber; +FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; + diff --git a/Example/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap b/Example/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap new file mode 100644 index 0000000..2a20d91 --- /dev/null +++ b/Example/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap @@ -0,0 +1,6 @@ +framework module Kingfisher { + umbrella header "Kingfisher-umbrella.h" + + export * + module * { export * } +} diff --git a/Example/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig b/Example/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig new file mode 100644 index 0000000..a5b3c1b --- /dev/null +++ b/Example/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig @@ -0,0 +1,10 @@ +CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Kingfisher +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" +OTHER_LDFLAGS = -framework "CFNetwork" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Example/Pods/Target Support Files/Pods/Info.plist b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Info.plist similarity index 92% rename from Example/Pods/Target Support Files/Pods/Info.plist rename to Example/Pods/Target Support Files/Pods-INSPhotoGallery/Info.plist index 6974542..2243fe6 100644 --- a/Example/Pods/Target Support Files/Pods/Info.plist +++ b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - org.cocoapods.${PRODUCT_NAME:rfc1034identifier} + ${PRODUCT_BUNDLE_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-acknowledgements.markdown b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-acknowledgements.markdown new file mode 100644 index 0000000..90812ee --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-acknowledgements.markdown @@ -0,0 +1,76 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## INSNibLoading + +The MIT License (MIT) + +Copyright © 2013 Nicolas Bouilleaud +UIView+NibLoading.h + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Copyright (c) 2015 inspace.io + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +## Kingfisher + +The MIT License (MIT) + +Copyright (c) 2015 Wei Wang + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +Generated by CocoaPods - https://cocoapods.org diff --git a/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-acknowledgements.plist b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-acknowledgements.plist new file mode 100644 index 0000000..8831778 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-acknowledgements.plist @@ -0,0 +1,110 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright © 2013 Nicolas Bouilleaud +UIView+NibLoading.h + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Copyright (c) 2015 inspace.io + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + Title + INSNibLoading + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2015 Wei Wang + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + Title + Kingfisher + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-dummy.m b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-dummy.m new file mode 100644 index 0000000..71c0d1a --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_INSPhotoGallery : NSObject +@end +@implementation PodsDummy_Pods_INSPhotoGallery +@end diff --git a/Example/Pods/Target Support Files/Pods/Pods-frameworks.sh b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-frameworks.sh similarity index 85% rename from Example/Pods/Target Support Files/Pods/Pods-frameworks.sh rename to Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-frameworks.sh index b5fadfa..ea86097 100755 --- a/Example/Pods/Target Support Files/Pods/Pods-frameworks.sh +++ b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-frameworks.sh @@ -16,7 +16,7 @@ install_framework() local source="$1" fi - local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" if [ -L "${source}" ]; then echo "Symlinked..." @@ -59,8 +59,8 @@ code_sign_if_enabled() { if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then # Use the current code_sign_identitiy echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\"" - /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1" + echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" + /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" fi } @@ -84,10 +84,10 @@ strip_invalid_archs() { if [[ "$CONFIGURATION" == "Debug" ]]; then - install_framework "Pods/Haneke.framework" - install_framework "Pods/INSNibLoading.framework" + install_framework "$BUILT_PRODUCTS_DIR/INSNibLoading/INSNibLoading.framework" + install_framework "$BUILT_PRODUCTS_DIR/Kingfisher/Kingfisher.framework" fi if [[ "$CONFIGURATION" == "Release" ]]; then - install_framework "Pods/Haneke.framework" - install_framework "Pods/INSNibLoading.framework" + install_framework "$BUILT_PRODUCTS_DIR/INSNibLoading/INSNibLoading.framework" + install_framework "$BUILT_PRODUCTS_DIR/Kingfisher/Kingfisher.framework" fi diff --git a/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-resources.sh b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-resources.sh new file mode 100755 index 0000000..0a15615 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-resources.sh @@ -0,0 +1,102 @@ +#!/bin/sh +set -e + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +case "${TARGETED_DEVICE_FAMILY}" in + 1,2) + TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" + ;; + 1) + TARGET_DEVICE_ARGS="--target-device iphone" + ;; + 2) + TARGET_DEVICE_ARGS="--target-device ipad" + ;; + *) + TARGET_DEVICE_ARGS="--target-device mac" + ;; +esac + +realpath() { + DIRECTORY="$(cd "${1%/*}" && pwd)" + FILENAME="${1##*/}" + echo "$DIRECTORY/$FILENAME" +} + +install_resource() +{ + if [[ "$1" = /* ]] ; then + RESOURCE_PATH="$1" + else + RESOURCE_PATH="${PODS_ROOT}/$1" + fi + if [[ ! -e "$RESOURCE_PATH" ]] ; then + cat << EOM +error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. +EOM + exit 1 + fi + case $RESOURCE_PATH in + *.storyboard) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.xib) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.framework) + echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + ;; + *.xcdatamodel) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" + ;; + *.xcdatamodeld) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" + ;; + *.xcmappingmodel) + echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" + xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" + ;; + *.xcassets) + ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH") + XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") + ;; + *) + echo "$RESOURCE_PATH" + echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" + ;; + esac +} + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then + mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi +rm -f "$RESOURCES_TO_COPY" + +if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] +then + # Find all other xcassets (this unfortunately includes those of path pods and other targets). + OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) + while read line; do + if [[ $line != "`realpath $PODS_ROOT`*" ]]; then + XCASSET_FILES+=("$line") + fi + done <<<"$OTHER_XCASSETS" + + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi diff --git a/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-umbrella.h b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-umbrella.h new file mode 100644 index 0000000..b04a1f9 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery-umbrella.h @@ -0,0 +1,6 @@ +#import + + +FOUNDATION_EXPORT double Pods_INSPhotoGalleryVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_INSPhotoGalleryVersionString[]; + diff --git a/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery.debug.xcconfig b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery.debug.xcconfig new file mode 100644 index 0000000..c1450e0 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery.debug.xcconfig @@ -0,0 +1,10 @@ +EMBEDDED_CONTENT_CONTAINS_SWIFT = YES +FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/INSNibLoading" "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/INSNibLoading/INSNibLoading.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher/Kingfisher.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "INSNibLoading" -framework "Kingfisher" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT}/Pods diff --git a/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery.modulemap b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery.modulemap new file mode 100644 index 0000000..1a5686b --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery.modulemap @@ -0,0 +1,6 @@ +framework module Pods_INSPhotoGallery { + umbrella header "Pods-INSPhotoGallery-umbrella.h" + + export * + module * { export * } +} diff --git a/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery.release.xcconfig b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery.release.xcconfig new file mode 100644 index 0000000..c1450e0 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-INSPhotoGallery/Pods-INSPhotoGallery.release.xcconfig @@ -0,0 +1,10 @@ +EMBEDDED_CONTENT_CONTAINS_SWIFT = YES +FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/INSNibLoading" "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/INSNibLoading/INSNibLoading.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher/Kingfisher.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "INSNibLoading" -framework "Kingfisher" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT}/Pods diff --git a/Example/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown b/Example/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown deleted file mode 100644 index cd83c2e..0000000 --- a/Example/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown +++ /dev/null @@ -1,246 +0,0 @@ -# Acknowledgements -This application makes use of the following third party libraries: - -## HanekeSwift - -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2014 Hermes Pique (@hpique) - 2014 Joan Romano (@joanromano) - 2014 Luis Ascorbe (@lascorbe) - 2014 Oriol Blanc (@oriolblanc) - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -## INSNibLoading - -The MIT License (MIT) - -Copyright © 2013 Nicolas Bouilleaud -UIView+NibLoading.h - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -Copyright (c) 2015 inspace.io - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - -Generated by CocoaPods - http://cocoapods.org diff --git a/Example/Pods/Target Support Files/Pods/Pods-acknowledgements.plist b/Example/Pods/Target Support Files/Pods/Pods-acknowledgements.plist deleted file mode 100644 index e36b2d2..0000000 --- a/Example/Pods/Target Support Files/Pods/Pods-acknowledgements.plist +++ /dev/null @@ -1,280 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - This application makes use of the following third party libraries: - Title - Acknowledgements - Type - PSGroupSpecifier - - - FooterText - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2014 Hermes Pique (@hpique) - 2014 Joan Romano (@joanromano) - 2014 Luis Ascorbe (@lascorbe) - 2014 Oriol Blanc (@oriolblanc) - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - Title - HanekeSwift - Type - PSGroupSpecifier - - - FooterText - The MIT License (MIT) - -Copyright © 2013 Nicolas Bouilleaud -UIView+NibLoading.h - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -Copyright (c) 2015 inspace.io - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - - Title - INSNibLoading - Type - PSGroupSpecifier - - - FooterText - Generated by CocoaPods - http://cocoapods.org - Title - - Type - PSGroupSpecifier - - - StringsTable - Acknowledgements - Title - Acknowledgements - - diff --git a/Example/Pods/Target Support Files/Pods/Pods-dummy.m b/Example/Pods/Target Support Files/Pods/Pods-dummy.m deleted file mode 100644 index ade64bd..0000000 --- a/Example/Pods/Target Support Files/Pods/Pods-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods : NSObject -@end -@implementation PodsDummy_Pods -@end diff --git a/Example/Pods/Target Support Files/Pods/Pods-resources.sh b/Example/Pods/Target Support Files/Pods/Pods-resources.sh deleted file mode 100755 index 16774fb..0000000 --- a/Example/Pods/Target Support Files/Pods/Pods-resources.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/sh -set -e - -mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" - -RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt -> "$RESOURCES_TO_COPY" - -XCASSET_FILES=() - -realpath() { - DIRECTORY="$(cd "${1%/*}" && pwd)" - FILENAME="${1##*/}" - echo "$DIRECTORY/$FILENAME" -} - -install_resource() -{ - case $1 in - *.storyboard) - echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" - ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" - ;; - *.xib) - echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" - ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" - ;; - *.framework) - echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - ;; - *.xcdatamodel) - echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" - xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" - ;; - *.xcdatamodeld) - echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" - xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" - ;; - *.xcmappingmodel) - echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" - xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" - ;; - *.xcassets) - ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1") - XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") - ;; - /*) - echo "$1" - echo "$1" >> "$RESOURCES_TO_COPY" - ;; - *) - echo "${PODS_ROOT}/$1" - echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" - ;; - esac -} - -mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then - mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" - rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -fi -rm -f "$RESOURCES_TO_COPY" - -if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] -then - case "${TARGETED_DEVICE_FAMILY}" in - 1,2) - TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" - ;; - 1) - TARGET_DEVICE_ARGS="--target-device iphone" - ;; - 2) - TARGET_DEVICE_ARGS="--target-device ipad" - ;; - *) - TARGET_DEVICE_ARGS="--target-device mac" - ;; - esac - - # Find all other xcassets (this unfortunately includes those of path pods and other targets). - OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) - while read line; do - if [[ $line != "`realpath $PODS_ROOT`*" ]]; then - XCASSET_FILES+=("$line") - fi - done <<<"$OTHER_XCASSETS" - - printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -fi diff --git a/Example/Pods/Target Support Files/Pods/Pods-umbrella.h b/Example/Pods/Target Support Files/Pods/Pods-umbrella.h deleted file mode 100644 index 21dcfd2..0000000 --- a/Example/Pods/Target Support Files/Pods/Pods-umbrella.h +++ /dev/null @@ -1,6 +0,0 @@ -#import - - -FOUNDATION_EXPORT double PodsVersionNumber; -FOUNDATION_EXPORT const unsigned char PodsVersionString[]; - diff --git a/Example/Pods/Target Support Files/Pods/Pods.debug.xcconfig b/Example/Pods/Target Support Files/Pods/Pods.debug.xcconfig deleted file mode 100644 index b84b3fb..0000000 --- a/Example/Pods/Target Support Files/Pods/Pods.debug.xcconfig +++ /dev/null @@ -1,8 +0,0 @@ -EMBEDDED_CONTENT_CONTAINS_SWIFT = YES -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' -OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/Haneke.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/INSNibLoading.framework/Headers" -OTHER_LDFLAGS = $(inherited) -framework "Haneke" -framework "INSNibLoading" -OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" -PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods -PODS_ROOT = ${SRCROOT}/Pods \ No newline at end of file diff --git a/Example/Pods/Target Support Files/Pods/Pods.modulemap b/Example/Pods/Target Support Files/Pods/Pods.modulemap deleted file mode 100644 index 8413413..0000000 --- a/Example/Pods/Target Support Files/Pods/Pods.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Pods { - umbrella header "Pods-umbrella.h" - - export * - module * { export * } -} diff --git a/Example/Pods/Target Support Files/Pods/Pods.release.xcconfig b/Example/Pods/Target Support Files/Pods/Pods.release.xcconfig deleted file mode 100644 index b84b3fb..0000000 --- a/Example/Pods/Target Support Files/Pods/Pods.release.xcconfig +++ /dev/null @@ -1,8 +0,0 @@ -EMBEDDED_CONTENT_CONTAINS_SWIFT = YES -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' -OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/Haneke.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/INSNibLoading.framework/Headers" -OTHER_LDFLAGS = $(inherited) -framework "Haneke" -framework "INSNibLoading" -OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" -PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods -PODS_ROOT = ${SRCROOT}/Pods \ No newline at end of file diff --git a/INSPhotoGallery.podspec b/INSPhotoGallery.podspec index 1a8f69e..c07947f 100644 --- a/INSPhotoGallery.podspec +++ b/INSPhotoGallery.podspec @@ -13,4 +13,4 @@ Pod::Spec.new do |s| s.platform = :ios, '8.0' s.frameworks = 'UIKit', 'Foundation' -end \ No newline at end of file +end diff --git a/INSPhotoGallery/INSPhoto.swift b/INSPhotoGallery/INSPhoto.swift index 8075c18..dc95e6e 100644 --- a/INSPhotoGallery/INSPhoto.swift +++ b/INSPhotoGallery/INSPhoto.swift @@ -27,75 +27,73 @@ import UIKit var image: UIImage? { get } var thumbnailImage: UIImage? { get } - func loadImageWithCompletionHandler(completion: (image: UIImage?, error: NSError?) -> ()) - func loadThumbnailImageWithCompletionHandler(completion: (image: UIImage?, error: NSError?) -> ()) + func loadImageWithCompletionHandler(_ completion: @escaping (_ image: UIImage?, _ error: Error?) -> ()) + func loadThumbnailImageWithCompletionHandler(_ completion: @escaping (_ image: UIImage?, _ error: Error?) -> ()) var attributedTitle: NSAttributedString? { get } } -public class INSPhoto: INSPhotoViewable, Equatable { - @objc public var image: UIImage? - @objc public var thumbnailImage: UIImage? +open class INSPhoto: INSPhotoViewable, Equatable { + @objc open var image: UIImage? + @objc open var thumbnailImage: UIImage? - var imageURL: NSURL? - var thumbnailImageURL: NSURL? + var imageURL: URL? + var thumbnailImageURL: URL? - @objc public var attributedTitle: NSAttributedString? + @objc open var attributedTitle: NSAttributedString? public init(image: UIImage?, thumbnailImage: UIImage?) { self.image = image self.thumbnailImage = thumbnailImage } - public init(imageURL: NSURL?, thumbnailImageURL: NSURL?) { + public init(imageURL: URL?, thumbnailImageURL: URL?) { self.imageURL = imageURL self.thumbnailImageURL = thumbnailImageURL } - public init (imageURL: NSURL?, thumbnailImage: UIImage) { + public init (imageURL: URL?, thumbnailImage: UIImage) { self.imageURL = imageURL self.thumbnailImage = thumbnailImage } - @objc public func loadImageWithCompletionHandler(completion: (image: UIImage?, error: NSError?) -> ()) { + @objc open func loadImageWithCompletionHandler(_ completion: @escaping (_ image: UIImage?, _ error: Error?) -> ()) { if let image = image { - completion(image: image, error: nil) + completion(image, nil) return } loadImageWithURL(imageURL, completion: completion) } - @objc public func loadThumbnailImageWithCompletionHandler(completion: (image: UIImage?, error: NSError?) -> ()) { + @objc open func loadThumbnailImageWithCompletionHandler(_ completion: @escaping (_ image: UIImage?, _ error: Error?) -> ()) { if let thumbnailImage = thumbnailImage { - completion(image: thumbnailImage, error: nil) + completion(thumbnailImage, nil) return } loadImageWithURL(thumbnailImageURL, completion: completion) } - public func loadImageWithURL(url: NSURL?, completion: (image: UIImage?, error: NSError?) -> ()) { - let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()) + open func loadImageWithURL(_ url: URL?, completion: @escaping (_ image: UIImage?, _ error: Error?) -> ()) { + let session = URLSession(configuration: URLSessionConfiguration.default) if let imageURL = url { - session.dataTaskWithURL(imageURL, completionHandler: { (response: NSData?, data: NSURLResponse?, error: NSError?) in - - dispatch_async(dispatch_get_main_queue(), { () -> Void in + session.dataTask(with: imageURL, completionHandler: { (response, data, error) in + DispatchQueue.main.async(execute: { () -> Void in if error != nil { - completion(image: nil, error: error) + completion(nil, error) } else if let response = response, let image = UIImage(data: response) { - completion(image: image, error: nil) + completion(image, nil) } else { - completion(image: nil, error: NSError(domain: "INSPhotoDomain", code: -1, userInfo: [ NSLocalizedDescriptionKey: "Couldn't load image"])) + completion(nil, NSError(domain: "INSPhotoDomain", code: -1, userInfo: [ NSLocalizedDescriptionKey: "Couldn't load image"])) } session.finishTasksAndInvalidate() }) - }).resume() } else { - completion(image: nil, error: NSError(domain: "INSPhotoDomain", code: -2, userInfo: [ NSLocalizedDescriptionKey: "Image URL not found."])) + completion(nil, NSError(domain: "INSPhotoDomain", code: -2, userInfo: [ NSLocalizedDescriptionKey: "Image URL not found."])) } } } public func ==(lhs: T, rhs: T) -> Bool { return lhs === rhs -} \ No newline at end of file +} diff --git a/INSPhotoGallery/INSPhotoViewController.swift b/INSPhotoGallery/INSPhotoViewController.swift index feae784..801fc07 100644 --- a/INSPhotoGallery/INSPhotoViewController.swift +++ b/INSPhotoGallery/INSPhotoViewController.swift @@ -19,7 +19,7 @@ import UIKit -public class INSPhotoViewController: UIViewController, UIScrollViewDelegate { +open class INSPhotoViewController: UIViewController, UIScrollViewDelegate { var photo: INSPhotoViewable var longPressGestureHandler: ((UILongPressGestureRecognizer) -> ())? @@ -40,7 +40,7 @@ public class INSPhotoViewController: UIViewController, UIScrollViewDelegate { }() lazy private(set) var activityIndicator: UIActivityIndicatorView = { - let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .White) + let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .white) activityIndicator.startAnimating() return activityIndicator }() @@ -58,17 +58,17 @@ public class INSPhotoViewController: UIViewController, UIScrollViewDelegate { scalingImageView.delegate = nil } - public override func viewDidLoad() { + open override func viewDidLoad() { super.viewDidLoad() scalingImageView.delegate = self scalingImageView.frame = view.bounds - scalingImageView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] + scalingImageView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(scalingImageView) view.addSubview(activityIndicator) activityIndicator.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY) - activityIndicator.autoresizingMask = [.FlexibleTopMargin, .FlexibleLeftMargin, .FlexibleRightMargin, .FlexibleBottomMargin] + activityIndicator.autoresizingMask = [.flexibleTopMargin, .flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin] activityIndicator.sizeToFit() view.addGestureRecognizer(doubleTapGestureRecognizer) @@ -87,13 +87,13 @@ public class INSPhotoViewController: UIViewController, UIScrollViewDelegate { } - public override func viewWillLayoutSubviews() { + open override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() scalingImageView.frame = view.bounds } private func loadThumbnailImage() { - view.bringSubviewToFront(activityIndicator) + view.bringSubview(toFront: activityIndicator) photo.loadThumbnailImageWithCompletionHandler { [weak self] (image, error) -> () in let completeLoading = { @@ -104,10 +104,10 @@ public class INSPhotoViewController: UIViewController, UIScrollViewDelegate { self?.loadFullSizeImage() } - if NSThread.isMainThread() { + if Thread.isMainThread { completeLoading() } else { - dispatch_async(dispatch_get_main_queue(), { () -> Void in + DispatchQueue.main.async(execute: { () -> Void in completeLoading() }) } @@ -115,31 +115,31 @@ public class INSPhotoViewController: UIViewController, UIScrollViewDelegate { } private func loadFullSizeImage() { - view.bringSubviewToFront(activityIndicator) + view.bringSubview(toFront: activityIndicator) self.photo.loadImageWithCompletionHandler({ [weak self] (image, error) -> () in let completeLoading = { self?.activityIndicator.stopAnimating() self?.scalingImageView.image = image } - if NSThread.isMainThread() { + if Thread.isMainThread { completeLoading() } else { - dispatch_async(dispatch_get_main_queue(), { () -> Void in + DispatchQueue.main.async(execute: { () -> Void in completeLoading() }) } }) } - @objc private func handleLongPressWithGestureRecognizer(recognizer: UILongPressGestureRecognizer) { - if recognizer.state == UIGestureRecognizerState.Began { + @objc private func handleLongPressWithGestureRecognizer(_ recognizer: UILongPressGestureRecognizer) { + if recognizer.state == UIGestureRecognizerState.began { longPressGestureHandler?(recognizer) } } - @objc private func handleDoubleTapWithGestureRecognizer(recognizer: UITapGestureRecognizer) { - let pointInView = recognizer.locationInView(scalingImageView.imageView) + @objc private func handleDoubleTapWithGestureRecognizer(_ recognizer: UITapGestureRecognizer) { + let pointInView = recognizer.location(in: scalingImageView.imageView) var newZoomScale = scalingImageView.maximumZoomScale if scalingImageView.zoomScale >= scalingImageView.maximumZoomScale || abs(scalingImageView.zoomScale - scalingImageView.maximumZoomScale) <= 0.01 { @@ -153,24 +153,24 @@ public class INSPhotoViewController: UIViewController, UIScrollViewDelegate { let originY = pointInView.y - (height / 2.0) let rectToZoom = CGRect(x: originX, y: originY, width: width, height: height) - scalingImageView.zoomToRect(rectToZoom, animated: true) + scalingImageView.zoom(to: rectToZoom, animated: true) } // MARK:- UIScrollViewDelegate - public func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { + open func viewForZooming(in scrollView: UIScrollView) -> UIView? { return scalingImageView.imageView } - public func scrollViewWillBeginZooming(scrollView: UIScrollView, withView view: UIView?) { - scrollView.panGestureRecognizer.enabled = true + open func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) { + scrollView.panGestureRecognizer.isEnabled = true } - public func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView?, atScale scale: CGFloat) { + open func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) { // There is a bug, especially prevalent on iPhone 6 Plus, that causes zooming to render all other gesture recognizers ineffective. // This bug is fixed by disabling the pan gesture recognizer of the scroll view when it is not needed. if (scrollView.zoomScale == scrollView.minimumZoomScale) { - scrollView.panGestureRecognizer.enabled = false; + scrollView.panGestureRecognizer.isEnabled = false; } } -} \ No newline at end of file +} diff --git a/INSPhotoGallery/INSPhotosDataSource.swift b/INSPhotoGallery/INSPhotosDataSource.swift index b521ecf..55c287c 100644 --- a/INSPhotoGallery/INSPhotosDataSource.swift +++ b/INSPhotoGallery/INSPhotosDataSource.swift @@ -26,18 +26,18 @@ public struct INSPhotosDataSource { return photos.count } - public func photoAtIndex(index: Int) -> INSPhotoViewable? { + public func photoAtIndex(_ index: Int) -> INSPhotoViewable? { if (index < photos.count && index >= 0) { return photos[index]; } return nil } - public func indexOfPhoto(photo: INSPhotoViewable) -> Int? { - return photos.indexOf({ $0 === photo}) + public func indexOfPhoto(_ photo: INSPhotoViewable) -> Int? { + return photos.index(where: { $0 === photo}) } - public func containsPhoto(photo: INSPhotoViewable) -> Bool { + public func containsPhoto(_ photo: INSPhotoViewable) -> Bool { return indexOfPhoto(photo) != nil } @@ -46,4 +46,4 @@ public struct INSPhotosDataSource { return photoAtIndex(index) } } -} \ No newline at end of file +} diff --git a/INSPhotoGallery/INSPhotosInteractionAnimator.swift b/INSPhotoGallery/INSPhotosInteractionAnimator.swift index d80a26c..16633ea 100644 --- a/INSPhotoGallery/INSPhotosInteractionAnimator.swift +++ b/INSPhotoGallery/INSPhotosInteractionAnimator.swift @@ -26,41 +26,41 @@ class INSPhotosInteractionAnimator: NSObject, UIViewControllerInteractiveTransit private var transitionContext: UIViewControllerContextTransitioning? - func startInteractiveTransition(transitionContext: UIViewControllerContextTransitioning) { + func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) { viewToHideWhenBeginningTransition?.alpha = 0.0 self.transitionContext = transitionContext } - func handlePanWithPanGestureRecognizer(gestureRecognizer: UIPanGestureRecognizer, viewToPan: UIView, anchorPoint: CGPoint) { - guard let fromView = transitionContext?.viewForKey(UITransitionContextFromViewKey) else { + func handlePanWithPanGestureRecognizer(_ gestureRecognizer: UIPanGestureRecognizer, viewToPan: UIView, anchorPoint: CGPoint) { + guard let fromView = transitionContext?.view(forKey: UITransitionContextViewKey.from) else { return } - let translatedPanGesturePoint = gestureRecognizer.translationInView(fromView) + let translatedPanGesturePoint = gestureRecognizer.translation(in: fromView) let newCenterPoint = CGPoint(x: anchorPoint.x, y: anchorPoint.y + translatedPanGesturePoint.y) viewToPan.center = newCenterPoint let verticalDelta = newCenterPoint.y - anchorPoint.y let backgroundAlpha = backgroundAlphaForPanningWithVerticalDelta(verticalDelta) - fromView.backgroundColor = fromView.backgroundColor?.colorWithAlphaComponent(backgroundAlpha) + fromView.backgroundColor = fromView.backgroundColor?.withAlphaComponent(backgroundAlpha) - if gestureRecognizer.state == .Ended { + if gestureRecognizer.state == .ended { finishPanWithPanGestureRecognizer(gestureRecognizer, verticalDelta: verticalDelta,viewToPan: viewToPan, anchorPoint: anchorPoint) } } - func finishPanWithPanGestureRecognizer(gestureRecognizer: UIPanGestureRecognizer, verticalDelta: CGFloat, viewToPan: UIView, anchorPoint: CGPoint) { - guard let fromView = transitionContext?.viewForKey(UITransitionContextFromViewKey) else { + func finishPanWithPanGestureRecognizer(_ gestureRecognizer: UIPanGestureRecognizer, verticalDelta: CGFloat, viewToPan: UIView, anchorPoint: CGPoint) { + guard let fromView = transitionContext?.view(forKey: UITransitionContextViewKey.from) else { return } let returnToCenterVelocityAnimationRatio = 0.00007 let panDismissDistanceRatio = 50.0 / 667.0 // distance over iPhone 6 height let panDismissMaximumDuration = 0.45 - let velocityY = gestureRecognizer.velocityInView(gestureRecognizer.view).y + let velocityY = gestureRecognizer.velocity(in: gestureRecognizer.view).y var animationDuration = (Double(abs(velocityY)) * returnToCenterVelocityAnimationRatio) + 0.2 - var animationCurve: UIViewAnimationOptions = .CurveEaseOut + var animationCurve: UIViewAnimationOptions = .curveEaseOut var finalPageViewCenterPoint = anchorPoint var finalBackgroundAlpha = 1.0 @@ -70,8 +70,8 @@ class INSPhotosInteractionAnimator: NSObject, UIViewControllerInteractiveTransit var didAnimateUsingAnimator = false if isDismissing { - if let animator = self.animator, let transitionContext = transitionContext where shouldAnimateUsingAnimator { - animator.animateTransition(transitionContext) + if let animator = self.animator, let transitionContext = transitionContext , shouldAnimateUsingAnimator { + animator.animateTransition(using: transitionContext) didAnimateUsingAnimator = true } else { let isPositiveDelta = verticalDelta >= 0 @@ -81,7 +81,7 @@ class INSPhotosInteractionAnimator: NSObject, UIViewControllerInteractiveTransit animationDuration = Double(abs(finalPageViewCenterPoint.y - viewToPan.center.y) / abs(velocityY)) animationDuration = min(animationDuration, panDismissMaximumDuration) - animationCurve = .CurveEaseOut + animationCurve = .curveEaseOut finalBackgroundAlpha = 0.0 } } @@ -89,9 +89,9 @@ class INSPhotosInteractionAnimator: NSObject, UIViewControllerInteractiveTransit if didAnimateUsingAnimator { self.transitionContext = nil } else { - UIView.animateWithDuration(animationDuration, delay: 0, options: animationCurve, animations: { () -> Void in + UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: { () -> Void in viewToPan.center = finalPageViewCenterPoint - fromView.backgroundColor = fromView.backgroundColor?.colorWithAlphaComponent(CGFloat(finalBackgroundAlpha)) + fromView.backgroundColor = fromView.backgroundColor?.withAlphaComponent(CGFloat(finalBackgroundAlpha)) }, completion: { finished in if isDismissing { @@ -104,30 +104,30 @@ class INSPhotosInteractionAnimator: NSObject, UIViewControllerInteractiveTransit } self.viewToHideWhenBeginningTransition?.alpha = 1.0 - self.transitionContext?.completeTransition(isDismissing && !(self.transitionContext?.transitionWasCancelled() ?? false)) + self.transitionContext?.completeTransition(isDismissing && !(self.transitionContext?.transitionWasCancelled ?? false)) self.transitionContext = nil }) } } private func fixCancellationStatusBarAppearanceBug() { - guard let toViewController = self.transitionContext?.viewControllerForKey(UITransitionContextToViewControllerKey), - let fromViewController = self.transitionContext?.viewControllerForKey(UITransitionContextFromViewControllerKey) else { + guard let toViewController = self.transitionContext?.viewController(forKey: UITransitionContextViewControllerKey.to), + let fromViewController = self.transitionContext?.viewController(forKey: UITransitionContextViewControllerKey.from) else { return } let statusBarViewControllerSelector = Selector("_setPresentedSta" + "tusBarViewController:") - if toViewController.respondsToSelector(statusBarViewControllerSelector) && fromViewController.modalPresentationCapturesStatusBarAppearance { - toViewController.performSelector(statusBarViewControllerSelector, withObject: fromViewController) + if toViewController.responds(to: statusBarViewControllerSelector) && fromViewController.modalPresentationCapturesStatusBarAppearance { + toViewController.perform(statusBarViewControllerSelector, with: fromViewController) } } private func isRadar20070670Fixed() -> Bool { - return NSProcessInfo.processInfo().isOperatingSystemAtLeastVersion(NSOperatingSystemVersion.init(majorVersion: 8, minorVersion: 3, patchVersion: 0)) + return ProcessInfo.processInfo.isOperatingSystemAtLeast(OperatingSystemVersion.init(majorVersion: 8, minorVersion: 3, patchVersion: 0)) } - private func backgroundAlphaForPanningWithVerticalDelta(delta: CGFloat) -> CGFloat { - guard let fromView = transitionContext?.viewForKey(UITransitionContextFromViewKey) else { + private func backgroundAlphaForPanningWithVerticalDelta(_ delta: CGFloat) -> CGFloat { + guard let fromView = transitionContext?.view(forKey: UITransitionContextViewKey.from) else { return 0.0 } @@ -139,4 +139,4 @@ class INSPhotosInteractionAnimator: NSObject, UIViewControllerInteractiveTransit let deltaAsPercentageOfMaximum = min(abs(delta) / maximumDelta, 1.0) return startingAlpha - (deltaAsPercentageOfMaximum * totalAvailableAlpha) } -} \ No newline at end of file +} diff --git a/INSPhotoGallery/INSPhotosOverlayView.swift b/INSPhotoGallery/INSPhotosOverlayView.swift index a9ade84..6c145b5 100644 --- a/INSPhotoGallery/INSPhotosOverlayView.swift +++ b/INSPhotoGallery/INSPhotosOverlayView.swift @@ -22,8 +22,8 @@ import UIKit public protocol INSPhotosOverlayViewable:class { weak var photosViewController: INSPhotosViewController? { get set } - func populateWithPhoto(photo: INSPhotoViewable) - func setHidden(hidden: Bool, animated: Bool) + func populateWithPhoto(_ photo: INSPhotoViewable) + func setHidden(_ hidden: Bool, animated: Bool) func view() -> UIView } @@ -33,12 +33,12 @@ extension INSPhotosOverlayViewable where Self: UIView { } } -public class INSPhotosOverlayView: UIView , INSPhotosOverlayViewable { - public private(set) var navigationBar: UINavigationBar! - public private(set) var captionLabel: UILabel! +open class INSPhotosOverlayView: UIView , INSPhotosOverlayViewable { + open private(set) var navigationBar: UINavigationBar! + open private(set) var captionLabel: UILabel! - public private(set) var navigationItem: UINavigationItem! - public weak var photosViewController: INSPhotosViewController? + open private(set) var navigationItem: UINavigationItem! + open weak var photosViewController: INSPhotosViewController? private var currentPhoto: INSPhotoViewable? var leftBarButtonItem: UIBarButtonItem? { @@ -68,14 +68,14 @@ public class INSPhotosOverlayView: UIView , INSPhotosOverlayViewable { } // Pass the touches down to other views - public override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { - if let hitView = super.hitTest(point, withEvent: event) where hitView != self { + open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let hitView = super.hitTest(point, with: event) , hitView != self { return hitView } return nil } - public override func layoutSubviews() { + open override func layoutSubviews() { // The navigation bar has a different intrinsic content size upon rotation, so we must update to that new size. // Do it without animation to more closely match the behavior in `UINavigationController` UIView.performWithoutAnimation { () -> Void in @@ -85,27 +85,27 @@ public class INSPhotosOverlayView: UIView , INSPhotosOverlayViewable { super.layoutSubviews() } - public func setHidden(hidden: Bool, animated: Bool) { - if self.hidden == hidden { + open func setHidden(_ hidden: Bool, animated: Bool) { + if self.isHidden == hidden { return } if animated { - self.hidden = false + self.isHidden = false self.alpha = hidden ? 1.0 : 0.0 - UIView.animateWithDuration(0.2, delay: 0.0, options: [.CurveEaseInOut, .AllowAnimatedContent, .AllowUserInteraction], animations: { () -> Void in + UIView.animate(withDuration: 0.2, delay: 0.0, options: [.allowAnimatedContent, .allowUserInteraction], animations: { () -> Void in self.alpha = hidden ? 0.0 : 1.0 }, completion: { result in self.alpha = 1.0 - self.hidden = hidden + self.isHidden = hidden }) } else { - self.hidden = hidden + self.isHidden = hidden } } - public func populateWithPhoto(photo: INSPhotoViewable) { + open func populateWithPhoto(_ photo: INSPhotoViewable) { self.currentPhoto = photo if let photosViewController = photosViewController { @@ -116,17 +116,17 @@ public class INSPhotosOverlayView: UIView , INSPhotosOverlayViewable { } } - @objc private func closeButtonTapped(sender: UIBarButtonItem) { - photosViewController?.dismissViewControllerAnimated(true, completion: nil) + @objc private func closeButtonTapped(_ sender: UIBarButtonItem) { + photosViewController?.dismiss(animated: true, completion: nil) } - @objc private func actionButtonTapped(sender: UIBarButtonItem) { + @objc private func actionButtonTapped(_ sender: UIBarButtonItem) { if let currentPhoto = currentPhoto { currentPhoto.loadImageWithCompletionHandler({ [weak self] (image, error) -> () in if let image = (image ?? currentPhoto.thumbnailImage) { let activityController = UIActivityViewController(activityItems: [image], applicationActivities: nil) activityController.popoverPresentationController?.barButtonItem = sender - self?.photosViewController?.presentViewController(activityController, animated: true, completion: nil) + self?.photosViewController?.present(activityController, animated: true, completion: nil) } }); } @@ -135,41 +135,41 @@ public class INSPhotosOverlayView: UIView , INSPhotosOverlayViewable { private func setupNavigationBar() { navigationBar = UINavigationBar() navigationBar.translatesAutoresizingMaskIntoConstraints = false - navigationBar.backgroundColor = UIColor.clearColor() + navigationBar.backgroundColor = UIColor.clear navigationBar.barTintColor = nil - navigationBar.translucent = true + navigationBar.isTranslucent = true navigationBar.shadowImage = UIImage() - navigationBar.setBackgroundImage(UIImage(), forBarMetrics: .Default) + navigationBar.setBackgroundImage(UIImage(), for: .default) navigationItem = UINavigationItem(title: "") navigationBar.items = [navigationItem] addSubview(navigationBar) - let topConstraint = NSLayoutConstraint(item: navigationBar, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1.0, constant: 0.0) - let widthConstraint = NSLayoutConstraint(item: navigationBar, attribute: .Width, relatedBy: .Equal, toItem: self, attribute: .Width, multiplier: 1.0, constant: 0.0) - let horizontalPositionConstraint = NSLayoutConstraint(item: navigationBar, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .CenterX, multiplier: 1.0, constant: 0.0) + let topConstraint = NSLayoutConstraint(item: navigationBar, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0.0) + let widthConstraint = NSLayoutConstraint(item: navigationBar, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1.0, constant: 0.0) + let horizontalPositionConstraint = NSLayoutConstraint(item: navigationBar, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1.0, constant: 0.0) self.addConstraints([topConstraint,widthConstraint,horizontalPositionConstraint]) - if let bundlePath = NSBundle(forClass: self.dynamicType).pathForResource("INSPhotoGallery", ofType: "bundle") { - let bundle = NSBundle(path: bundlePath) - leftBarButtonItem = UIBarButtonItem(image: UIImage(named: "INSPhotoGalleryClose", inBundle: bundle, compatibleWithTraitCollection: nil), landscapeImagePhone: UIImage(named: "INSPhotoGalleryCloseLandscape", inBundle: bundle, compatibleWithTraitCollection: nil), style: .Plain, target: self, action: #selector(INSPhotosOverlayView.closeButtonTapped(_:))) + if let bundlePath = Bundle(for: type(of: self)).path(forResource: "INSPhotoGallery", ofType: "bundle") { + let bundle = Bundle(path: bundlePath) + leftBarButtonItem = UIBarButtonItem(image: UIImage(named: "INSPhotoGalleryClose", in: bundle, compatibleWith: nil), landscapeImagePhone: UIImage(named: "INSPhotoGalleryCloseLandscape", in: bundle, compatibleWith: nil), style: .plain, target: self, action: #selector(INSPhotosOverlayView.closeButtonTapped(_:))) } else { - leftBarButtonItem = UIBarButtonItem(title: "CLOSE".uppercaseString, style: .Plain, target: self, action: #selector(INSPhotosOverlayView.closeButtonTapped(_:))) + leftBarButtonItem = UIBarButtonItem(title: "CLOSE".uppercased(), style: .plain, target: self, action: #selector(INSPhotosOverlayView.closeButtonTapped(_:))) } - rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Action, target: self, action: #selector(INSPhotosOverlayView.actionButtonTapped(_:))) + rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(INSPhotosOverlayView.actionButtonTapped(_:))) } private func setupCaptionLabel() { captionLabel = UILabel() captionLabel.translatesAutoresizingMaskIntoConstraints = false - captionLabel.backgroundColor = UIColor.clearColor() + captionLabel.backgroundColor = UIColor.clear captionLabel.numberOfLines = 0 addSubview(captionLabel) - let bottomConstraint = NSLayoutConstraint(item: self, attribute: .Bottom, relatedBy: .Equal, toItem: captionLabel, attribute: .Bottom, multiplier: 1.0, constant: 8.0) - let leadingConstraint = NSLayoutConstraint(item: captionLabel, attribute: .Leading, relatedBy: .Equal, toItem: self, attribute: .Leading, multiplier: 1.0, constant: 8.0) - let trailingConstraint = NSLayoutConstraint(item: captionLabel, attribute: .Trailing, relatedBy: .Equal, toItem: self, attribute: .Trailing, multiplier: 1.0, constant: 8.0) + let bottomConstraint = NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal, toItem: captionLabel, attribute: .bottom, multiplier: 1.0, constant: 8.0) + let leadingConstraint = NSLayoutConstraint(item: captionLabel, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1.0, constant: 8.0) + let trailingConstraint = NSLayoutConstraint(item: captionLabel, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1.0, constant: 8.0) self.addConstraints([bottomConstraint,leadingConstraint,trailingConstraint]) } -} \ No newline at end of file +} diff --git a/INSPhotoGallery/INSPhotosTransitionAnimator.swift b/INSPhotoGallery/INSPhotosTransitionAnimator.swift index f11a4ed..972c46d 100644 --- a/INSPhotoGallery/INSPhotosTransitionAnimator.swift +++ b/INSPhotoGallery/INSPhotosTransitionAnimator.swift @@ -53,23 +53,23 @@ class INSPhotosTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioni } } - func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval { + func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { if shouldPerformZoomingAnimation { return animationDurationWithZooming } return animationDurationWithoutZooming } - func fadeDurationForTransitionContext(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { + func fadeDurationForTransitionContext(_ transitionContext: UIViewControllerContextTransitioning) -> TimeInterval { if shouldPerformZoomingAnimation { - return transitionDuration(transitionContext) * animationDurationFadeRatio + return transitionDuration(using: transitionContext) * animationDurationFadeRatio } - return transitionDuration(transitionContext) + return transitionDuration(using: transitionContext) } // MARK:- UIViewControllerAnimatedTransitioning - func animateTransition(transitionContext: UIViewControllerContextTransitioning) { + func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { setupTransitionContainerHierarchyWithTransitionContext(transitionContext) // There is issue with startingView frame when performFadeAnimation @@ -81,33 +81,33 @@ class INSPhotosTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioni performFadeAnimationWithTransitionContext(transitionContext) } - func setupTransitionContainerHierarchyWithTransitionContext(transitionContext: UIViewControllerContextTransitioning) { + func setupTransitionContainerHierarchyWithTransitionContext(_ transitionContext: UIViewControllerContextTransitioning) { - if let toView = transitionContext.viewForKey(UITransitionContextToViewKey), - let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) { - toView.frame = transitionContext.finalFrameForViewController(toViewController) - let containerView = transitionContext.containerView() + if let toView = transitionContext.view(forKey: UITransitionContextViewKey.to), + let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) { + toView.frame = transitionContext.finalFrame(for: toViewController) + let containerView = transitionContext.containerView - if !toView.isDescendantOfView(containerView) { + if !toView.isDescendant(of: containerView) { containerView.addSubview(toView) } } if dismissing { - if let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey) { - transitionContext.containerView().bringSubviewToFront(fromView) + if let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) { + transitionContext.containerView.bringSubview(toFront: fromView) } } } - func performFadeAnimationWithTransitionContext(transitionContext: UIViewControllerContextTransitioning) { - let fadeView = dismissing ? transitionContext.viewForKey(UITransitionContextFromViewKey) : transitionContext.viewForKey(UITransitionContextToViewKey) + func performFadeAnimationWithTransitionContext(_ transitionContext: UIViewControllerContextTransitioning) { + let fadeView = dismissing ? transitionContext.view(forKey: UITransitionContextViewKey.from) : transitionContext.view(forKey: UITransitionContextViewKey.to) let beginningAlpha: CGFloat = dismissing ? 1.0 : 0.0 let endingAlpha: CGFloat = dismissing ? 0.0 : 1.0 fadeView?.alpha = beginningAlpha - UIView.animateWithDuration(fadeDurationForTransitionContext(transitionContext), animations: { () -> Void in + UIView.animate(withDuration: fadeDurationForTransitionContext(transitionContext), animations: { () -> Void in fadeView?.alpha = endingAlpha }) { finished in if !self.shouldPerformZoomingAnimation { @@ -116,9 +116,9 @@ class INSPhotosTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioni } } - func performZoomingAnimationWithTransitionContext(transitionContext: UIViewControllerContextTransitioning) { + func performZoomingAnimationWithTransitionContext(_ transitionContext: UIViewControllerContextTransitioning) { - let containerView = transitionContext.containerView() + let containerView = transitionContext.containerView guard let startingView = startingView, let endingView = endingView else { return } @@ -133,7 +133,7 @@ class INSPhotosTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioni startingViewForAnimation.center = translatedStartingViewCenter - endingViewForAnimation.transform = CGAffineTransformScale(endingViewForAnimation.transform, endingViewInitialTransform, endingViewInitialTransform) + endingViewForAnimation.transform = endingViewForAnimation.transform.scaledBy(x: endingViewInitialTransform, y: endingViewInitialTransform) endingViewForAnimation.center = translatedStartingViewCenter endingViewForAnimation.alpha = 0.0 @@ -144,14 +144,14 @@ class INSPhotosTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioni endingView.alpha = 0.0 startingView.alpha = 0.0 - let fadeInDuration = transitionDuration(transitionContext) * animationDurationEndingViewFadeInRatio - let fadeOutDuration = transitionDuration(transitionContext) * animationDurationStartingViewFadeOutRatio + let fadeInDuration = transitionDuration(using: transitionContext) * animationDurationEndingViewFadeInRatio + let fadeOutDuration = transitionDuration(using: transitionContext) * animationDurationStartingViewFadeOutRatio // Ending view / starting view replacement animation - UIView.animateWithDuration(fadeInDuration, delay: 0.0, options: [.AllowAnimatedContent,.BeginFromCurrentState], animations: { () -> Void in + UIView.animate(withDuration: fadeInDuration, delay: 0.0, options: [.allowAnimatedContent,.beginFromCurrentState], animations: { () -> Void in endingViewForAnimation.alpha = 1.0 }) { result in - UIView.animateWithDuration(fadeOutDuration, delay: 0.0, options: [.AllowAnimatedContent,.BeginFromCurrentState], animations: { () -> Void in + UIView.animate(withDuration: fadeOutDuration, delay: 0.0, options: [.allowAnimatedContent,.beginFromCurrentState], animations: { () -> Void in startingViewForAnimation.alpha = 0.0 }, completion: { result in startingViewForAnimation.removeFromSuperview() @@ -161,10 +161,10 @@ class INSPhotosTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioni let startingViewFinalTransform = 1.0 / endingViewInitialTransform let translatedEndingViewFinalCenter = endingView.ins_translatedCenterPointToContainerView(containerView) - UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, usingSpringWithDamping:CGFloat(zoomingAnimationSpringDamping), initialSpringVelocity:0, options: [.AllowAnimatedContent,.BeginFromCurrentState], animations: { () -> Void in + UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, usingSpringWithDamping:CGFloat(zoomingAnimationSpringDamping), initialSpringVelocity:0, options: [.allowAnimatedContent,.beginFromCurrentState], animations: { () -> Void in endingViewForAnimation.transform = finalEndingViewTransform endingViewForAnimation.center = translatedEndingViewFinalCenter - startingViewForAnimation.transform = CGAffineTransformScale(startingViewForAnimation.transform, startingViewFinalTransform, startingViewFinalTransform) + startingViewForAnimation.transform = startingViewForAnimation.transform.scaledBy(x: startingViewFinalTransform, y: startingViewFinalTransform) startingViewForAnimation.center = translatedEndingViewFinalCenter }) { result in @@ -175,14 +175,14 @@ class INSPhotosTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioni } } - func completeTransitionWithTransitionContext(transitionContext: UIViewControllerContextTransitioning) { - if transitionContext.isInteractive() { - if transitionContext.transitionWasCancelled() { + func completeTransitionWithTransitionContext(_ transitionContext: UIViewControllerContextTransitioning) { + if transitionContext.isInteractive { + if transitionContext.transitionWasCancelled { transitionContext.cancelInteractiveTransition() } else { transitionContext.finishInteractiveTransition() } } - transitionContext.completeTransition(!transitionContext.transitionWasCancelled()) + transitionContext.completeTransition(!transitionContext.transitionWasCancelled) } } diff --git a/INSPhotoGallery/INSPhotosViewController.swift b/INSPhotoGallery/INSPhotosViewController.swift index be54a30..9d9c09c 100644 --- a/INSPhotoGallery/INSPhotosViewController.swift +++ b/INSPhotoGallery/INSPhotosViewController.swift @@ -19,49 +19,49 @@ import UIKit -public typealias INSPhotosViewControllerReferenceViewHandler = (photo: INSPhotoViewable) -> (UIView?) -public typealias INSPhotosViewControllerNavigateToPhotoHandler = (photo: INSPhotoViewable) -> () -public typealias INSPhotosViewControllerDismissHandler = (viewController: INSPhotosViewController) -> () -public typealias INSPhotosViewControllerLongPressHandler = (photo: INSPhotoViewable, gestureRecognizer: UILongPressGestureRecognizer) -> (Bool) +public typealias INSPhotosViewControllerReferenceViewHandler = (_ photo: INSPhotoViewable) -> (UIView?) +public typealias INSPhotosViewControllerNavigateToPhotoHandler = (_ photo: INSPhotoViewable) -> () +public typealias INSPhotosViewControllerDismissHandler = (_ viewController: INSPhotosViewController) -> () +public typealias INSPhotosViewControllerLongPressHandler = (_ photo: INSPhotoViewable, _ gestureRecognizer: UILongPressGestureRecognizer) -> (Bool) -public class INSPhotosViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, UIViewControllerTransitioningDelegate { +open class INSPhotosViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, UIViewControllerTransitioningDelegate { /* * Returns the view from which to animate for object conforming to INSPhotoViewable */ - public var referenceViewForPhotoWhenDismissingHandler: INSPhotosViewControllerReferenceViewHandler? + open var referenceViewForPhotoWhenDismissingHandler: INSPhotosViewControllerReferenceViewHandler? /* * Called when a new photo is displayed through a swipe gesture. */ - public var navigateToPhotoHandler: INSPhotosViewControllerNavigateToPhotoHandler? + open var navigateToPhotoHandler: INSPhotosViewControllerNavigateToPhotoHandler? /* * Called before INSPhotosViewController will start a user-initiated dismissal. */ - public var willDismissHandler: INSPhotosViewControllerDismissHandler? + open var willDismissHandler: INSPhotosViewControllerDismissHandler? /* * Called after the INSPhotosViewController has been dismissed by the user. */ - public var didDismissHandler: INSPhotosViewControllerDismissHandler? + open var didDismissHandler: INSPhotosViewControllerDismissHandler? /* * Called when a photo is long pressed. */ - public var longPressGestureHandler: INSPhotosViewControllerLongPressHandler? + open var longPressGestureHandler: INSPhotosViewControllerLongPressHandler? /* * The overlay view displayed over photos, can be changed but must implement INSPhotosOverlayViewable */ - public var overlayView: INSPhotosOverlayViewable = INSPhotosOverlayView(frame: CGRect.zero) { + open var overlayView: INSPhotosOverlayViewable = INSPhotosOverlayView(frame: CGRect.zero) { willSet { overlayView.view().removeFromSuperview() } didSet { overlayView.photosViewController = self - overlayView.view().autoresizingMask = [.FlexibleWidth, .FlexibleHeight] + overlayView.view().autoresizingMask = [.flexibleWidth, .flexibleHeight] overlayView.view().frame = view.bounds view.addSubview(overlayView.view()) } @@ -70,14 +70,14 @@ public class INSPhotosViewController: UIViewController, UIPageViewControllerData /* * INSPhotoViewController is currently displayed by page view controller */ - public var currentPhotoViewController: INSPhotoViewController? { + open var currentPhotoViewController: INSPhotoViewController? { return pageViewController.viewControllers?.first as? INSPhotoViewController } /* * Photo object that is currently displayed by INSPhotoViewController */ - public var currentPhoto: INSPhotoViewable? { + open var currentPhoto: INSPhotoViewable? { return currentPhotoViewController?.photo } @@ -112,7 +112,7 @@ public class INSPhotosViewController: UIViewController, UIPageViewControllerData initialSetupWithInitialPhoto(nil) } - public override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) { + public override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: Bundle!) { dataSource = INSPhotosDataSource(photos: []) super.init(nibName: nil, bundle: nil) initialSetupWithInitialPhoto(nil) @@ -135,11 +135,11 @@ public class INSPhotosViewController: UIViewController, UIPageViewControllerData transitionAnimator.endingView = currentPhotoViewController?.scalingImageView.imageView } - private func initialSetupWithInitialPhoto(initialPhoto: INSPhotoViewable? = nil) { + private func initialSetupWithInitialPhoto(_ initialPhoto: INSPhotoViewable? = nil) { overlayView.photosViewController = self setupPageViewControllerWithInitialPhoto(initialPhoto) - modalPresentationStyle = .Custom + modalPresentationStyle = .custom transitioningDelegate = self modalPresentationCapturesStatusBarAppearance = true @@ -147,7 +147,7 @@ public class INSPhotosViewController: UIViewController, UIPageViewControllerData } private func setupOverlayViewInitialItems() { - let textColor = view.tintColor ?? UIColor.whiteColor() + let textColor = view.tintColor ?? UIColor.white if let overlayView = overlayView as? INSPhotosOverlayView { overlayView.photosViewController = self overlayView.titleTextAttributes = [NSForegroundColorAttributeName: textColor] @@ -156,30 +156,30 @@ public class INSPhotosViewController: UIViewController, UIPageViewControllerData // MARK: - View Life Cycle - override public func viewDidLoad() { + override open func viewDidLoad() { super.viewDidLoad() - view.tintColor = UIColor.whiteColor() - view.backgroundColor = UIColor.blackColor() - pageViewController.view.backgroundColor = UIColor.clearColor() + view.tintColor = UIColor.white + view.backgroundColor = UIColor.black + pageViewController.view.backgroundColor = UIColor.clear pageViewController.view.addGestureRecognizer(panGestureRecognizer) pageViewController.view.addGestureRecognizer(singleTapGestureRecognizer) addChildViewController(pageViewController) view.addSubview(pageViewController.view) - pageViewController.view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] - pageViewController.didMoveToParentViewController(self) + pageViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] + pageViewController.didMove(toParentViewController: self) setupOverlayView() } - public override func viewDidAppear(animated: Bool) { + open override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) // This fix issue that navigationBar animate to up // when presentingViewController is UINavigationViewController statusBarHidden = true - UIView.animateWithDuration(0.25) { () -> Void in + UIView.animate(withDuration: 0.25) { () -> Void in self.setNeedsStatusBarAppearanceUpdate() } } @@ -187,19 +187,19 @@ public class INSPhotosViewController: UIViewController, UIPageViewControllerData private func setupOverlayView() { updateCurrentPhotosInformation() - overlayView.view().autoresizingMask = [.FlexibleWidth, .FlexibleHeight] + overlayView.view().autoresizingMask = [.flexibleWidth, .flexibleHeight] overlayView.view().frame = view.bounds view.addSubview(overlayView.view()) overlayView.setHidden(true, animated: false) } - private func setupPageViewControllerWithInitialPhoto(initialPhoto: INSPhotoViewable? = nil) { - pageViewController = UIPageViewController(transitionStyle: .Scroll, navigationOrientation: .Horizontal, options: [UIPageViewControllerOptionInterPageSpacingKey: 16.0]) - pageViewController.view.backgroundColor = UIColor.clearColor() + private func setupPageViewControllerWithInitialPhoto(_ initialPhoto: INSPhotoViewable? = nil) { + pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [UIPageViewControllerOptionInterPageSpacingKey: 16.0]) + pageViewController.view.backgroundColor = UIColor.clear pageViewController.delegate = self pageViewController.dataSource = self - if let photo = initialPhoto where dataSource.containsPhoto(photo) { + if let photo = initialPhoto , dataSource.containsPhoto(photo) { changeToPhoto(photo, animated: false) } else if let photo = dataSource.photos.first { changeToPhoto(photo, animated: false) @@ -220,36 +220,36 @@ public class INSPhotosViewController: UIViewController, UIPageViewControllerData - parameter photo: The photo to make the currently displayed photo. - parameter animated: Whether to animate the transition to the new photo. */ - public func changeToPhoto(photo: INSPhotoViewable, animated: Bool) { + open func changeToPhoto(_ photo: INSPhotoViewable, animated: Bool) { if !dataSource.containsPhoto(photo) { return } let photoViewController = initializePhotoViewControllerForPhoto(photo) - pageViewController.setViewControllers([photoViewController], direction: .Forward, animated: animated, completion: nil) + pageViewController.setViewControllers([photoViewController], direction: .forward, animated: animated, completion: nil) updateCurrentPhotosInformation() } // MARK: - Gesture Recognizers - @objc private func handlePanGestureRecognizer(gestureRecognizer: UIPanGestureRecognizer) { - if gestureRecognizer.state == .Began { + @objc private func handlePanGestureRecognizer(_ gestureRecognizer: UIPanGestureRecognizer) { + if gestureRecognizer.state == .began { interactiveDismissal = true - dismissViewControllerAnimated(true, completion: nil) + dismiss(animated: true, completion: nil) } else { interactiveDismissal = false interactiveAnimator.handlePanWithPanGestureRecognizer(gestureRecognizer, viewToPan: pageViewController.view, anchorPoint: CGPoint(x: view.bounds.midX, y: view.bounds.midY)) } } - @objc private func handleSingleTapGestureRecognizer(gestureRecognizer: UITapGestureRecognizer) { - overlayView.setHidden(!overlayView.view().hidden, animated: true) + @objc private func handleSingleTapGestureRecognizer(_ gestureRecognizer: UITapGestureRecognizer) { + overlayView.setHidden(!overlayView.view().isHidden, animated: true) } // MARK: - View Controller Dismissal - public override func dismissViewControllerAnimated(flag: Bool, completion: (() -> Void)?) { + open override func dismiss(animated flag: Bool, completion: (() -> Void)?) { if presentedViewController != nil { - super.dismissViewControllerAnimated(flag, completion: completion) + super.dismiss(animated: flag, completion: completion) return } var startingView: UIView? @@ -259,23 +259,23 @@ public class INSPhotosViewController: UIViewController, UIPageViewControllerData transitionAnimator.startingView = startingView if let currentPhoto = currentPhoto { - transitionAnimator.endingView = referenceViewForPhotoWhenDismissingHandler?(photo: currentPhoto) + transitionAnimator.endingView = referenceViewForPhotoWhenDismissingHandler?(currentPhoto) } else { transitionAnimator.endingView = nil } - let overlayWasHiddenBeforeTransition = overlayView.view().hidden + let overlayWasHiddenBeforeTransition = overlayView.view().isHidden overlayView.setHidden(true, animated: true) - willDismissHandler?(viewController: self) + willDismissHandler?(self) - super.dismissViewControllerAnimated(flag) { () -> Void in + super.dismiss(animated: flag) { () -> Void in let isStillOnscreen = self.view.window != nil if isStillOnscreen && !overlayWasHiddenBeforeTransition { self.overlayView.setHidden(false, animated: true) } if !isStillOnscreen { - self.didDismissHandler?(viewController: self) + self.didDismissHandler?(self) } completion?() } @@ -283,9 +283,9 @@ public class INSPhotosViewController: UIViewController, UIPageViewControllerData // MARK: - UIPageViewControllerDataSource / UIPageViewControllerDelegate - private func initializePhotoViewControllerForPhoto(photo: INSPhotoViewable) -> INSPhotoViewController { + private func initializePhotoViewControllerForPhoto(_ photo: INSPhotoViewable) -> INSPhotoViewController { let photoViewController = INSPhotoViewController(photo: photo) - singleTapGestureRecognizer.requireGestureRecognizerToFail(photoViewController.doubleTapGestureRecognizer) + singleTapGestureRecognizer.require(toFail: photoViewController.doubleTapGestureRecognizer) photoViewController.longPressGestureHandler = { [weak self] gesture in guard let weakSelf = self else { return @@ -293,7 +293,7 @@ public class INSPhotosViewController: UIViewController, UIPageViewControllerData weakSelf.shouldHandleLongPressGesture = false if let gestureHandler = weakSelf.longPressGestureHandler { - weakSelf.shouldHandleLongPressGesture = gestureHandler(photo: photo, gestureRecognizer: gesture) + weakSelf.shouldHandleLongPressGesture = gestureHandler(photo, gesture) } weakSelf.shouldHandleLongPressGesture = !weakSelf.shouldHandleLongPressGesture @@ -301,17 +301,17 @@ public class INSPhotosViewController: UIViewController, UIPageViewControllerData guard let view = gesture.view else { return } - let menuController = UIMenuController.sharedMenuController() - var targetRect = CGRectZero - targetRect.origin = gesture.locationInView(view) - menuController.setTargetRect(targetRect, inView: view) + let menuController = UIMenuController.shared + var targetRect = CGRect.zero + targetRect.origin = gesture.location(in: view) + menuController.setTargetRect(targetRect, in: view) menuController.setMenuVisible(true, animated: true) } } return photoViewController } - @objc public func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? { + @objc open func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { guard let photoViewController = viewController as? INSPhotoViewController, let photoIndex = dataSource.indexOfPhoto(photoViewController.photo), let newPhoto = dataSource[photoIndex-1] else { @@ -320,7 +320,7 @@ public class INSPhotosViewController: UIViewController, UIPageViewControllerData return initializePhotoViewControllerForPhoto(newPhoto) } - @objc public func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? { + @objc open func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { guard let photoViewController = viewController as? INSPhotoViewController, let photoIndex = dataSource.indexOfPhoto(photoViewController.photo), let newPhoto = dataSource[photoIndex+1] else { @@ -329,28 +329,28 @@ public class INSPhotosViewController: UIViewController, UIPageViewControllerData return initializePhotoViewControllerForPhoto(newPhoto) } - @objc public func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { + @objc open func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { if completed { updateCurrentPhotosInformation() if let currentPhotoViewController = currentPhotoViewController { - navigateToPhotoHandler?(photo: currentPhotoViewController.photo) + navigateToPhotoHandler?(currentPhotoViewController.photo) } } } // MARK: - UIViewControllerTransitioningDelegate - public func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { + open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { transitionAnimator.dismissing = false return transitionAnimator } - public func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { + open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { transitionAnimator.dismissing = true return transitionAnimator } - public func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { + open func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { if interactiveDismissal { transitionAnimator.endingViewForAnimation = transitionAnimator.endingView?.ins_snapshotView() interactiveAnimator.animator = transitionAnimator @@ -364,36 +364,36 @@ public class INSPhotosViewController: UIViewController, UIPageViewControllerData // MARK: - UIResponder - @objc public override func copy(sender: AnyObject?) { - UIPasteboard.generalPasteboard().image = currentPhoto?.image ?? currentPhotoViewController?.scalingImageView.image + open override func copy(_ sender: Any?) { + UIPasteboard.general.image = currentPhoto?.image ?? currentPhotoViewController?.scalingImageView.image } - public override func canBecomeFirstResponder() -> Bool { + open override var canBecomeFirstResponder: Bool { return true } - public override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool { - if let _ = currentPhoto?.image ?? currentPhotoViewController?.scalingImageView.image where shouldHandleLongPressGesture && action == #selector(NSObject.copy) { + open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { + if let _ = currentPhoto?.image ?? currentPhotoViewController?.scalingImageView.image , shouldHandleLongPressGesture && action == #selector(NSObject.copy) { return true } return false } - + // MARK: - Status Bar - public override func prefersStatusBarHidden() -> Bool { - if let parentStatusBarHidden = presentingViewController?.prefersStatusBarHidden() where parentStatusBarHidden == true { + open override var prefersStatusBarHidden: Bool { + if let parentStatusBarHidden = presentingViewController?.prefersStatusBarHidden , parentStatusBarHidden == true { return parentStatusBarHidden } return statusBarHidden } - public override func preferredStatusBarStyle() -> UIStatusBarStyle { - return .Default + open override var preferredStatusBarStyle: UIStatusBarStyle { + return .default } - public override func preferredStatusBarUpdateAnimation() -> UIStatusBarAnimation { - return .Fade + open override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { + return .fade } } diff --git a/INSPhotoGallery/INSScalingImageView.swift b/INSPhotoGallery/INSScalingImageView.swift index 2d67220..e32bae7 100644 --- a/INSPhotoGallery/INSScalingImageView.swift +++ b/INSPhotoGallery/INSScalingImageView.swift @@ -18,6 +18,17 @@ // limitations under the License. import UIKit +private func < (lhs: T?, rhs: T?) -> Bool { + switch (lhs, rhs) { + case let (l?, r?): + return l < r + case (nil, _?): + return true + default: + return false + } +} + class INSScalingImageView: UIScrollView { lazy var imageView: UIImageView = { @@ -51,7 +62,7 @@ class INSScalingImageView: UIScrollView { updateZoomScale() } - override func didAddSubview(subview: UIView) { + override func didAddSubview(_ subview: UIView) { super.didAddSubview(subview) centerScrollViewContents() } @@ -71,8 +82,8 @@ class INSScalingImageView: UIScrollView { horizontalInset = (bounds.width - contentSize.width) * 0.5; } - if (self.contentSize.height < CGRectGetHeight(bounds)) { - verticalInset = (CGRectGetHeight(bounds) - contentSize.height) * 0.5; + if (self.contentSize.height < bounds.height) { + verticalInset = (bounds.height - contentSize.height) * 0.5; } if (window?.screen.scale < 2.0) { @@ -84,10 +95,10 @@ class INSScalingImageView: UIScrollView { self.contentInset = UIEdgeInsetsMake(verticalInset, horizontalInset, verticalInset, horizontalInset); } - private func updateImage(image: UIImage?) { + private func updateImage(_ image: UIImage?) { let size = image?.size ?? CGSize.zero - imageView.transform = CGAffineTransformIdentity + imageView.transform = CGAffineTransform.identity imageView.image = image imageView.frame = CGRect(origin: CGPoint.zero, size: size) self.contentSize = size @@ -114,7 +125,7 @@ class INSScalingImageView: UIScrollView { // // This is enabled in scrollViewWillBeginZooming so panning while zoomed-in // is unaffected. - self.panGestureRecognizer.enabled = false + self.panGestureRecognizer.isEnabled = false } } -} \ No newline at end of file +} diff --git a/INSPhotoGallery/UIVIew+INSPhotoViewer.swift b/INSPhotoGallery/UIVIew+INSPhotoViewer.swift index dc7eefc..ca4c2b6 100644 --- a/INSPhotoGallery/UIVIew+INSPhotoViewer.swift +++ b/INSPhotoGallery/UIVIew+INSPhotoViewer.swift @@ -25,7 +25,7 @@ extension UIView { var snapshotedView: UIView! if let view = self as? UIImageView { - snapshotedView = view.dynamicType.init(image: view.image) + snapshotedView = type(of: view).init(image: view.image) snapshotedView.bounds = view.bounds } else { snapshotedView = UIView(frame: frame) @@ -39,19 +39,19 @@ extension UIView { return snapshotedView } else { - return snapshotViewAfterScreenUpdates(true)! + return snapshotView(afterScreenUpdates: true)! } } - func ins_translatedCenterPointToContainerView(containerView: UIView) -> CGPoint { + func ins_translatedCenterPointToContainerView(_ containerView: UIView) -> CGPoint { var centerPoint = center // Special case for zoomed scroll views. - if let scrollView = self.superview as? UIScrollView where scrollView.zoomScale != 1.0 { + if let scrollView = self.superview as? UIScrollView , scrollView.zoomScale != 1.0 { centerPoint.x += (scrollView.bounds.width - scrollView.contentSize.width) / 2.0 + scrollView.contentOffset.x centerPoint.y += (scrollView.bounds.height - scrollView.contentSize.height) / 2.0 + scrollView.contentOffset.y } - return self.superview?.convertPoint(centerPoint, toView: containerView) ?? CGPoint.zero + return self.superview?.convert(centerPoint, to: containerView) ?? CGPoint.zero } }