├── .gitignore ├── .swift-version ├── .travis.yml ├── Example ├── LTAutoScrollView.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── LTAutoScrollView-Example.xcscheme ├── LTAutoScrollView.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── LTAutoScrollView │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── PushViewController.swift │ ├── Resource │ │ ├── image1.png │ │ ├── image2.png │ │ ├── pageControlCurrentDot@2x.png │ │ ├── pageControlCurrentDot@3x.png │ │ ├── pageControlDot@2x.png │ │ └── pageControlDot@3x.png │ └── ViewController.swift ├── Podfile ├── Podfile.lock ├── Pods │ ├── Kingfisher │ │ ├── LICENSE │ │ ├── README.md │ │ └── Sources │ │ │ ├── AnimatedImageView.swift │ │ │ ├── Box.swift │ │ │ ├── CacheSerializer.swift │ │ │ ├── Filter.swift │ │ │ ├── FormatIndicatedCacheSerializer.swift │ │ │ ├── Image.swift │ │ │ ├── ImageCache.swift │ │ │ ├── ImageDownloader.swift │ │ │ ├── ImageModifier.swift │ │ │ ├── ImagePrefetcher.swift │ │ │ ├── ImageProcessor.swift │ │ │ ├── ImageTransition.swift │ │ │ ├── ImageView+Kingfisher.swift │ │ │ ├── Indicator.swift │ │ │ ├── Kingfisher.h │ │ │ ├── Kingfisher.swift │ │ │ ├── KingfisherManager.swift │ │ │ ├── KingfisherOptionsInfo.swift │ │ │ ├── Placeholder.swift │ │ │ ├── RequestModifier.swift │ │ │ ├── Resource.swift │ │ │ ├── String+MD5.swift │ │ │ ├── ThreadHelper.swift │ │ │ └── UIButton+Kingfisher.swift │ ├── Local Podspecs │ │ └── LTAutoScrollView.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ └── project.pbxproj │ └── Target Support Files │ │ ├── Kingfisher │ │ ├── Info.plist │ │ ├── Kingfisher-dummy.m │ │ ├── Kingfisher-prefix.pch │ │ ├── Kingfisher-umbrella.h │ │ ├── Kingfisher.modulemap │ │ └── Kingfisher.xcconfig │ │ ├── LTAutoScrollView │ │ ├── Info.plist │ │ ├── LTAutoScrollView-dummy.m │ │ ├── LTAutoScrollView-prefix.pch │ │ ├── LTAutoScrollView-umbrella.h │ │ ├── LTAutoScrollView.modulemap │ │ └── LTAutoScrollView.xcconfig │ │ └── Pods-LTAutoScrollView_Example │ │ ├── Info.plist │ │ ├── Pods-LTAutoScrollView_Example-acknowledgements.markdown │ │ ├── Pods-LTAutoScrollView_Example-acknowledgements.plist │ │ ├── Pods-LTAutoScrollView_Example-dummy.m │ │ ├── Pods-LTAutoScrollView_Example-frameworks.sh │ │ ├── Pods-LTAutoScrollView_Example-resources.sh │ │ ├── Pods-LTAutoScrollView_Example-umbrella.h │ │ ├── Pods-LTAutoScrollView_Example.debug.xcconfig │ │ ├── Pods-LTAutoScrollView_Example.modulemap │ │ └── Pods-LTAutoScrollView_Example.release.xcconfig └── Tests │ ├── Info.plist │ └── Tests.swift ├── LICENSE ├── LTAutoScrollView.podspec ├── LTAutoScrollView ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ └── LTAutoScrollView.swift ├── README.md ├── _Pods.xcodeproj └── gltauto.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 26 | # Carthage/Checkouts 27 | 28 | Carthage/Build 29 | 30 | # We recommend against adding the Pods directory to your .gitignore. However 31 | # you should judge for yourself, the pros and cons are mentioned at: 32 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 33 | # 34 | # Note: if you ignore the Pods directory, make sure to uncomment 35 | # `pod install` in .travis.yml 36 | # 37 | # Pods/ 38 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/LTAutoScrollView.xcworkspace -scheme LTAutoScrollView-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /Example/LTAutoScrollView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/LTAutoScrollView.xcodeproj/xcshareddata/xcschemes/LTAutoScrollView-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 80 | 82 | 88 | 89 | 90 | 91 | 92 | 93 | 99 | 101 | 107 | 108 | 109 | 110 | 112 | 113 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /Example/LTAutoScrollView.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/LTAutoScrollView.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/LTAutoScrollView/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // LTAutoScrollView 4 | // 5 | // Created by 1282990794@qq.com on 04/14/2018. 6 | // Copyright (c) 2018 1282990794@qq.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | window?.rootViewController = UINavigationController(rootViewController: ViewController()) 20 | return true 21 | } 22 | 23 | func applicationWillResignActive(_ application: UIApplication) { 24 | // 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. 25 | // 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. 26 | } 27 | 28 | func applicationDidEnterBackground(_ application: UIApplication) { 29 | // 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. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | func applicationWillEnterForeground(_ application: UIApplication) { 34 | // 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. 35 | } 36 | 37 | func applicationDidBecomeActive(_ application: UIApplication) { 38 | // 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. 39 | } 40 | 41 | func applicationWillTerminate(_ application: UIApplication) { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Example/LTAutoScrollView/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/LTAutoScrollView/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Example/LTAutoScrollView/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/LTAutoScrollView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | LSRequiresIPhoneOS 29 | 30 | UILaunchStoryboardName 31 | LaunchScreen 32 | UIMainStoryboardFile 33 | Main 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationLandscapeLeft 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Example/LTAutoScrollView/PushViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PushViewController.swift 3 | // LTAutoScrollView_Example 4 | // 5 | // Created by 高刘通 on 2018/4/16. 6 | // Copyright © 2018年 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import LTAutoScrollView 11 | 12 | class PushViewController: UIViewController { 13 | 14 | private let images = ["image1", "image2" , "image1", "image2"] 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | view.backgroundColor = UIColor.white 19 | // Do any additional setup after loading the view. 20 | view.addSubview(autoScrollView) 21 | self.automaticallyAdjustsScrollViewInsets = false 22 | } 23 | 24 | override func didReceiveMemoryWarning() { 25 | super.didReceiveMemoryWarning() 26 | // Dispose of any resources that can be recreated. 27 | } 28 | 29 | 30 | /* 设置为系统的pageControl样式利用dotType */ 31 | private lazy var autoScrollView: LTAutoScrollView = { 32 | let autoScrollView = LTAutoScrollView(frame: CGRect(x: 0, y: 100, width: view.bounds.width, height: 150)) 33 | autoScrollView.glt_timeInterval = 1.5 34 | autoScrollView.images = images 35 | autoScrollView.imageHandle = {(imageView, imageName) in 36 | imageView.image = UIImage(named: imageName) 37 | } 38 | autoScrollView.didSelectItemHandle = { 39 | print("autoScrollView8 点击了第 \($0) 个索引") 40 | } 41 | 42 | let layout = LTDotLayout(dotColor: UIColor.white, dotSelectColor: UIColor.red, dotType: .default) 43 | /*设置dot的间距*/ 44 | layout.dotMargin = 8 45 | /* 如果需要改变dot的大小,设置dotWidth的宽度即可 */ 46 | layout.dotWidth = 10 47 | /*如需和系统一致,dot放大效果需手动关闭 */ 48 | layout.isScale = false 49 | 50 | autoScrollView.dotLayout = layout 51 | return autoScrollView 52 | }() 53 | 54 | } 55 | 56 | 57 | -------------------------------------------------------------------------------- /Example/LTAutoScrollView/Resource/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glthello/LTAutoScrollView/5bdf6b3b3c3c99df5313326406d1a02e51cbdd4d/Example/LTAutoScrollView/Resource/image1.png -------------------------------------------------------------------------------- /Example/LTAutoScrollView/Resource/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glthello/LTAutoScrollView/5bdf6b3b3c3c99df5313326406d1a02e51cbdd4d/Example/LTAutoScrollView/Resource/image2.png -------------------------------------------------------------------------------- /Example/LTAutoScrollView/Resource/pageControlCurrentDot@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glthello/LTAutoScrollView/5bdf6b3b3c3c99df5313326406d1a02e51cbdd4d/Example/LTAutoScrollView/Resource/pageControlCurrentDot@2x.png -------------------------------------------------------------------------------- /Example/LTAutoScrollView/Resource/pageControlCurrentDot@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glthello/LTAutoScrollView/5bdf6b3b3c3c99df5313326406d1a02e51cbdd4d/Example/LTAutoScrollView/Resource/pageControlCurrentDot@3x.png -------------------------------------------------------------------------------- /Example/LTAutoScrollView/Resource/pageControlDot@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glthello/LTAutoScrollView/5bdf6b3b3c3c99df5313326406d1a02e51cbdd4d/Example/LTAutoScrollView/Resource/pageControlDot@2x.png -------------------------------------------------------------------------------- /Example/LTAutoScrollView/Resource/pageControlDot@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glthello/LTAutoScrollView/5bdf6b3b3c3c99df5313326406d1a02e51cbdd4d/Example/LTAutoScrollView/Resource/pageControlDot@3x.png -------------------------------------------------------------------------------- /Example/LTAutoScrollView/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // LTAutoScrollView 4 | // 5 | // Created by 1282990794@qq.com on 04/14/2018. 6 | // Copyright (c) 2018 1282990794@qq.com. All rights reserved. 7 | // 8 | // 如有疑问,欢迎联系本人QQ: 1282990794 9 | // 10 | // UICollectionView 实现无限轮播功能,本框架不依赖任何第三方框架,Demo中使用Kingfisher加载,仅用于本Demo中测试,开发中可根据自己的实际需求进行更换 11 | // 12 | // github地址: https://github.com/gltwy/LTAutoScrollView 13 | // 14 | // clone地址: https://github.com/gltwy/LTAutoScrollView.git 15 | // 16 | // 支持cocoapods安装: pod 'LTAutoScrollView' 17 | // 18 | 19 | 20 | import UIKit 21 | import Kingfisher 22 | import LTAutoScrollView 23 | 24 | class ViewController: UIViewController { 25 | 26 | /* 用作本地图片展示 */ 27 | private let images = ["image1", "image2" , "image1", "image2"] 28 | 29 | /* pageControl未选中图片 */ 30 | private let dotImage = UIImage(named: "pageControlDot") 31 | 32 | /* pageControl选中图片 */ 33 | private let dotSelectImage = UIImage(named: "pageControlCurrentDot") 34 | 35 | /* 用作网络图片展示 */ 36 | private let imageUrls = [ 37 | "http://i2.hdslb.com/bfs/archive/41f5d8b1bb5c6a03e6740ab342c9461786d45c0a.jpg", 38 | "http://scimg.jb51.net/allimg/151113/14-15111310522aG.jpg" , 39 | "http://i2.hdslb.com/bfs/archive/41f5d8b1bb5c6a03e6740ab342c9461786d45c0a.jpg", 40 | "http://scimg.jb51.net/allimg/151113/14-15111310522aG.jpg" 41 | ] 42 | 43 | private lazy var scrollView: UIScrollView = { 44 | let scrollView = UIScrollView(frame: view.frame) 45 | scrollView.backgroundColor = UIColor.RGBA(0, 0, 0, 0.25) 46 | return scrollView 47 | }() 48 | 49 | /* 上下轮播利用scrollDirection控制 自定义pageControl 不设置dotLayout 则为隐藏pageControl */ 50 | private lazy var autoScrollView1: LTAutoScrollView = { 51 | //pageControl的dot设置,详情看内部属性说明 52 | let layout = LTDotLayout(dotWidth: 37/3.0, dotHeight: 19/3.0, dotMargin: 8, dotImage: dotImage, dotSelectImage: dotSelectImage) 53 | let autoScrollView = LTAutoScrollView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 150), dotLayout: layout) 54 | //设置滚动时间间隔 默认2.0s 55 | autoScrollView.glt_timeInterval = 1.5 56 | //设置轮播图的方向 默认水平 57 | autoScrollView.scrollDirection = .vertical 58 | //加载网络图片传入图片url数组, 加载本地图片传入图片名称数组 59 | autoScrollView.images = images 60 | //加载图片,内部不依赖任何图片加载框架 61 | autoScrollView.imageHandle = {(imageView, imageName) in 62 | //加载本地图片(根据传入的images数组来决定加载方式) 63 | imageView.image = UIImage(named: imageName) 64 | //加载网络图片(根据传入的images数组来决定加载方式) 65 | // imageView.kf.setImage(with: URL(string: imageName)) 66 | } 67 | // 滚动手势禁用(文字轮播较实用) 默认为false 68 | autoScrollView.isDisableScrollGesture = false 69 | //设置pageControl View的高度 默认为20 70 | autoScrollView.gltPageControlHeight = 20; 71 | //dot在轮播图的位置 中心 左侧 右侧 默认居中 72 | autoScrollView.dotDirection = .default 73 | //点击事件 74 | autoScrollView.didSelectItemHandle = { 75 | print("autoScrollView1 点击了第 \($0) 个索引") 76 | } 77 | //自动滚动到当前索引事件 78 | autoScrollView.autoDidSelectItemHandle = { index in 79 | print("autoScrollView1 自动滚动到了第 \(index) 个索引") 80 | } 81 | //PageControl点击事件 82 | autoScrollView.pageControlDidSelectIndexHandle = { index in 83 | print("autoScrollView1 pageControl点击了第 \(index) 个索引") 84 | } 85 | return autoScrollView 86 | }() 87 | 88 | /* 左右轮播 加载网络图片 */ 89 | private lazy var autoScrollView2: LTAutoScrollView = { 90 | 91 | let autoScrollView = LTAutoScrollView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 150)) 92 | autoScrollView.glt_timeInterval = 1.5 93 | autoScrollView.images = imageUrls 94 | autoScrollView.imageHandle = {(imageView, imageName) in 95 | imageView.kf.setImage(with: URL(string: imageName)) 96 | } 97 | // 是否自动轮播 默认true 98 | autoScrollView.isAutoScroll = true 99 | 100 | autoScrollView.didSelectItemHandle = {[weak self] in 101 | self?.navigationController?.pushViewController(PushViewController(), animated: true) 102 | print("autoScrollView2 点击了第 \($0) 个索引") 103 | } 104 | autoScrollView.pageControlDidSelectIndexHandle = { index in 105 | print("autoScrollView2 pageControl点击了第 \(index) 个索引") 106 | } 107 | let layout = LTDotLayout(dotImage: dotImage, dotSelectImage: dotSelectImage) 108 | layout.dotMargin = 10.0 109 | autoScrollView.dotLayout = layout 110 | return autoScrollView 111 | }() 112 | 113 | /* 左右轮播 自定义控件 autoType = custom控制 */ 114 | private lazy var autoScrollView3: LTAutoScrollView = { 115 | let autoScrollView = LTAutoScrollView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 150)) 116 | autoScrollView.glt_timeInterval = 1.5 117 | autoScrollView.autoViewHandle = { 118 | return self.customAutoView1() 119 | } 120 | autoScrollView.didSelectItemHandle = { 121 | print("autoScrollView3 点击了第 \($0) 个索引") 122 | } 123 | 124 | //设置pageControl的位置 125 | autoScrollView.dotDirection = .right 126 | //dot在轮播图的位置 左侧 或 右侧时,距离最屏幕最左边或最最右边的距离 127 | autoScrollView.adjustValue = 15.0 128 | //pageControl高度调整从而改变pageControl位置 129 | autoScrollView.gltPageControlHeight = 25 130 | 131 | let layout = LTDotLayout(dotImage: dotImage, dotSelectImage: dotSelectImage) 132 | autoScrollView.dotLayout = layout 133 | return autoScrollView 134 | }() 135 | 136 | /* 文字上下轮播两行 */ 137 | private lazy var autoScrollView4: LTAutoScrollView = { 138 | let autoScrollView = LTAutoScrollView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 60)) 139 | autoScrollView.glt_timeInterval = 1.5 140 | autoScrollView.scrollDirection = .vertical 141 | 142 | // 滚动手势禁用(文字轮播较实用) 143 | autoScrollView.isDisableScrollGesture = true 144 | 145 | autoScrollView.autoViewHandle = { 146 | return self.customAutoView2() 147 | } 148 | return autoScrollView 149 | }() 150 | 151 | /* 文字上下轮播一行 */ 152 | private lazy var autoScrollView5: LTAutoScrollView = { 153 | let autoScrollView = LTAutoScrollView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 30)) 154 | autoScrollView.glt_timeInterval = 1.5 155 | autoScrollView.scrollDirection = .vertical 156 | autoScrollView.isDisableScrollGesture = true 157 | autoScrollView.autoViewHandle = { 158 | return self.customAutoView3() 159 | } 160 | return autoScrollView 161 | }() 162 | 163 | /* 加载本地图片 */ 164 | private lazy var autoScrollView6: LTAutoScrollView = { 165 | let autoScrollView = LTAutoScrollView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 150)) 166 | autoScrollView.glt_timeInterval = 1.5 167 | autoScrollView.images = images 168 | autoScrollView.imageHandle = {(imageView, imageName) in 169 | imageView.image = UIImage(named: imageName) 170 | } 171 | autoScrollView.didSelectItemHandle = { 172 | print("autoScrollView6 点击了第 \($0) 个索引") 173 | } 174 | let layout = LTDotLayout(dotImage: dotImage, dotSelectImage: dotSelectImage) 175 | autoScrollView.dotLayout = layout 176 | return autoScrollView 177 | }() 178 | 179 | /* 隐藏pageControl */ 180 | private lazy var autoScrollView7: LTAutoScrollView = { 181 | let autoScrollView = LTAutoScrollView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 150)) 182 | autoScrollView.glt_timeInterval = 1.5 183 | autoScrollView.images = images 184 | autoScrollView.imageHandle = {(imageView, imageName) in 185 | imageView.image = UIImage(named: imageName) 186 | } 187 | autoScrollView.didSelectItemHandle = { 188 | print("autoScrollView7 点击了第 \($0) 个索引") 189 | } 190 | return autoScrollView 191 | }() 192 | 193 | /* 设置为系统的pageControl样式利用dotType */ 194 | private lazy var autoScrollView8: LTAutoScrollView = { 195 | let autoScrollView = LTAutoScrollView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 150)) 196 | autoScrollView.glt_timeInterval = 1.5 197 | autoScrollView.images = images 198 | autoScrollView.imageHandle = {(imageView, imageName) in 199 | imageView.image = UIImage(named: imageName) 200 | } 201 | autoScrollView.didSelectItemHandle = { 202 | print("autoScrollView8 点击了第 \($0) 个索引") 203 | } 204 | 205 | let layout = LTDotLayout(dotColor: UIColor.white, dotSelectColor: UIColor.red, dotType: .default) 206 | /*设置dot的间距*/ 207 | layout.dotMargin = 8 208 | /* 如果需要改变dot的大小,设置dotWidth的宽度即可 */ 209 | layout.dotWidth = 10 210 | /*如需和系统一致,dot放大效果需手动关闭 */ 211 | layout.isScale = false 212 | 213 | autoScrollView.dotLayout = layout 214 | return autoScrollView 215 | }() 216 | 217 | override func viewDidLoad() { 218 | super.viewDidLoad() 219 | view.addSubview(scrollView) 220 | view.backgroundColor = UIColor.white 221 | self.title = "轮播图" 222 | 223 | let lable1 = baseLabel(Y: 34, text: "autoScrollView1") 224 | scrollView.addSubview(autoScrollView1) 225 | autoScrollView1.frame.origin.y = nextAutoViewY(lable1) 226 | 227 | let lable2 = baseLabel(Y: nextItemY(autoScrollView1), text: "autoScrollView2") 228 | scrollView.addSubview(autoScrollView2) 229 | autoScrollView2.frame.origin.y = nextAutoViewY(lable2) 230 | 231 | 232 | let lable3 = baseLabel(Y: nextItemY(autoScrollView2), text: "autoScrollView3") 233 | scrollView.addSubview(autoScrollView3) 234 | autoScrollView3.frame.origin.y = nextAutoViewY(lable3) 235 | 236 | let lable4 = baseLabel(Y: nextItemY(autoScrollView3), text: "autoScrollView4") 237 | scrollView.addSubview(autoScrollView4) 238 | autoScrollView4.frame.origin.y = nextAutoViewY(lable4) 239 | 240 | let lable5 = baseLabel(Y: nextItemY(autoScrollView4), text: "autoScrollView5") 241 | scrollView.addSubview(autoScrollView5) 242 | autoScrollView5.frame.origin.y = nextAutoViewY(lable5) 243 | 244 | let lable6 = baseLabel(Y: nextItemY(autoScrollView5), text: "autoScrollView6") 245 | scrollView.addSubview(autoScrollView6) 246 | autoScrollView6.frame.origin.y = nextAutoViewY(lable6) 247 | 248 | let lable7 = baseLabel(Y: nextItemY(autoScrollView6), text: "autoScrollView7") 249 | scrollView.addSubview(autoScrollView7) 250 | autoScrollView7.frame.origin.y = nextAutoViewY(lable7) 251 | 252 | let lable8 = baseLabel(Y: nextItemY(autoScrollView7), text: "autoScrollView8") 253 | scrollView.addSubview(autoScrollView8) 254 | autoScrollView8.frame.origin.y = nextAutoViewY(lable8) 255 | 256 | scrollView.contentSize = CGSize(width: view.bounds.width, height: viewBottom(autoScrollView8) + 34); 257 | } 258 | 259 | override func didReceiveMemoryWarning() { 260 | super.didReceiveMemoryWarning() 261 | // Dispose of any resources that can be recreated. 262 | } 263 | 264 | } 265 | 266 | extension ViewController { 267 | 268 | private func customAutoView1() -> [UIImageView] { 269 | var views = [UIImageView]() 270 | for index in 0 ..< 3 { 271 | let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 150)) 272 | imageView.image = UIImage(named: "image\(index / 2 + 1)") 273 | views.append(imageView) 274 | 275 | let label1 = UILabel(frame: CGRect(x: 40, y: 40, width: 180, height: 25)) 276 | label1.textColor = UIColor.white 277 | label1.textAlignment = .center 278 | label1.backgroundColor = UIColor.RGBA(0, 0, 0, 0.45) 279 | label1.text = "自定义内部控件" 280 | imageView.addSubview(label1) 281 | 282 | let label2 = UILabel(frame: CGRect(x: 0, y: 125, width: view.bounds.width, height: 25)) 283 | label2.textColor = UIColor.white 284 | label2.backgroundColor = UIColor.RGBA(0, 0, 0, 0.45) 285 | label2.text = "自定义内部控件" 286 | imageView.addSubview(label2) 287 | } 288 | return views 289 | } 290 | 291 | private func customAutoView2() -> [UIView] { 292 | var views = [UIView]() 293 | let labelText = ["大家一起起来嗨!", "今天天气不错哦!", "厉害了我的哥!"] 294 | for index in 0 ..< labelText.count { 295 | let bottomView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 60)) 296 | views.append(bottomView) 297 | 298 | let label1 = UILabel(frame: CGRect(x: 10, y: 10, width: 180, height: 20)) 299 | label1.textColor = UIColor.white 300 | label1.backgroundColor = UIColor.RGBA(0, 0, 0, 0.45) 301 | label1.text = labelText[index] 302 | bottomView.addSubview(label1) 303 | 304 | let label2 = UILabel(frame: CGRect(x: 10, y: 30, width: view.bounds.width, height: 20)) 305 | label2.textColor = UIColor.white 306 | label2.backgroundColor = UIColor.RGBA(0, 0, 0, 0.45) 307 | label2.text = "不错哦!哈哈哈哈" 308 | bottomView.addSubview(label2) 309 | } 310 | return views 311 | } 312 | 313 | private func customAutoView3() -> [UIView] { 314 | var views = [UIView]() 315 | let labelText = ["大家一起起来嗨!", "今天天气不错哦!", "厉害了我的哥!"] 316 | for index in 0 ..< labelText.count { 317 | let bottomView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 60)) 318 | views.append(bottomView) 319 | 320 | let label1 = UILabel(frame: CGRect(x: 10, y: 0, width: 180, height: 30)) 321 | label1.textColor = UIColor.white 322 | label1.backgroundColor = UIColor.RGBA(0, 0, 0, 0.45) 323 | label1.text = labelText[index] 324 | bottomView.addSubview(label1) 325 | } 326 | return views 327 | } 328 | } 329 | 330 | extension ViewController { 331 | @discardableResult 332 | private func baseLabel(Y: CGFloat, text: String?) -> UILabel { 333 | let label = UILabel(frame: CGRect(x: 0, y: Y, width: view.bounds.width, height: 25)) 334 | label.textColor = UIColor.white 335 | label.backgroundColor = UIColor.RGBA(0, 0, 0, 0.45) 336 | label.text = text 337 | scrollView.addSubview(label) 338 | return label 339 | } 340 | 341 | private func viewBottom(_ view: UIView) -> CGFloat { 342 | return view.frame.origin.y + view.bounds.height 343 | } 344 | 345 | private func nextItemY(_ view: UIView) -> CGFloat { 346 | return viewBottom(view) + 15 347 | } 348 | 349 | private func nextAutoViewY(_ lable: UILabel) -> CGFloat { 350 | return viewBottom(lable) + 1 351 | } 352 | } 353 | 354 | extension UIColor { 355 | static func RGBA(_ R:CGFloat, _ G:CGFloat, _ B:CGFloat, _ alpha:CGFloat) -> UIColor{ 356 | let color = UIColor.init(red: (R / 255.0), green: (G / 255.0), blue: (B / 255.0), alpha: alpha); 357 | return color; 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'LTAutoScrollView_Example' do 4 | 5 | pod 'Kingfisher', '~> 4.7.0' 6 | pod 'LTAutoScrollView', :path => '../' 7 | 8 | # target 'LTAutoScrollView_Tests' do 9 | # inherit! :search_paths 10 | # 11 | # 12 | # end 13 | end 14 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Kingfisher (4.7.0) 3 | - LTAutoScrollView (0.1.0) 4 | 5 | DEPENDENCIES: 6 | - Kingfisher (~> 4.7.0) 7 | - LTAutoScrollView (from `../`) 8 | 9 | EXTERNAL SOURCES: 10 | LTAutoScrollView: 11 | :path: "../" 12 | 13 | SPEC CHECKSUMS: 14 | Kingfisher: da6b005aa96d37698e3e4f1ccfe96a5b9bbf27d6 15 | LTAutoScrollView: 457e1fb733fd4ba33d36d4dd6e21a07891022c1d 16 | 17 | PODFILE CHECKSUM: 084a963cfe4e84dec4a03d4df38492d4e14e1424 18 | 19 | COCOAPODS: 1.2.0 20 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Wei Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Kingfisher 4 | 5 |

6 | 7 |

8 | 9 | 10 | 11 | 12 | 13 | codebeat badge 14 | 15 | 16 |

17 | 18 | Kingfisher is a lightweight, pure-Swift library for downloading and caching images from the web. This project is heavily inspired by the popular [SDWebImage](https://github.com/rs/SDWebImage). It provides you a chance to use a pure-Swift alternative in your next app. 19 | 20 | ## Features 21 | 22 | - [x] Asynchronous image downloading and caching. 23 | - [x] `URLSession`-based networking. Basic image processors and filters supplied. 24 | - [x] Multiple-layer cache for both memory and disk. 25 | - [x] Cancelable downloading and processing tasks to improve performance. 26 | - [x] Independent components. Use the downloader or caching system separately as you need. 27 | - [x] Prefetching images and showing them from cache later when necessary. 28 | - [x] Extensions for `UIImageView`, `NSImage` and `UIButton` to directly set an image from a URL. 29 | - [x] Built-in transition animation when setting images. 30 | - [x] Customizable placeholder while loading images. 31 | - [x] Extensible image processing and image format support. 32 | 33 | The simplest use-case is setting an image to an image view with the `UIImageView` extension: 34 | 35 | ```swift 36 | let url = URL(string: "url_of_your_image") 37 | imageView.kf.setImage(with: url) 38 | ``` 39 | 40 | Kingfisher will download the image from `url`, send it to both the memory cache and the disk cache, and display it in `imageView`. When you use the same code later, the image will be retrieved from cache and shown immediately. 41 | 42 | For more examples of using Kingfisher, take a look at the [Cheat Sheet](https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet). 43 | 44 | ## Requirements 45 | 46 | - iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+ 47 | - Swift 4 (Kingfisher 4.x), Swift 3 (Kingfisher 3.x) 48 | 49 | Main development of Kingfisher is based on Swift 4. Only critical bug fixes will be applied to Kingfisher 3.x. 50 | 51 | - Kingfisher 4.0 Migration - Kingfisher 3.x should be source compatible to Kingfisher 4. The reason for a major update is that we need to specify the Swift version explicitly for Xcode. All deprecated methods in Kingfisher 3 has been removed, so please ensure you have no warning left before you migrate from Kingfisher 3 to Kingfisher 4. If you have any trouble in migrating, please open an issue to discuss. 52 | - [Kingfisher 3.0 Migration Guide](https://github.com/onevcat/Kingfisher/wiki/Kingfisher-3.0-Migration-Guide) - If you are upgrading to Kingfisher 3.x from an earlier version, please read this for more information. 53 | 54 | ## Next Steps 55 | 56 | We prepared a [wiki page](https://github.com/onevcat/Kingfisher/wiki). You can find tons of useful things there. 57 | 58 | * [Installation Guide](https://github.com/onevcat/Kingfisher/wiki/Installation-Guide) - Follow it to integrate Kingfisher into your project. 59 | * [Cheat Sheet](https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet)- Curious about what Kingfisher could do and how would it look like when used in your project? See this page for useful code snippets. If you are already familiar with Kingfisher, you could also learn new tricks to improve the way you use Kingfisher! 60 | * [API Reference](http://onevcat.github.io/Kingfisher/) - Lastly, please remember to read the full whenever you may need a more detailed reference. 61 | 62 | ## Other 63 | 64 | ### Future of Kingfisher 65 | 66 | I want to keep Kingfisher lightweight. This framework will focus on providing a simple solution for downloading and caching images. This doesn’t mean the framework can’t be improved. Kingfisher is far from perfect, so necessary and useful updates will be made to make it better. 67 | 68 | ### Developments and Tests 69 | 70 | Any contributing and pull requests are warmly welcome. However, before you plan to implement some features or try to fix an uncertain issue, it is recommended to open a discussion first. 71 | 72 | The test images are contained in another project to keep this project repo fast and slim. You could run `./setup.sh` in the root folder of Kingfisher to clone the test images when you need to run the tests target. It would be appreciated if your pull requests could build and with all tests green. :) 73 | 74 | ### About the logo 75 | 76 | 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 suggestions? 77 | 78 | ### Contact 79 | 80 | 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). Pull requests are warmly welcome as well. 81 | 82 | ## Contributors 83 | 84 | This project exists thanks to all the people who contribute. [[Contribute]](https://github.com/onevcat/Kingfisher/blob/master/CONTRIBUTING.md). 85 | 86 | 87 | 88 | ## Backers 89 | 90 | Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/kingfisher#backer)] 91 | 92 | 93 | 94 | 95 | ## Sponsors 96 | 97 | Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/kingfisher#sponsor)] 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | ### License 113 | 114 | Kingfisher is released under the MIT license. See LICENSE for details. 115 | 116 | 117 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Box.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Box.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2018/3/17. 6 | // Copyright (c) 2018 Wei Wang 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | import Foundation 27 | 28 | class Box { 29 | let value: T 30 | 31 | init(_ value: T) { 32 | self.value = value 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/CacheSerializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CacheSerializer.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2016/09/02. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// An `CacheSerializer` would be used to convert some data to an image object for 30 | /// retrieving from disk cache and vice versa for storing to disk cache. 31 | public protocol CacheSerializer { 32 | 33 | /// Get the serialized data from a provided image 34 | /// and optional original data for caching to disk. 35 | /// 36 | /// 37 | /// - parameter image: The image needed to be serialized. 38 | /// - parameter original: The original data which is just downloaded. 39 | /// If the image is retrieved from cache instead of 40 | /// downloaded, it will be `nil`. 41 | /// 42 | /// - returns: A data which will be stored to cache, or `nil` when no valid 43 | /// data could be serialized. 44 | func data(with image: Image, original: Data?) -> Data? 45 | 46 | /// Get an image deserialized from provided data. 47 | /// 48 | /// - parameter data: The data from which an image should be deserialized. 49 | /// - parameter options: Options for deserialization. 50 | /// 51 | /// - returns: An image deserialized or `nil` when no valid image 52 | /// could be deserialized. 53 | func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? 54 | } 55 | 56 | 57 | /// `DefaultCacheSerializer` is a basic `CacheSerializer` used in default cache of 58 | /// Kingfisher. It could serialize and deserialize PNG, JEPG and GIF images. For 59 | /// image other than these formats, a normalized `pngRepresentation` will be used. 60 | public struct DefaultCacheSerializer: CacheSerializer { 61 | 62 | public static let `default` = DefaultCacheSerializer() 63 | private init() {} 64 | 65 | public func data(with image: Image, original: Data?) -> Data? { 66 | let imageFormat = original?.kf.imageFormat ?? .unknown 67 | 68 | let data: Data? 69 | switch imageFormat { 70 | case .PNG: data = image.kf.pngRepresentation() 71 | case .JPEG: data = image.kf.jpegRepresentation(compressionQuality: 1.0) 72 | case .GIF: data = image.kf.gifRepresentation() 73 | case .unknown: data = original ?? image.kf.normalized.kf.pngRepresentation() 74 | } 75 | 76 | return data 77 | } 78 | 79 | public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? { 80 | let options = options ?? KingfisherEmptyOptionsInfo 81 | return Kingfisher.image( 82 | data: data, 83 | scale: options.scaleFactor, 84 | preloadAllAnimationData: options.preloadAllAnimationData, 85 | onlyFirstFrame: options.onlyLoadFirstFrame) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Filter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Filter.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2016/08/31. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | 28 | 29 | import CoreImage 30 | import Accelerate 31 | 32 | // Reuse the same CI Context for all CI drawing. 33 | private let ciContext = CIContext(options: nil) 34 | 35 | /// Transformer method which will be used in to provide a `Filter`. 36 | public typealias Transformer = (CIImage) -> CIImage? 37 | 38 | /// Supply a filter to create an `ImageProcessor`. 39 | public protocol CIImageProcessor: ImageProcessor { 40 | var filter: Filter { get } 41 | } 42 | 43 | extension CIImageProcessor { 44 | public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { 45 | switch item { 46 | case .image(let image): 47 | return image.kf.apply(filter) 48 | case .data(_): 49 | return (DefaultImageProcessor.default >> self).process(item: item, options: options) 50 | } 51 | } 52 | } 53 | 54 | /// Wrapper for a `Transformer` of CIImage filters. 55 | public struct Filter { 56 | 57 | let transform: Transformer 58 | 59 | public init(tranform: @escaping Transformer) { 60 | self.transform = tranform 61 | } 62 | 63 | /// Tint filter which will apply a tint color to images. 64 | public static var tint: (Color) -> Filter = { 65 | color in 66 | Filter { input in 67 | let colorFilter = CIFilter(name: "CIConstantColorGenerator")! 68 | colorFilter.setValue(CIColor(color: color), forKey: kCIInputColorKey) 69 | 70 | let colorImage = colorFilter.outputImage 71 | let filter = CIFilter(name: "CISourceOverCompositing")! 72 | filter.setValue(colorImage, forKey: kCIInputImageKey) 73 | filter.setValue(input, forKey: kCIInputBackgroundImageKey) 74 | #if swift(>=4.0) 75 | return filter.outputImage?.cropped(to: input.extent) 76 | #else 77 | return filter.outputImage?.cropping(to: input.extent) 78 | #endif 79 | } 80 | } 81 | 82 | public typealias ColorElement = (CGFloat, CGFloat, CGFloat, CGFloat) 83 | 84 | /// Color control filter which will apply color control change to images. 85 | public static var colorControl: (ColorElement) -> Filter = { arg -> Filter in 86 | let (brightness, contrast, saturation, inputEV) = arg 87 | return Filter { input in 88 | let paramsColor = [kCIInputBrightnessKey: brightness, 89 | kCIInputContrastKey: contrast, 90 | kCIInputSaturationKey: saturation] 91 | 92 | let paramsExposure = [kCIInputEVKey: inputEV] 93 | #if swift(>=4.0) 94 | let blackAndWhite = input.applyingFilter("CIColorControls", parameters: paramsColor) 95 | return blackAndWhite.applyingFilter("CIExposureAdjust", parameters: paramsExposure) 96 | #else 97 | let blackAndWhite = input.applyingFilter("CIColorControls", withInputParameters: paramsColor) 98 | return blackAndWhite.applyingFilter("CIExposureAdjust", withInputParameters: paramsExposure) 99 | #endif 100 | } 101 | 102 | } 103 | } 104 | 105 | extension Kingfisher where Base: Image { 106 | /// Apply a `Filter` containing `CIImage` transformer to `self`. 107 | /// 108 | /// - parameter filter: The filter used to transform `self`. 109 | /// 110 | /// - returns: A transformed image by input `Filter`. 111 | /// 112 | /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned. 113 | public func apply(_ filter: Filter) -> Image { 114 | 115 | guard let cgImage = cgImage else { 116 | assertionFailure("[Kingfisher] Tint image only works for CG-based image.") 117 | return base 118 | } 119 | 120 | let inputImage = CIImage(cgImage: cgImage) 121 | guard let outputImage = filter.transform(inputImage) else { 122 | return base 123 | } 124 | 125 | guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else { 126 | assertionFailure("[Kingfisher] Can not make an tint image within context.") 127 | return base 128 | } 129 | 130 | #if os(macOS) 131 | return fixedForRetinaPixel(cgImage: result, to: size) 132 | #else 133 | return Image(cgImage: result, scale: base.scale, orientation: base.imageOrientation) 134 | #endif 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/FormatIndicatedCacheSerializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestModifier.swift 3 | // Kingfisher 4 | // 5 | // Created by Junyu Kuang on 5/28/17. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// `FormatIndicatedCacheSerializer` let you indicate an image format for serialized caches. 30 | /// 31 | /// It could serialize and deserialize PNG, JEPG and GIF images. For 32 | /// image other than these formats, a normalized `pngRepresentation` will be used. 33 | /// 34 | /// Example: 35 | /// ```` 36 | /// private let profileImageSize = CGSize(width: 44, height: 44) 37 | /// 38 | /// private let imageProcessor = RoundCornerImageProcessor( 39 | /// cornerRadius: profileImageSize.width / 2, targetSize: profileImageSize) 40 | /// 41 | /// private let optionsInfo: KingfisherOptionsInfo = [ 42 | /// .cacheSerializer(FormatIndicatedCacheSerializer.png), 43 | /// .backgroundDecode, .processor(imageProcessor), .scaleFactor(UIScreen.main.scale)] 44 | /// 45 | /// extension UIImageView { 46 | /// func setProfileImage(with url: URL) { 47 | /// // Image will always cached as PNG format to preserve alpha channel for round rect. 48 | /// _ = kf.setImage(with: url, options: optionsInfo) 49 | /// } 50 | ///} 51 | /// ```` 52 | public struct FormatIndicatedCacheSerializer: CacheSerializer { 53 | 54 | public static let png = FormatIndicatedCacheSerializer(imageFormat: .PNG) 55 | public static let jpeg = FormatIndicatedCacheSerializer(imageFormat: .JPEG) 56 | public static let gif = FormatIndicatedCacheSerializer(imageFormat: .GIF) 57 | 58 | /// The indicated image format. 59 | private let imageFormat: ImageFormat 60 | 61 | public func data(with image: Image, original: Data?) -> Data? { 62 | 63 | func imageData(withFormat imageFormat: ImageFormat) -> Data? { 64 | switch imageFormat { 65 | case .PNG: return image.kf.pngRepresentation() 66 | case .JPEG: return image.kf.jpegRepresentation(compressionQuality: 1.0) 67 | case .GIF: return image.kf.gifRepresentation() 68 | case .unknown: return nil 69 | } 70 | } 71 | 72 | // generate data with indicated image format 73 | if let data = imageData(withFormat: imageFormat) { 74 | return data 75 | } 76 | 77 | let originalFormat = original?.kf.imageFormat ?? .unknown 78 | 79 | // generate data with original image's format 80 | if originalFormat != imageFormat, let data = imageData(withFormat: originalFormat) { 81 | return data 82 | } 83 | 84 | return original ?? image.kf.normalized.kf.pngRepresentation() 85 | } 86 | 87 | /// Same implementation as `DefaultCacheSerializer`. 88 | public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? { 89 | let options = options ?? KingfisherEmptyOptionsInfo 90 | return Kingfisher.image( 91 | data: data, 92 | scale: options.scaleFactor, 93 | preloadAllAnimationData: options.preloadAllAnimationData, 94 | onlyFirstFrame: options.onlyLoadFirstFrame) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/ImageModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageModifier.swift 3 | // Kingfisher 4 | // 5 | // Created by Ethan Gill on 2017/11/28. 6 | // 7 | // Copyright (c) 2018 Ethan Gill 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// An `ImageModifier` can be used to change properties on an Image in between 30 | /// cache serialization and use of the image. 31 | public protocol ImageModifier { 32 | /// Modify an input `Image`. 33 | /// 34 | /// - parameter image: Image which will be modified by `self` 35 | /// 36 | /// - returns: The modified image. 37 | /// 38 | /// - Note: The return value will be unmodified if modifying is not possible on 39 | /// the current platform. 40 | /// - Note: Most modifiers support UIImage or NSImage, but not CGImage. 41 | func modify(_ image: Image) -> Image 42 | } 43 | 44 | extension ImageModifier { 45 | func modify(_ image: Image?) -> Image? { 46 | guard let image = image else { 47 | return nil 48 | } 49 | return modify(image) 50 | } 51 | } 52 | 53 | typealias ModifierImp = ((Image) -> Image) 54 | 55 | fileprivate struct GeneralModifier: ImageModifier { 56 | let identifier: String 57 | let m: ModifierImp 58 | func modify(_ image: Image) -> Image { 59 | return m(image) 60 | } 61 | } 62 | 63 | /// The default modifier. 64 | /// Does nothing and returns the image it was given 65 | public struct DefaultImageModifier: ImageModifier { 66 | 67 | /// A default `DefaultImageModifier` which can be used everywhere. 68 | public static let `default` = DefaultImageModifier() 69 | 70 | /// Initialize a `DefaultImageModifier` 71 | private init() {} 72 | 73 | /// Modify an input `Image`. 74 | /// 75 | /// - parameter image: Image which will be modified by `self` 76 | /// 77 | /// - returns: The modified image. 78 | /// 79 | /// - Note: See documentation of `ImageModifier` protocol for more. 80 | public func modify(_ image: Image) -> Image { 81 | return image 82 | } 83 | } 84 | 85 | /// A custom modifier. 86 | /// Can be initialized with a block to modify images in a custom way 87 | public struct AnyImageModifier: ImageModifier { 88 | 89 | /// A block which modifies images, or returns the original image 90 | /// if modification cannot be performed. 91 | let block: (Image) -> Image 92 | 93 | /// Initialize an `AnyImageModifier` 94 | public init(modify: @escaping (Image) -> Image) { 95 | block = modify 96 | } 97 | 98 | /// Modifies an input `Image` using this `AnyImageModifier`'s `block`. 99 | /// 100 | /// - parameter image: Image which will be modified by `self` 101 | /// 102 | /// - returns: The modified image. 103 | /// 104 | /// - Note: See documentation of `ImageModifier` protocol for more. 105 | public func modify(_ image: Image) -> Image { 106 | return block(image) 107 | } 108 | } 109 | 110 | #if os(iOS) || os(tvOS) || os(watchOS) 111 | import UIKit 112 | 113 | /// Modifier for setting the rendering mode of images. 114 | /// Only UI-based images are supported; if a non-UI image is passed in, the 115 | /// modifier will do nothing. 116 | public struct RenderingModeImageModifier: ImageModifier { 117 | 118 | /// The rendering mode to apply to the image. 119 | public let renderingMode: UIImageRenderingMode 120 | 121 | /// Initialize a `RenderingModeImageModifier` 122 | /// 123 | /// - parameter renderingMode: The rendering mode to apply to the image. 124 | /// Default is .automatic 125 | public init(renderingMode: UIImageRenderingMode = .automatic) { 126 | self.renderingMode = renderingMode 127 | } 128 | 129 | /// Modify an input `Image`. 130 | /// 131 | /// - parameter image: Image which will be modified by `self` 132 | /// 133 | /// - returns: The modified image. 134 | /// 135 | /// - Note: See documentation of `ImageModifier` protocol for more. 136 | public func modify(_ image: Image) -> Image { 137 | return image.withRenderingMode(renderingMode) 138 | } 139 | } 140 | 141 | /// Modifier for setting the `flipsForRightToLeftLayoutDirection` property of images. 142 | /// Only UI-based images are supported; if a non-UI image is passed in, the 143 | /// modifier will do nothing. 144 | public struct FlipsForRightToLeftLayoutDirectionImageModifier: ImageModifier { 145 | /// Initialize a `FlipsForRightToLeftLayoutDirectionImageModifier` 146 | /// 147 | /// - Note: On versions of iOS lower than 9.0, the image will be returned 148 | /// unmodified. 149 | public init() {} 150 | 151 | /// Modify an input `Image`. 152 | /// 153 | /// - parameter image: Image which will be modified by `self` 154 | /// 155 | /// - returns: The modified image. 156 | /// 157 | /// - Note: See documentation of `ImageModifier` protocol for more. 158 | public func modify(_ image: Image) -> Image { 159 | if #available(iOS 9.0, *) { 160 | return image.imageFlippedForRightToLeftLayoutDirection() 161 | } else { 162 | return image 163 | } 164 | } 165 | } 166 | 167 | /// Modifier for setting the `alignmentRectInsets` property of images. 168 | /// Only UI-based images are supported; if a non-UI image is passed in, the 169 | /// modifier will do nothing. 170 | public struct AlignmentRectInsetsImageModifier: ImageModifier { 171 | 172 | /// The alignment insets to apply to the image 173 | public let alignmentInsets: UIEdgeInsets 174 | 175 | /// Initialize a `AlignmentRectInsetsImageModifier` 176 | public init(alignmentInsets: UIEdgeInsets) { 177 | self.alignmentInsets = alignmentInsets 178 | } 179 | 180 | /// Modify an input `Image`. 181 | /// 182 | /// - parameter image: Image which will be modified by `self` 183 | /// 184 | /// - returns: The modified image. 185 | /// 186 | /// - Note: See documentation of `ImageModifier` protocol for more. 187 | public func modify(_ image: Image) -> Image { 188 | return image.withAlignmentRectInsets(alignmentInsets) 189 | } 190 | } 191 | #endif 192 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/ImagePrefetcher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImagePrefetcher.swift 3 | // Kingfisher 4 | // 5 | // Created by Claire Knight on 24/02/2016 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | 28 | #if os(macOS) 29 | import AppKit 30 | #else 31 | import UIKit 32 | #endif 33 | 34 | 35 | /// Progress update block of prefetcher. 36 | /// 37 | /// - `skippedResources`: An array of resources that are already cached before the prefetching starting. 38 | /// - `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. 39 | /// - `completedResources`: An array of resources that are downloaded and cached successfully. 40 | public typealias PrefetcherProgressBlock = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> Void) 41 | 42 | /// Completion block of prefetcher. 43 | /// 44 | /// - `skippedResources`: An array of resources that are already cached before the prefetching starting. 45 | /// - `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. 46 | /// - `completedResources`: An array of resources that are downloaded and cached successfully. 47 | public typealias PrefetcherCompletionHandler = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> Void) 48 | 49 | /// `ImagePrefetcher` represents a downloading manager for requesting many images via URLs, then caching them. 50 | /// This is useful when you know a list of image resources and want to download them before showing. 51 | public class ImagePrefetcher { 52 | 53 | /// The maximum concurrent downloads to use when prefetching images. Default is 5. 54 | public var maxConcurrentDownloads = 5 55 | 56 | private let prefetchResources: [Resource] 57 | private let optionsInfo: KingfisherOptionsInfo 58 | private var progressBlock: PrefetcherProgressBlock? 59 | private var completionHandler: PrefetcherCompletionHandler? 60 | 61 | private var tasks = [URL: RetrieveImageDownloadTask]() 62 | 63 | private var pendingResources: ArraySlice 64 | private var skippedResources = [Resource]() 65 | private var completedResources = [Resource]() 66 | private var failedResources = [Resource]() 67 | 68 | private var stopped = false 69 | 70 | // The created manager used for prefetch. We will use the helper method in manager. 71 | private let manager: KingfisherManager 72 | 73 | private var finished: Bool { 74 | return failedResources.count + skippedResources.count + completedResources.count == prefetchResources.count && self.tasks.isEmpty 75 | } 76 | 77 | /** 78 | Init an image prefetcher with an array of URLs. 79 | 80 | The prefetcher should be initiated with a list of prefetching targets. The URLs list is immutable. 81 | After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process. 82 | The images already cached will be skipped without downloading again. 83 | 84 | - parameter urls: The URLs which should be prefetched. 85 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. 86 | - parameter progressBlock: Called every time an resource is downloaded, skipped or cancelled. 87 | - parameter completionHandler: Called when the whole prefetching process finished. 88 | 89 | - returns: An `ImagePrefetcher` object. 90 | 91 | - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as 92 | the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`. 93 | Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method. 94 | */ 95 | public convenience init(urls: [URL], 96 | options: KingfisherOptionsInfo? = nil, 97 | progressBlock: PrefetcherProgressBlock? = nil, 98 | completionHandler: PrefetcherCompletionHandler? = nil) 99 | { 100 | let resources: [Resource] = urls.map { $0 } 101 | self.init(resources: resources, options: options, progressBlock: progressBlock, completionHandler: completionHandler) 102 | } 103 | 104 | /** 105 | Init an image prefetcher with an array of resources. 106 | 107 | The prefetcher should be initiated with a list of prefetching targets. The resources list is immutable. 108 | After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process. 109 | The images already cached will be skipped without downloading again. 110 | 111 | - parameter resources: The resources which should be prefetched. See `Resource` type for more. 112 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. 113 | - parameter progressBlock: Called every time an resource is downloaded, skipped or cancelled. 114 | - parameter completionHandler: Called when the whole prefetching process finished. 115 | 116 | - returns: An `ImagePrefetcher` object. 117 | 118 | - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as 119 | the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`. 120 | Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method. 121 | */ 122 | public init(resources: [Resource], 123 | options: KingfisherOptionsInfo? = nil, 124 | progressBlock: PrefetcherProgressBlock? = nil, 125 | completionHandler: PrefetcherCompletionHandler? = nil) 126 | { 127 | prefetchResources = resources 128 | pendingResources = ArraySlice(resources) 129 | 130 | // We want all callbacks from main queue, so we ignore the call back queue in options 131 | let optionsInfoWithoutQueue = options?.removeAllMatchesIgnoringAssociatedValue(.callbackDispatchQueue(nil)) 132 | self.optionsInfo = optionsInfoWithoutQueue ?? KingfisherEmptyOptionsInfo 133 | 134 | let cache = self.optionsInfo.targetCache 135 | let downloader = self.optionsInfo.downloader 136 | manager = KingfisherManager(downloader: downloader, cache: cache) 137 | 138 | self.progressBlock = progressBlock 139 | self.completionHandler = completionHandler 140 | } 141 | 142 | /** 143 | Start to download the resources and cache them. This can be useful for background downloading 144 | of assets that are required for later use in an app. This code will not try and update any UI 145 | with the results of the process. 146 | */ 147 | public func start() 148 | { 149 | // Since we want to handle the resources cancellation in main thread only. 150 | DispatchQueue.main.safeAsync { 151 | 152 | guard !self.stopped else { 153 | assertionFailure("You can not restart the same prefetcher. Try to create a new prefetcher.") 154 | self.handleComplete() 155 | return 156 | } 157 | 158 | guard self.maxConcurrentDownloads > 0 else { 159 | assertionFailure("There should be concurrent downloads value should be at least 1.") 160 | self.handleComplete() 161 | return 162 | } 163 | 164 | guard self.prefetchResources.count > 0 else { 165 | self.handleComplete() 166 | return 167 | } 168 | 169 | let initialConcurentDownloads = min(self.prefetchResources.count, self.maxConcurrentDownloads) 170 | for _ in 0 ..< initialConcurentDownloads { 171 | if let resource = self.pendingResources.popFirst() { 172 | self.startPrefetching(resource) 173 | } 174 | } 175 | } 176 | } 177 | 178 | 179 | /** 180 | Stop current downloading progress, and cancel any future prefetching activity that might be occuring. 181 | */ 182 | public func stop() { 183 | DispatchQueue.main.safeAsync { 184 | if self.finished { return } 185 | self.stopped = true 186 | self.tasks.values.forEach { $0.cancel() } 187 | } 188 | } 189 | 190 | func downloadAndCache(_ resource: Resource) { 191 | 192 | let downloadTaskCompletionHandler: CompletionHandler = { (image, error, _, _) -> Void in 193 | self.tasks.removeValue(forKey: resource.downloadURL) 194 | if let _ = error { 195 | self.failedResources.append(resource) 196 | } else { 197 | self.completedResources.append(resource) 198 | } 199 | 200 | self.reportProgress() 201 | if self.stopped { 202 | if self.tasks.isEmpty { 203 | self.failedResources.append(contentsOf: self.pendingResources) 204 | self.handleComplete() 205 | } 206 | } else { 207 | self.reportCompletionOrStartNext() 208 | } 209 | } 210 | 211 | let downloadTask = manager.downloadAndCacheImage( 212 | with: resource.downloadURL, 213 | forKey: resource.cacheKey, 214 | retrieveImageTask: RetrieveImageTask(), 215 | progressBlock: nil, 216 | completionHandler: downloadTaskCompletionHandler, 217 | options: optionsInfo) 218 | 219 | if let downloadTask = downloadTask { 220 | tasks[resource.downloadURL] = downloadTask 221 | } 222 | } 223 | 224 | func append(cached resource: Resource) { 225 | skippedResources.append(resource) 226 | 227 | reportProgress() 228 | reportCompletionOrStartNext() 229 | } 230 | 231 | func startPrefetching(_ resource: Resource) 232 | { 233 | if optionsInfo.forceRefresh { 234 | downloadAndCache(resource) 235 | } else { 236 | let alreadyInCache = manager.cache.imageCachedType(forKey: resource.cacheKey, 237 | processorIdentifier: optionsInfo.processor.identifier).cached 238 | if alreadyInCache { 239 | append(cached: resource) 240 | } else { 241 | downloadAndCache(resource) 242 | } 243 | } 244 | } 245 | 246 | func reportProgress() { 247 | progressBlock?(skippedResources, failedResources, completedResources) 248 | } 249 | 250 | func reportCompletionOrStartNext() { 251 | DispatchQueue.main.async { 252 | if let resource = self.pendingResources.popFirst() { 253 | self.startPrefetching(resource) 254 | } else { 255 | guard self.tasks.isEmpty else { return } 256 | self.handleComplete() 257 | } 258 | } 259 | } 260 | 261 | func handleComplete() { 262 | completionHandler?(skippedResources, failedResources, completedResources) 263 | completionHandler = nil 264 | progressBlock = nil 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/ImageTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageTransition.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/9/18. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(macOS) 28 | // Not implemented for macOS and watchOS yet. 29 | 30 | import AppKit 31 | 32 | /// Image transition is not supported on macOS. 33 | public enum ImageTransition { 34 | case none 35 | var duration: TimeInterval { 36 | return 0 37 | } 38 | } 39 | 40 | #elseif os(watchOS) 41 | import UIKit 42 | /// Image transition is not supported on watchOS. 43 | public enum ImageTransition { 44 | case none 45 | var duration: TimeInterval { 46 | return 0 47 | } 48 | } 49 | #else 50 | import UIKit 51 | 52 | /** 53 | Transition effect which will be used when an image downloaded and set by `UIImageView` extension API in Kingfisher. 54 | You can assign an enum value with transition duration as an item in `KingfisherOptionsInfo` 55 | to enable the animation transition. 56 | 57 | Apple's UIViewAnimationOptions is used under the hood. 58 | For custom transition, you should specified your own transition options, animations and 59 | comletion handler as well. 60 | */ 61 | public enum ImageTransition { 62 | /// No animation transistion. 63 | case none 64 | 65 | /// Fade in the loaded image. 66 | case fade(TimeInterval) 67 | 68 | /// Flip from left transition. 69 | case flipFromLeft(TimeInterval) 70 | 71 | /// Flip from right transition. 72 | case flipFromRight(TimeInterval) 73 | 74 | /// Flip from top transition. 75 | case flipFromTop(TimeInterval) 76 | 77 | /// Flip from bottom transition. 78 | case flipFromBottom(TimeInterval) 79 | 80 | /// Custom transition. 81 | case custom(duration: TimeInterval, 82 | options: UIViewAnimationOptions, 83 | animations: ((UIImageView, UIImage) -> Void)?, 84 | completion: ((Bool) -> Void)?) 85 | 86 | var duration: TimeInterval { 87 | switch self { 88 | case .none: return 0 89 | case .fade(let duration): return duration 90 | 91 | case .flipFromLeft(let duration): return duration 92 | case .flipFromRight(let duration): return duration 93 | case .flipFromTop(let duration): return duration 94 | case .flipFromBottom(let duration): return duration 95 | 96 | case .custom(let duration, _, _, _): return duration 97 | } 98 | } 99 | 100 | var animationOptions: UIViewAnimationOptions { 101 | switch self { 102 | case .none: return [] 103 | case .fade(_): return .transitionCrossDissolve 104 | 105 | case .flipFromLeft(_): return .transitionFlipFromLeft 106 | case .flipFromRight(_): return .transitionFlipFromRight 107 | case .flipFromTop(_): return .transitionFlipFromTop 108 | case .flipFromBottom(_): return .transitionFlipFromBottom 109 | 110 | case .custom(_, let options, _, _): return options 111 | } 112 | } 113 | 114 | var animations: ((UIImageView, UIImage) -> Void)? { 115 | switch self { 116 | case .custom(_, _, let animations, _): return animations 117 | default: return { $0.image = $1 } 118 | } 119 | } 120 | 121 | var completion: ((Bool) -> Void)? { 122 | switch self { 123 | case .custom(_, _, _, let completion): return completion 124 | default: return nil 125 | } 126 | } 127 | } 128 | #endif 129 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageView+Kingfisher.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/6. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | 28 | #if os(macOS) 29 | import AppKit 30 | #else 31 | import UIKit 32 | #endif 33 | 34 | // MARK: - Extension methods. 35 | /** 36 | * Set image to use from web. 37 | */ 38 | extension Kingfisher where Base: ImageView { 39 | /** 40 | Set an image with a resource, a placeholder image, options, progress handler and completion handler. 41 | 42 | - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. 43 | - parameter placeholder: A placeholder image when retrieving the image at URL. 44 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. 45 | - parameter progressBlock: Called when the image downloading progress gets updated. 46 | - parameter completionHandler: Called when the image retrieved and set. 47 | 48 | - returns: A task represents the retrieving process. 49 | 50 | - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. 51 | The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. 52 | 53 | If `resource` is `nil`, the `placeholder` image will be set and 54 | `completionHandler` will be called with both `error` and `image` being `nil`. 55 | */ 56 | @discardableResult 57 | public func setImage(with resource: Resource?, 58 | placeholder: Placeholder? = nil, 59 | options: KingfisherOptionsInfo? = nil, 60 | progressBlock: DownloadProgressBlock? = nil, 61 | completionHandler: CompletionHandler? = nil) -> RetrieveImageTask 62 | { 63 | guard let resource = resource else { 64 | self.placeholder = placeholder 65 | setWebURL(nil) 66 | completionHandler?(nil, nil, .none, nil) 67 | return .empty 68 | } 69 | 70 | var options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo) 71 | let noImageOrPlaceholderSet = base.image == nil && self.placeholder == nil 72 | 73 | if !options.keepCurrentImageWhileLoading || noImageOrPlaceholderSet { // Always set placeholder while there is no image/placehoer yet. 74 | self.placeholder = placeholder 75 | } 76 | 77 | let maybeIndicator = indicator 78 | maybeIndicator?.startAnimatingView() 79 | 80 | setWebURL(resource.downloadURL) 81 | 82 | if base.shouldPreloadAllAnimation() { 83 | options.append(.preloadAllAnimationData) 84 | } 85 | 86 | let task = KingfisherManager.shared.retrieveImage( 87 | with: resource, 88 | options: options, 89 | progressBlock: { receivedSize, totalSize in 90 | guard resource.downloadURL == self.webURL else { 91 | return 92 | } 93 | if let progressBlock = progressBlock { 94 | progressBlock(receivedSize, totalSize) 95 | } 96 | }, 97 | completionHandler: {[weak base] image, error, cacheType, imageURL in 98 | DispatchQueue.main.safeAsync { 99 | maybeIndicator?.stopAnimatingView() 100 | guard let strongBase = base, imageURL == self.webURL else { 101 | completionHandler?(image, error, cacheType, imageURL) 102 | return 103 | } 104 | 105 | self.setImageTask(nil) 106 | guard let image = image else { 107 | completionHandler?(nil, error, cacheType, imageURL) 108 | return 109 | } 110 | 111 | guard let transitionItem = options.lastMatchIgnoringAssociatedValue(.transition(.none)), 112 | case .transition(let transition) = transitionItem, ( options.forceTransition || cacheType == .none) else 113 | { 114 | self.placeholder = nil 115 | strongBase.image = image 116 | completionHandler?(image, error, cacheType, imageURL) 117 | return 118 | } 119 | 120 | #if !os(macOS) 121 | UIView.transition(with: strongBase, duration: 0.0, options: [], 122 | animations: { maybeIndicator?.stopAnimatingView() }, 123 | completion: { _ in 124 | 125 | self.placeholder = nil 126 | UIView.transition(with: strongBase, duration: transition.duration, 127 | options: [transition.animationOptions, .allowUserInteraction], 128 | animations: { 129 | // Set image property in the animation. 130 | transition.animations?(strongBase, image) 131 | }, 132 | completion: { finished in 133 | transition.completion?(finished) 134 | completionHandler?(image, error, cacheType, imageURL) 135 | }) 136 | }) 137 | #endif 138 | } 139 | }) 140 | 141 | setImageTask(task) 142 | 143 | return task 144 | } 145 | 146 | /** 147 | Cancel the image download task bounded to the image view if it is running. 148 | Nothing will happen if the downloading has already finished. 149 | */ 150 | public func cancelDownloadTask() { 151 | imageTask?.cancel() 152 | } 153 | } 154 | 155 | // MARK: - Associated Object 156 | private var lastURLKey: Void? 157 | private var indicatorKey: Void? 158 | private var indicatorTypeKey: Void? 159 | private var placeholderKey: Void? 160 | private var imageTaskKey: Void? 161 | 162 | extension Kingfisher where Base: ImageView { 163 | /// Get the image URL binded to this image view. 164 | public var webURL: URL? { 165 | return objc_getAssociatedObject(base, &lastURLKey) as? URL 166 | } 167 | 168 | fileprivate func setWebURL(_ url: URL?) { 169 | objc_setAssociatedObject(base, &lastURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 170 | } 171 | 172 | /// Holds which indicator type is going to be used. 173 | /// Default is .none, means no indicator will be shown. 174 | public var indicatorType: IndicatorType { 175 | get { 176 | let indicator = objc_getAssociatedObject(base, &indicatorTypeKey) as? IndicatorType 177 | return indicator ?? .none 178 | } 179 | 180 | set { 181 | switch newValue { 182 | case .none: 183 | indicator = nil 184 | case .activity: 185 | indicator = ActivityIndicator() 186 | case .image(let data): 187 | indicator = ImageIndicator(imageData: data) 188 | case .custom(let anIndicator): 189 | indicator = anIndicator 190 | } 191 | 192 | objc_setAssociatedObject(base, &indicatorTypeKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 193 | } 194 | } 195 | 196 | /// Holds any type that conforms to the protocol `Indicator`. 197 | /// The protocol `Indicator` has a `view` property that will be shown when loading an image. 198 | /// It will be `nil` if `indicatorType` is `.none`. 199 | public fileprivate(set) var indicator: Indicator? { 200 | get { 201 | guard let box = objc_getAssociatedObject(base, &indicatorKey) as? Box else { 202 | return nil 203 | } 204 | return box.value 205 | } 206 | 207 | set { 208 | // Remove previous 209 | if let previousIndicator = indicator { 210 | previousIndicator.view.removeFromSuperview() 211 | } 212 | 213 | // Add new 214 | if var newIndicator = newValue { 215 | // Set default indicator frame if the view's frame not set. 216 | if newIndicator.view.frame == .zero { 217 | newIndicator.view.frame = base.frame 218 | } 219 | newIndicator.viewCenter = CGPoint(x: base.bounds.midX, y: base.bounds.midY) 220 | newIndicator.view.isHidden = true 221 | base.addSubview(newIndicator.view) 222 | } 223 | 224 | // Save in associated object 225 | // Wrap newValue with Box to workaround an issue that Swift does not recognize 226 | // and casting protocol for associate object correctly. https://github.com/onevcat/Kingfisher/issues/872 227 | objc_setAssociatedObject(base, &indicatorKey, newValue.map(Box.init), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 228 | } 229 | } 230 | 231 | fileprivate var imageTask: RetrieveImageTask? { 232 | return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask 233 | } 234 | 235 | fileprivate func setImageTask(_ task: RetrieveImageTask?) { 236 | objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 237 | } 238 | 239 | public fileprivate(set) var placeholder: Placeholder? { 240 | get { 241 | return objc_getAssociatedObject(base, &placeholderKey) as? Placeholder 242 | } 243 | 244 | set { 245 | if let previousPlaceholder = placeholder { 246 | previousPlaceholder.remove(from: base) 247 | } 248 | 249 | if let newPlaceholder = newValue { 250 | newPlaceholder.add(to: base) 251 | } else { 252 | base.image = nil 253 | } 254 | 255 | objc_setAssociatedObject(base, &placeholderKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 256 | } 257 | } 258 | } 259 | 260 | 261 | @objc extension ImageView { 262 | func shouldPreloadAllAnimation() -> Bool { return true } 263 | } 264 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Indicator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Indicator.swift 3 | // Kingfisher 4 | // 5 | // Created by João D. Moreira on 30/08/16. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(macOS) 28 | import AppKit 29 | #else 30 | import UIKit 31 | #endif 32 | 33 | #if os(macOS) 34 | public typealias IndicatorView = NSView 35 | #else 36 | public typealias IndicatorView = UIView 37 | #endif 38 | 39 | public enum IndicatorType { 40 | /// No indicator. 41 | case none 42 | /// Use system activity indicator. 43 | case activity 44 | /// Use an image as indicator. GIF is supported. 45 | case image(imageData: Data) 46 | /// Use a custom indicator, which conforms to the `Indicator` protocol. 47 | case custom(indicator: Indicator) 48 | } 49 | 50 | // MARK: - Indicator Protocol 51 | public protocol Indicator { 52 | func startAnimatingView() 53 | func stopAnimatingView() 54 | 55 | var viewCenter: CGPoint { get set } 56 | var view: IndicatorView { get } 57 | } 58 | 59 | extension Indicator { 60 | #if os(macOS) 61 | public var viewCenter: CGPoint { 62 | get { 63 | let frame = view.frame 64 | return CGPoint(x: frame.origin.x + frame.size.width / 2.0, y: frame.origin.y + frame.size.height / 2.0 ) 65 | } 66 | set { 67 | let frame = view.frame 68 | let newFrame = CGRect(x: newValue.x - frame.size.width / 2.0, 69 | y: newValue.y - frame.size.height / 2.0, 70 | width: frame.size.width, 71 | height: frame.size.height) 72 | view.frame = newFrame 73 | } 74 | } 75 | #else 76 | public var viewCenter: CGPoint { 77 | get { 78 | return view.center 79 | } 80 | set { 81 | view.center = newValue 82 | } 83 | } 84 | #endif 85 | } 86 | 87 | // MARK: - ActivityIndicator 88 | // Displays a NSProgressIndicator / UIActivityIndicatorView 89 | final class ActivityIndicator: Indicator { 90 | 91 | #if os(macOS) 92 | private let activityIndicatorView: NSProgressIndicator 93 | #else 94 | private let activityIndicatorView: UIActivityIndicatorView 95 | #endif 96 | private var animatingCount = 0 97 | 98 | var view: IndicatorView { 99 | return activityIndicatorView 100 | } 101 | 102 | func startAnimatingView() { 103 | animatingCount += 1 104 | // Alrady animating 105 | if animatingCount == 1 { 106 | #if os(macOS) 107 | activityIndicatorView.startAnimation(nil) 108 | #else 109 | activityIndicatorView.startAnimating() 110 | #endif 111 | activityIndicatorView.isHidden = false 112 | } 113 | } 114 | 115 | func stopAnimatingView() { 116 | animatingCount = max(animatingCount - 1, 0) 117 | if animatingCount == 0 { 118 | #if os(macOS) 119 | activityIndicatorView.stopAnimation(nil) 120 | #else 121 | activityIndicatorView.stopAnimating() 122 | #endif 123 | activityIndicatorView.isHidden = true 124 | } 125 | } 126 | 127 | init() { 128 | #if os(macOS) 129 | activityIndicatorView = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16)) 130 | activityIndicatorView.controlSize = .small 131 | activityIndicatorView.style = .spinning 132 | #else 133 | #if os(tvOS) 134 | let indicatorStyle = UIActivityIndicatorViewStyle.white 135 | #else 136 | let indicatorStyle = UIActivityIndicatorViewStyle.gray 137 | #endif 138 | activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle:indicatorStyle) 139 | activityIndicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin, .flexibleTopMargin] 140 | #endif 141 | } 142 | } 143 | 144 | // MARK: - ImageIndicator 145 | // Displays an ImageView. Supports gif 146 | final class ImageIndicator: Indicator { 147 | private let animatedImageIndicatorView: ImageView 148 | 149 | var view: IndicatorView { 150 | return animatedImageIndicatorView 151 | } 152 | 153 | init?(imageData data: Data, processor: ImageProcessor = DefaultImageProcessor.default, options: KingfisherOptionsInfo = KingfisherEmptyOptionsInfo) { 154 | 155 | var options = options 156 | // Use normal image view to show animations, so we need to preload all animation data. 157 | if !options.preloadAllAnimationData { 158 | options.append(.preloadAllAnimationData) 159 | } 160 | 161 | guard let image = processor.process(item: .data(data), options: options) else { 162 | return nil 163 | } 164 | 165 | animatedImageIndicatorView = ImageView() 166 | animatedImageIndicatorView.image = image 167 | animatedImageIndicatorView.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height) 168 | 169 | #if os(macOS) 170 | // Need for gif to animate on macOS 171 | self.animatedImageIndicatorView.imageScaling = .scaleNone 172 | self.animatedImageIndicatorView.canDrawSubviewsIntoLayer = true 173 | #else 174 | animatedImageIndicatorView.contentMode = .center 175 | animatedImageIndicatorView.autoresizingMask = [.flexibleLeftMargin, 176 | .flexibleRightMargin, 177 | .flexibleBottomMargin, 178 | .flexibleTopMargin] 179 | #endif 180 | } 181 | 182 | func startAnimatingView() { 183 | #if os(macOS) 184 | animatedImageIndicatorView.animates = true 185 | #else 186 | animatedImageIndicatorView.startAnimating() 187 | #endif 188 | animatedImageIndicatorView.isHidden = false 189 | } 190 | 191 | func stopAnimatingView() { 192 | #if os(macOS) 193 | animatedImageIndicatorView.animates = false 194 | #else 195 | animatedImageIndicatorView.stopAnimating() 196 | #endif 197 | animatedImageIndicatorView.isHidden = true 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Kingfisher.h: -------------------------------------------------------------------------------- 1 | // 2 | // Kingfisher.h 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/6. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | //! Project version number for Kingfisher. 30 | FOUNDATION_EXPORT double KingfisherVersionNumber; 31 | 32 | //! Project version string for Kingfisher. 33 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; 34 | 35 | // In this header, you should import all the public headers of your framework using statements like #import 36 | 37 | 38 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Kingfisher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Kingfisher.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 16/9/14. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | import ImageIO 29 | 30 | #if os(macOS) 31 | import AppKit 32 | public typealias Image = NSImage 33 | public typealias View = NSView 34 | public typealias Color = NSColor 35 | public typealias ImageView = NSImageView 36 | public typealias Button = NSButton 37 | #else 38 | import UIKit 39 | public typealias Image = UIImage 40 | public typealias Color = UIColor 41 | #if !os(watchOS) 42 | public typealias ImageView = UIImageView 43 | public typealias View = UIView 44 | public typealias Button = UIButton 45 | #endif 46 | #endif 47 | 48 | public final class Kingfisher { 49 | public let base: Base 50 | public init(_ base: Base) { 51 | self.base = base 52 | } 53 | } 54 | 55 | /** 56 | A type that has Kingfisher extensions. 57 | */ 58 | public protocol KingfisherCompatible { 59 | associatedtype CompatibleType 60 | var kf: CompatibleType { get } 61 | } 62 | 63 | public extension KingfisherCompatible { 64 | public var kf: Kingfisher { 65 | get { return Kingfisher(self) } 66 | } 67 | } 68 | 69 | extension Image: KingfisherCompatible { } 70 | #if !os(watchOS) 71 | extension ImageView: KingfisherCompatible { } 72 | extension Button: KingfisherCompatible { } 73 | #endif 74 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/KingfisherManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KingfisherManager.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/6. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(macOS) 28 | import AppKit 29 | #else 30 | import UIKit 31 | #endif 32 | 33 | public typealias DownloadProgressBlock = ((_ receivedSize: Int64, _ totalSize: Int64) -> Void) 34 | public typealias CompletionHandler = ((_ image: Image?, _ error: NSError?, _ cacheType: CacheType, _ imageURL: URL?) -> Void) 35 | 36 | /// RetrieveImageTask represents a task of image retrieving process. 37 | /// It contains an async task of getting image from disk and from network. 38 | public final class RetrieveImageTask { 39 | 40 | public static let empty = RetrieveImageTask() 41 | 42 | // If task is canceled before the download task started (which means the `downloadTask` is nil), 43 | // the download task should not begin. 44 | var cancelledBeforeDownloadStarting: Bool = false 45 | 46 | /// The network retrieve task in this image task. 47 | public var downloadTask: RetrieveImageDownloadTask? 48 | 49 | /** 50 | Cancel current task. If this task is already done, do nothing. 51 | */ 52 | public func cancel() { 53 | if let downloadTask = downloadTask { 54 | downloadTask.cancel() 55 | } else { 56 | cancelledBeforeDownloadStarting = true 57 | } 58 | } 59 | } 60 | 61 | /// Error domain of Kingfisher 62 | public let KingfisherErrorDomain = "com.onevcat.Kingfisher.Error" 63 | 64 | /// Main manager class of Kingfisher. It connects Kingfisher downloader and cache. 65 | /// You can use this class to retrieve an image via a specified URL from web or cache. 66 | public class KingfisherManager { 67 | 68 | /// Shared manager used by the extensions across Kingfisher. 69 | public static let shared = KingfisherManager() 70 | 71 | /// Cache used by this manager 72 | public var cache: ImageCache 73 | 74 | /// Downloader used by this manager 75 | public var downloader: ImageDownloader 76 | 77 | /// Default options used by the manager. This option will be used in 78 | /// Kingfisher manager related methods, including all image view and 79 | /// button extension methods. You can also passing the options per image by 80 | /// sending an `options` parameter to Kingfisher's APIs, the per image option 81 | /// will overwrite the default ones if exist. 82 | /// 83 | /// - Note: This option will not be applied to independent using of `ImageDownloader` or `ImageCache`. 84 | public var defaultOptions = KingfisherEmptyOptionsInfo 85 | 86 | var currentDefaultOptions: KingfisherOptionsInfo { 87 | return [.downloader(downloader), .targetCache(cache)] + defaultOptions 88 | } 89 | 90 | convenience init() { 91 | self.init(downloader: .default, cache: .default) 92 | } 93 | 94 | init(downloader: ImageDownloader, cache: ImageCache) { 95 | self.downloader = downloader 96 | self.cache = cache 97 | } 98 | 99 | /** 100 | Get an image with resource. 101 | If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first. 102 | If not found, it will download the image at `resource.downloadURL` and cache it with `resource.cacheKey`. 103 | These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more. 104 | 105 | - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. 106 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. 107 | - parameter progressBlock: Called every time downloaded data changed. This could be used as a progress UI. 108 | - parameter completionHandler: Called when the whole retrieving process finished. 109 | 110 | - returns: A `RetrieveImageTask` task object. You can use this object to cancel the task. 111 | */ 112 | @discardableResult 113 | public func retrieveImage(with resource: Resource, 114 | options: KingfisherOptionsInfo?, 115 | progressBlock: DownloadProgressBlock?, 116 | completionHandler: CompletionHandler?) -> RetrieveImageTask 117 | { 118 | let task = RetrieveImageTask() 119 | let options = currentDefaultOptions + (options ?? KingfisherEmptyOptionsInfo) 120 | if options.forceRefresh { 121 | _ = downloadAndCacheImage( 122 | with: resource.downloadURL, 123 | forKey: resource.cacheKey, 124 | retrieveImageTask: task, 125 | progressBlock: progressBlock, 126 | completionHandler: completionHandler, 127 | options: options) 128 | } else { 129 | tryToRetrieveImageFromCache( 130 | forKey: resource.cacheKey, 131 | with: resource.downloadURL, 132 | retrieveImageTask: task, 133 | progressBlock: progressBlock, 134 | completionHandler: completionHandler, 135 | options: options) 136 | } 137 | 138 | return task 139 | } 140 | 141 | @discardableResult 142 | func downloadAndCacheImage(with url: URL, 143 | forKey key: String, 144 | retrieveImageTask: RetrieveImageTask, 145 | progressBlock: DownloadProgressBlock?, 146 | completionHandler: CompletionHandler?, 147 | options: KingfisherOptionsInfo) -> RetrieveImageDownloadTask? 148 | { 149 | let downloader = options.downloader 150 | return downloader.downloadImage(with: url, retrieveImageTask: retrieveImageTask, options: options, 151 | progressBlock: { receivedSize, totalSize in 152 | progressBlock?(receivedSize, totalSize) 153 | }, 154 | completionHandler: { image, error, imageURL, originalData in 155 | 156 | let targetCache = options.targetCache 157 | if let error = error, error.code == KingfisherError.notModified.rawValue { 158 | // Not modified. Try to find the image from cache. 159 | // (The image should be in cache. It should be guaranteed by the framework users.) 160 | targetCache.retrieveImage(forKey: key, options: options, completionHandler: { (cacheImage, cacheType) -> Void in 161 | completionHandler?(cacheImage, nil, cacheType, url) 162 | }) 163 | return 164 | } 165 | 166 | if let image = image, let originalData = originalData { 167 | targetCache.store(image, 168 | original: originalData, 169 | forKey: key, 170 | processorIdentifier:options.processor.identifier, 171 | cacheSerializer: options.cacheSerializer, 172 | toDisk: !options.cacheMemoryOnly, 173 | completionHandler: nil) 174 | if options.cacheOriginalImage && options.processor != DefaultImageProcessor.default { 175 | let originalCache = options.originalCache 176 | let defaultProcessor = DefaultImageProcessor.default 177 | if let originalImage = defaultProcessor.process(item: .data(originalData), options: options) { 178 | originalCache.store(originalImage, 179 | original: originalData, 180 | forKey: key, 181 | processorIdentifier: defaultProcessor.identifier, 182 | cacheSerializer: options.cacheSerializer, 183 | toDisk: !options.cacheMemoryOnly, 184 | completionHandler: nil) 185 | } 186 | } 187 | } 188 | 189 | completionHandler?(image, error, .none, url) 190 | 191 | }) 192 | } 193 | 194 | func tryToRetrieveImageFromCache(forKey key: String, 195 | with url: URL, 196 | retrieveImageTask: RetrieveImageTask, 197 | progressBlock: DownloadProgressBlock?, 198 | completionHandler: CompletionHandler?, 199 | options: KingfisherOptionsInfo) 200 | { 201 | 202 | let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> Void in 203 | completionHandler?(image, error, cacheType, imageURL) 204 | } 205 | 206 | func handleNoCache() { 207 | if options.onlyFromCache { 208 | let error = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notCached.rawValue, userInfo: nil) 209 | diskTaskCompletionHandler(nil, error, .none, url) 210 | return 211 | } 212 | self.downloadAndCacheImage( 213 | with: url, 214 | forKey: key, 215 | retrieveImageTask: retrieveImageTask, 216 | progressBlock: progressBlock, 217 | completionHandler: diskTaskCompletionHandler, 218 | options: options) 219 | 220 | } 221 | 222 | let targetCache = options.targetCache 223 | // First, try to get the exactly image from cache 224 | targetCache.retrieveImage(forKey: key, options: options) { image, cacheType in 225 | // If found, we could finish now. 226 | if image != nil { 227 | diskTaskCompletionHandler(image, nil, cacheType, url) 228 | return 229 | } 230 | 231 | // If not found, and we are using a default processor, download it! 232 | let processor = options.processor 233 | guard processor != DefaultImageProcessor.default else { 234 | handleNoCache() 235 | return 236 | } 237 | 238 | // If processor is not the default one, we have a chance to check whether 239 | // the original image is already in cache. 240 | let originalCache = options.originalCache 241 | let optionsWithoutProcessor = options.removeAllMatchesIgnoringAssociatedValue(.processor(processor)) 242 | originalCache.retrieveImage(forKey: key, options: optionsWithoutProcessor) { image, cacheType in 243 | // If we found the original image, there is no need to download it again. 244 | // We could just apply processor to it now. 245 | guard let image = image else { 246 | handleNoCache() 247 | return 248 | } 249 | 250 | guard let processedImage = processor.process(item: .image(image), options: options) else { 251 | diskTaskCompletionHandler(nil, nil, .none, url) 252 | return 253 | } 254 | targetCache.store(processedImage, 255 | original: nil, 256 | forKey: key, 257 | processorIdentifier:options.processor.identifier, 258 | cacheSerializer: options.cacheSerializer, 259 | toDisk: !options.cacheMemoryOnly, 260 | completionHandler: nil) 261 | diskTaskCompletionHandler(processedImage, nil, .none, url) 262 | } 263 | } 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KingfisherOptionsInfo.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/23. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(macOS) 28 | import AppKit 29 | #else 30 | import UIKit 31 | #endif 32 | 33 | 34 | /** 35 | * KingfisherOptionsInfo is a typealias for [KingfisherOptionsInfoItem]. You can use the enum of option item with value to control some behaviors of Kingfisher. 36 | */ 37 | public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem] 38 | let KingfisherEmptyOptionsInfo = [KingfisherOptionsInfoItem]() 39 | 40 | /** 41 | Items could be added into KingfisherOptionsInfo. 42 | */ 43 | public enum KingfisherOptionsInfoItem { 44 | /// The associated value of this member should be an ImageCache object. Kingfisher will use the specified 45 | /// cache object when handling related operations, including trying to retrieve the cached images and store 46 | /// the downloaded image to it. 47 | case targetCache(ImageCache) 48 | 49 | /// Cache for storing and retrieving original image. 50 | /// Preferred prior to targetCache for storing and retrieving original images if specified. 51 | /// Only used if a non-default image processor is involved. 52 | case originalCache(ImageCache) 53 | 54 | /// The associated value of this member should be an ImageDownloader object. Kingfisher will use this 55 | /// downloader to download the images. 56 | case downloader(ImageDownloader) 57 | 58 | /// Member for animation transition when using UIImageView. Kingfisher will use the `ImageTransition` of 59 | /// this enum to animate the image in if it is downloaded from web. The transition will not happen when the 60 | /// image is retrieved from either memory or disk cache by default. If you need to do the transition even when 61 | /// the image being retrieved from cache, set `ForceTransition` as well. 62 | case transition(ImageTransition) 63 | 64 | /// Associated `Float` value will be set as the priority of image download task. The value for it should be 65 | /// between 0.0~1.0. If this option not set, the default value (`NSURLSessionTaskPriorityDefault`) will be used. 66 | case downloadPriority(Float) 67 | 68 | /// If set, `Kingfisher` will ignore the cache and try to fire a download task for the resource. 69 | case forceRefresh 70 | 71 | /// If set, `Kingfisher` will try to retrieve the image from memory cache first. If the image is not in memory 72 | /// cache, then it will ignore the disk cache but download the image again from network. This is useful when 73 | /// you want to display a changeable image behind the same url, while avoiding download it again and again. 74 | case fromMemoryCacheOrRefresh 75 | 76 | /// If set, setting the image to an image view will happen with transition even when retrieved from cache. 77 | /// See `Transition` option for more. 78 | case forceTransition 79 | 80 | /// If set, `Kingfisher` will only cache the value in memory but not in disk. 81 | case cacheMemoryOnly 82 | 83 | /// If set, `Kingfisher` will only try to retrieve the image from cache not from network. 84 | case onlyFromCache 85 | 86 | /// Decode the image in background thread before using. 87 | case backgroundDecode 88 | 89 | /// The associated value of this member will be used as the target queue of dispatch callbacks when 90 | /// retrieving images from cache. If not set, `Kingfisher` will use main quese for callbacks. 91 | case callbackDispatchQueue(DispatchQueue?) 92 | 93 | /// The associated value of this member will be used as the scale factor when converting retrieved data to an image. 94 | /// It is the image scale, instead of your screen scale. You may need to specify the correct scale when you dealing 95 | /// with 2x or 3x retina images. 96 | case scaleFactor(CGFloat) 97 | 98 | /// Whether all the animated image data should be preloaded. Default it false, which means following frames will be 99 | /// loaded on need. If true, all the animated image data will be loaded and decoded into memory. This option is mainly 100 | /// used for back compatibility internally. You should not set it directly. `AnimatedImageView` will not preload 101 | /// all data, while a normal image view (`UIImageView` or `NSImageView`) will load all data. Choose to use 102 | /// corresponding image view type instead of setting this option. 103 | case preloadAllAnimationData 104 | 105 | /// The `ImageDownloadRequestModifier` contained will be used to change the request before it being sent. 106 | /// This is the last chance you can modify the request. You can modify the request for some customizing purpose, 107 | /// such as adding auth token to the header, do basic HTTP auth or something like url mapping. The original request 108 | /// will be sent without any modification by default. 109 | case requestModifier(ImageDownloadRequestModifier) 110 | 111 | /// Processor for processing when the downloading finishes, a processor will convert the downloaded data to an image 112 | /// and/or apply some filter on it. If a cache is connected to the downloader (it happens when you are using 113 | /// KingfisherManager or the image extension methods), the converted image will also be sent to cache as well as the 114 | /// image view. `DefaultImageProcessor.default` will be used by default. 115 | case processor(ImageProcessor) 116 | 117 | /// Supply an `CacheSerializer` to convert some data to an image object for 118 | /// retrieving from disk cache or vice versa for storing to disk cache. 119 | /// `DefaultCacheSerializer.default` will be used by default. 120 | case cacheSerializer(CacheSerializer) 121 | 122 | /// Modifier for modifying an image right before it is used. 123 | /// If the image was fetched directly from the downloader, the modifier will 124 | /// run directly after the processor. 125 | /// If the image is being fetched from a cache, the modifier will run after 126 | /// the cacheSerializer. 127 | /// Use `ImageModifier` when you need to set properties on a concrete type 128 | /// of `Image`, such as a `UIImage`, that do not persist when caching the image. 129 | case imageModifier(ImageModifier) 130 | 131 | /// Keep the existing image while setting another image to an image view. 132 | /// By setting this option, the placeholder image parameter of imageview extension method 133 | /// will be ignored and the current image will be kept while loading or downloading the new image. 134 | case keepCurrentImageWhileLoading 135 | 136 | /// If set, Kingfisher will only load the first frame from a animated image data file as a single image. 137 | /// Loading a lot of animated images may take too much memory. It will be useful when you want to display a 138 | /// static preview of the first frame from a animated image. 139 | /// This option will be ignored if the target image is not animated image data. 140 | case onlyLoadFirstFrame 141 | 142 | /// If set and an `ImageProcessor` is used, Kingfisher will try to cache both 143 | /// the final result and original image. Kingfisher will have a chance to use 144 | /// the original image when another processor is applied to the same resouce, 145 | /// instead of downloading it again. 146 | case cacheOriginalImage 147 | } 148 | 149 | precedencegroup ItemComparisonPrecedence { 150 | associativity: none 151 | higherThan: LogicalConjunctionPrecedence 152 | } 153 | 154 | infix operator <== : ItemComparisonPrecedence 155 | 156 | // This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values. 157 | func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool { 158 | switch (lhs, rhs) { 159 | case (.targetCache(_), .targetCache(_)): return true 160 | case (.originalCache(_), .originalCache(_)): return true 161 | case (.downloader(_), .downloader(_)): return true 162 | case (.transition(_), .transition(_)): return true 163 | case (.downloadPriority(_), .downloadPriority(_)): return true 164 | case (.forceRefresh, .forceRefresh): return true 165 | case (.fromMemoryCacheOrRefresh, .fromMemoryCacheOrRefresh): return true 166 | case (.forceTransition, .forceTransition): return true 167 | case (.cacheMemoryOnly, .cacheMemoryOnly): return true 168 | case (.onlyFromCache, .onlyFromCache): return true 169 | case (.backgroundDecode, .backgroundDecode): return true 170 | case (.callbackDispatchQueue(_), .callbackDispatchQueue(_)): return true 171 | case (.scaleFactor(_), .scaleFactor(_)): return true 172 | case (.preloadAllAnimationData, .preloadAllAnimationData): return true 173 | case (.requestModifier(_), .requestModifier(_)): return true 174 | case (.processor(_), .processor(_)): return true 175 | case (.cacheSerializer(_), .cacheSerializer(_)): return true 176 | case (.imageModifier(_), .imageModifier(_)): return true 177 | case (.keepCurrentImageWhileLoading, .keepCurrentImageWhileLoading): return true 178 | case (.onlyLoadFirstFrame, .onlyLoadFirstFrame): return true 179 | case (.cacheOriginalImage, .cacheOriginalImage): return true 180 | default: return false 181 | } 182 | } 183 | 184 | 185 | extension Collection where Iterator.Element == KingfisherOptionsInfoItem { 186 | func lastMatchIgnoringAssociatedValue(_ target: Iterator.Element) -> Iterator.Element? { 187 | return reversed().first { $0 <== target } 188 | } 189 | 190 | func removeAllMatchesIgnoringAssociatedValue(_ target: Iterator.Element) -> [Iterator.Element] { 191 | return filter { !($0 <== target) } 192 | } 193 | } 194 | 195 | public extension Collection where Iterator.Element == KingfisherOptionsInfoItem { 196 | /// The target `ImageCache` which is used. 197 | public var targetCache: ImageCache { 198 | if let item = lastMatchIgnoringAssociatedValue(.targetCache(.default)), 199 | case .targetCache(let cache) = item 200 | { 201 | return cache 202 | } 203 | return ImageCache.default 204 | } 205 | 206 | /// The original `ImageCache` which is used. 207 | public var originalCache: ImageCache { 208 | if let item = lastMatchIgnoringAssociatedValue(.originalCache(.default)), 209 | case .originalCache(let cache) = item 210 | { 211 | return cache 212 | } 213 | return targetCache 214 | } 215 | 216 | /// The `ImageDownloader` which is specified. 217 | public var downloader: ImageDownloader { 218 | if let item = lastMatchIgnoringAssociatedValue(.downloader(.default)), 219 | case .downloader(let downloader) = item 220 | { 221 | return downloader 222 | } 223 | return ImageDownloader.default 224 | } 225 | 226 | /// Member for animation transition when using UIImageView. 227 | public var transition: ImageTransition { 228 | if let item = lastMatchIgnoringAssociatedValue(.transition(.none)), 229 | case .transition(let transition) = item 230 | { 231 | return transition 232 | } 233 | return ImageTransition.none 234 | } 235 | 236 | /// A `Float` value set as the priority of image download task. The value for it should be 237 | /// between 0.0~1.0. 238 | public var downloadPriority: Float { 239 | if let item = lastMatchIgnoringAssociatedValue(.downloadPriority(0)), 240 | case .downloadPriority(let priority) = item 241 | { 242 | return priority 243 | } 244 | return URLSessionTask.defaultPriority 245 | } 246 | 247 | /// Whether an image will be always downloaded again or not. 248 | public var forceRefresh: Bool { 249 | return contains{ $0 <== .forceRefresh } 250 | } 251 | 252 | /// Whether an image should be got only from memory cache or download. 253 | public var fromMemoryCacheOrRefresh: Bool { 254 | return contains{ $0 <== .fromMemoryCacheOrRefresh } 255 | } 256 | 257 | /// Whether the transition should always happen or not. 258 | public var forceTransition: Bool { 259 | return contains{ $0 <== .forceTransition } 260 | } 261 | 262 | /// Whether cache the image only in memory or not. 263 | public var cacheMemoryOnly: Bool { 264 | return contains{ $0 <== .cacheMemoryOnly } 265 | } 266 | 267 | /// Whether only load the images from cache or not. 268 | public var onlyFromCache: Bool { 269 | return contains{ $0 <== .onlyFromCache } 270 | } 271 | 272 | /// Whether the image should be decoded in background or not. 273 | public var backgroundDecode: Bool { 274 | return contains{ $0 <== .backgroundDecode } 275 | } 276 | 277 | /// Whether the image data should be all loaded at once if it is an animated image. 278 | public var preloadAllAnimationData: Bool { 279 | return contains { $0 <== .preloadAllAnimationData } 280 | } 281 | 282 | /// The queue of callbacks should happen from Kingfisher. 283 | public var callbackDispatchQueue: DispatchQueue { 284 | if let item = lastMatchIgnoringAssociatedValue(.callbackDispatchQueue(nil)), 285 | case .callbackDispatchQueue(let queue) = item 286 | { 287 | return queue ?? DispatchQueue.main 288 | } 289 | return DispatchQueue.main 290 | } 291 | 292 | /// The scale factor which should be used for the image. 293 | public var scaleFactor: CGFloat { 294 | if let item = lastMatchIgnoringAssociatedValue(.scaleFactor(0)), 295 | case .scaleFactor(let scale) = item 296 | { 297 | return scale 298 | } 299 | return 1.0 300 | } 301 | 302 | /// The `ImageDownloadRequestModifier` will be used before sending a download request. 303 | public var modifier: ImageDownloadRequestModifier { 304 | if let item = lastMatchIgnoringAssociatedValue(.requestModifier(NoModifier.default)), 305 | case .requestModifier(let modifier) = item 306 | { 307 | return modifier 308 | } 309 | return NoModifier.default 310 | } 311 | 312 | /// `ImageProcessor` for processing when the downloading finishes. 313 | public var processor: ImageProcessor { 314 | if let item = lastMatchIgnoringAssociatedValue(.processor(DefaultImageProcessor.default)), 315 | case .processor(let processor) = item 316 | { 317 | return processor 318 | } 319 | return DefaultImageProcessor.default 320 | } 321 | 322 | /// `ImageModifier` for modifying right before the image is displayed. 323 | public var imageModifier: ImageModifier { 324 | if let item = lastMatchIgnoringAssociatedValue(.imageModifier(DefaultImageModifier.default)), 325 | case .imageModifier(let imageModifier) = item 326 | { 327 | return imageModifier 328 | } 329 | return DefaultImageModifier.default 330 | } 331 | 332 | /// `CacheSerializer` to convert image to data for storing in cache. 333 | public var cacheSerializer: CacheSerializer { 334 | if let item = lastMatchIgnoringAssociatedValue(.cacheSerializer(DefaultCacheSerializer.default)), 335 | case .cacheSerializer(let cacheSerializer) = item 336 | { 337 | return cacheSerializer 338 | } 339 | return DefaultCacheSerializer.default 340 | } 341 | 342 | /// Keep the existing image while setting another image to an image view. 343 | /// Or the placeholder will be used while downloading. 344 | public var keepCurrentImageWhileLoading: Bool { 345 | return contains { $0 <== .keepCurrentImageWhileLoading } 346 | } 347 | 348 | public var onlyLoadFirstFrame: Bool { 349 | return contains { $0 <== .onlyLoadFirstFrame } 350 | } 351 | 352 | public var cacheOriginalImage: Bool { 353 | return contains { $0 <== .cacheOriginalImage } 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Placeholder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Placeholder.swift 3 | // Kingfisher 4 | // 5 | // Created by Tieme van Veen on 28/08/2017. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(macOS) 28 | import AppKit 29 | #else 30 | import UIKit 31 | #endif 32 | 33 | 34 | /// Represent a placeholder type which could be set while loading as well as 35 | /// loading finished without getting an image. 36 | public protocol Placeholder { 37 | 38 | /// How the placeholder should be added to a given image view. 39 | func add(to imageView: ImageView) 40 | 41 | /// How the placeholder should be removed from a given image view. 42 | func remove(from imageView: ImageView) 43 | } 44 | 45 | /// Default implementation of an image placeholder. The image will be set or 46 | /// reset directly for `image` property of the image view. 47 | extension Placeholder where Self: Image { 48 | 49 | /// How the placeholder should be added to a given image view. 50 | public func add(to imageView: ImageView) { imageView.image = self } 51 | 52 | /// How the placeholder should be removed from a given image view. 53 | public func remove(from imageView: ImageView) { imageView.image = nil } 54 | } 55 | 56 | extension Image: Placeholder {} 57 | 58 | /// Default implementation of an arbitrary view as placeholder. The view will be 59 | /// added as a subview when adding and be removed from its super view when removing. 60 | /// 61 | /// To use your customize View type as placeholder, simply let it conforming to 62 | /// `Placeholder` by `extension MyView: Placeholder {}`. 63 | extension Placeholder where Self: View { 64 | 65 | /// How the placeholder should be added to a given image view. 66 | public func add(to imageView: ImageView) { 67 | imageView.addSubview(self) 68 | 69 | self.translatesAutoresizingMaskIntoConstraints = false 70 | NSLayoutConstraint.activate([ 71 | NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: imageView, attribute: .centerX, multiplier: 1, constant: 0), 72 | NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: imageView, attribute: .centerY, multiplier: 1, constant: 0), 73 | NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: imageView, attribute: .height, multiplier: 1, constant: 0), 74 | NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: imageView, attribute: .width, multiplier: 1, constant: 0) 75 | ]) 76 | } 77 | 78 | /// How the placeholder should be removed from a given image view. 79 | public func remove(from imageView: ImageView) { 80 | self.removeFromSuperview() 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/RequestModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestModifier.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2016/09/05. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// Request modifier of image downloader. 30 | public protocol ImageDownloadRequestModifier { 31 | func modified(for request: URLRequest) -> URLRequest? 32 | } 33 | 34 | struct NoModifier: ImageDownloadRequestModifier { 35 | static let `default` = NoModifier() 36 | private init() {} 37 | func modified(for request: URLRequest) -> URLRequest? { 38 | return request 39 | } 40 | } 41 | 42 | public struct AnyModifier: ImageDownloadRequestModifier { 43 | 44 | let block: (URLRequest) -> URLRequest? 45 | 46 | public func modified(for request: URLRequest) -> URLRequest? { 47 | return block(request) 48 | } 49 | 50 | public init(modify: @escaping (URLRequest) -> URLRequest? ) { 51 | block = modify 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Resource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Resource.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/6. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | 30 | /// `Resource` protocol defines how to download and cache a resource from network. 31 | public protocol Resource { 32 | /// The key used in cache. 33 | var cacheKey: String { get } 34 | 35 | /// The target image URL. 36 | var downloadURL: URL { get } 37 | } 38 | 39 | /** 40 | ImageResource is a simple combination of `downloadURL` and `cacheKey`. 41 | 42 | When passed to image view set methods, Kingfisher will try to download the target 43 | image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache. 44 | */ 45 | public struct ImageResource: Resource { 46 | /// The key used in cache. 47 | public let cacheKey: String 48 | 49 | /// The target image URL. 50 | public let downloadURL: URL 51 | 52 | /** 53 | Create a resource. 54 | 55 | - parameter downloadURL: The target image URL. 56 | - parameter cacheKey: The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key. 57 | 58 | - returns: A resource. 59 | */ 60 | public init(downloadURL: URL, cacheKey: String? = nil) { 61 | self.downloadURL = downloadURL 62 | self.cacheKey = cacheKey ?? downloadURL.absoluteString 63 | } 64 | } 65 | 66 | /** 67 | URL conforms to `Resource` in Kingfisher. 68 | The `absoluteString` of this URL is used as `cacheKey`. And the URL itself will be used as `downloadURL`. 69 | If you need customize the url and/or cache key, use `ImageResource` instead. 70 | */ 71 | extension URL: Resource { 72 | public var cacheKey: String { return absoluteString } 73 | public var downloadURL: URL { return self } 74 | } 75 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/String+MD5.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+MD5.swift 3 | // Kingfisher 4 | // 5 | // To date, adding CommonCrypto to a Swift framework is problematic. See: 6 | // http://stackoverflow.com/questions/25248598/importing-commoncrypto-in-a-swift-framework 7 | // We're using a subset and modified version of CryptoSwift as an alternative. 8 | // The following is an altered source version that only includes MD5. The original software can be found at: 9 | // https://github.com/krzyzanowskim/CryptoSwift 10 | // This is the original copyright notice: 11 | 12 | /* 13 | Copyright (C) 2014 Marcin Krzyżanowski 14 | This software is provided 'as-is', without any express or implied warranty. 15 | In no event will the authors be held liable for any damages arising from the use of this software. 16 | 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: 17 | - 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. 18 | - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 19 | - This notice may not be removed or altered from any source or binary distribution. 20 | */ 21 | 22 | import Foundation 23 | 24 | public struct StringProxy { 25 | fileprivate let base: String 26 | init(proxy: String) { 27 | base = proxy 28 | } 29 | } 30 | 31 | extension String: KingfisherCompatible { 32 | public typealias CompatibleType = StringProxy 33 | public var kf: CompatibleType { 34 | return StringProxy(proxy: self) 35 | } 36 | } 37 | 38 | extension StringProxy { 39 | var md5: String { 40 | if let data = base.data(using: .utf8, allowLossyConversion: true) { 41 | 42 | let message = data.withUnsafeBytes { bytes -> [UInt8] in 43 | return Array(UnsafeBufferPointer(start: bytes, count: data.count)) 44 | } 45 | 46 | let MD5Calculator = MD5(message) 47 | let MD5Data = MD5Calculator.calculate() 48 | 49 | var MD5String = String() 50 | for c in MD5Data { 51 | MD5String += String(format: "%02x", c) 52 | } 53 | return MD5String 54 | 55 | } else { 56 | return base 57 | } 58 | } 59 | } 60 | 61 | 62 | /** array of bytes, little-endian representation */ 63 | func arrayOfBytes(_ value: T, length: Int? = nil) -> [UInt8] { 64 | let totalBytes = length ?? (MemoryLayout.size * 8) 65 | 66 | let valuePointer = UnsafeMutablePointer.allocate(capacity: 1) 67 | valuePointer.pointee = value 68 | 69 | let bytes = valuePointer.withMemoryRebound(to: UInt8.self, capacity: totalBytes) { (bytesPointer) -> [UInt8] in 70 | var bytes = [UInt8](repeating: 0, count: totalBytes) 71 | for j in 0...size, totalBytes) { 72 | bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee 73 | } 74 | return bytes 75 | } 76 | 77 | #if swift(>=4.1) 78 | valuePointer.deinitialize(count: 1) 79 | valuePointer.deallocate() 80 | #else 81 | valuePointer.deinitialize() 82 | valuePointer.deallocate(capacity: 1) 83 | #endif 84 | 85 | return bytes 86 | } 87 | 88 | extension Int { 89 | /** Array of bytes with optional padding (little-endian) */ 90 | func bytes(_ totalBytes: Int = MemoryLayout.size) -> [UInt8] { 91 | return arrayOfBytes(self, length: totalBytes) 92 | } 93 | 94 | } 95 | 96 | extension NSMutableData { 97 | 98 | /** Convenient way to append bytes */ 99 | func appendBytes(_ arrayOfBytes: [UInt8]) { 100 | append(arrayOfBytes, length: arrayOfBytes.count) 101 | } 102 | 103 | } 104 | 105 | protocol HashProtocol { 106 | var message: Array { get } 107 | 108 | /** Common part for hash calculation. Prepare header data. */ 109 | func prepare(_ len: Int) -> Array 110 | } 111 | 112 | extension HashProtocol { 113 | 114 | func prepare(_ len: Int) -> Array { 115 | var tmpMessage = message 116 | 117 | // Step 1. Append Padding Bits 118 | tmpMessage.append(0x80) // append one bit (UInt8 with one bit) to message 119 | 120 | // append "0" bit until message length in bits ≡ 448 (mod 512) 121 | var msgLength = tmpMessage.count 122 | var counter = 0 123 | 124 | while msgLength % len != (len - 8) { 125 | counter += 1 126 | msgLength += 1 127 | } 128 | 129 | tmpMessage += Array(repeating: 0, count: counter) 130 | return tmpMessage 131 | } 132 | } 133 | 134 | func toUInt32Array(_ slice: ArraySlice) -> Array { 135 | var result = Array() 136 | result.reserveCapacity(16) 137 | 138 | for idx in stride(from: slice.startIndex, to: slice.endIndex, by: MemoryLayout.size) { 139 | let d0 = UInt32(slice[idx.advanced(by: 3)]) << 24 140 | let d1 = UInt32(slice[idx.advanced(by: 2)]) << 16 141 | let d2 = UInt32(slice[idx.advanced(by: 1)]) << 8 142 | let d3 = UInt32(slice[idx]) 143 | let val: UInt32 = d0 | d1 | d2 | d3 144 | 145 | result.append(val) 146 | } 147 | return result 148 | } 149 | 150 | struct BytesIterator: IteratorProtocol { 151 | 152 | let chunkSize: Int 153 | let data: [UInt8] 154 | 155 | init(chunkSize: Int, data: [UInt8]) { 156 | self.chunkSize = chunkSize 157 | self.data = data 158 | } 159 | 160 | var offset = 0 161 | 162 | mutating func next() -> ArraySlice? { 163 | let end = min(chunkSize, data.count - offset) 164 | let result = data[offset.. 0 ? result : nil 167 | } 168 | } 169 | 170 | struct BytesSequence: Sequence { 171 | let chunkSize: Int 172 | let data: [UInt8] 173 | 174 | func makeIterator() -> BytesIterator { 175 | return BytesIterator(chunkSize: chunkSize, data: data) 176 | } 177 | } 178 | 179 | func rotateLeft(_ value: UInt32, bits: UInt32) -> UInt32 { 180 | return ((value << bits) & 0xFFFFFFFF) | (value >> (32 - bits)) 181 | } 182 | 183 | class MD5: HashProtocol { 184 | 185 | static let size = 16 // 128 / 8 186 | let message: [UInt8] 187 | 188 | init (_ message: [UInt8]) { 189 | self.message = message 190 | } 191 | 192 | /** specifies the per-round shift amounts */ 193 | private let shifts: [UInt32] = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 194 | 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 195 | 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 196 | 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21] 197 | 198 | /** binary integer part of the sines of integers (Radians) */ 199 | private let sines: [UInt32] = [0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 200 | 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 201 | 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 202 | 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 203 | 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 204 | 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 205 | 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 206 | 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 207 | 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 208 | 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 209 | 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, 210 | 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 211 | 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 212 | 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 213 | 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 214 | 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391] 215 | 216 | private let hashes: [UInt32] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476] 217 | 218 | func calculate() -> [UInt8] { 219 | var tmpMessage = prepare(64) 220 | tmpMessage.reserveCapacity(tmpMessage.count + 4) 221 | 222 | // hash values 223 | var hh = hashes 224 | 225 | // Step 2. Append Length a 64-bit representation of lengthInBits 226 | let lengthInBits = (message.count * 8) 227 | let lengthBytes = lengthInBits.bytes(64 / 8) 228 | tmpMessage += lengthBytes.reversed() 229 | 230 | // Process the message in successive 512-bit chunks: 231 | let chunkSizeBytes = 512 / 8 // 64 232 | 233 | for chunk in BytesSequence(chunkSize: chunkSizeBytes, data: tmpMessage) { 234 | // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15 235 | var M = toUInt32Array(chunk) 236 | assert(M.count == 16, "Invalid array") 237 | 238 | // Initialize hash value for this chunk: 239 | var A: UInt32 = hh[0] 240 | var B: UInt32 = hh[1] 241 | var C: UInt32 = hh[2] 242 | var D: UInt32 = hh[3] 243 | 244 | var dTemp: UInt32 = 0 245 | 246 | // Main loop 247 | for j in 0 ..< sines.count { 248 | var g = 0 249 | var F: UInt32 = 0 250 | 251 | switch j { 252 | case 0...15: 253 | F = (B & C) | ((~B) & D) 254 | g = j 255 | break 256 | case 16...31: 257 | F = (D & B) | (~D & C) 258 | g = (5 * j + 1) % 16 259 | break 260 | case 32...47: 261 | F = B ^ C ^ D 262 | g = (3 * j + 5) % 16 263 | break 264 | case 48...63: 265 | F = C ^ (B | (~D)) 266 | g = (7 * j) % 16 267 | break 268 | default: 269 | break 270 | } 271 | dTemp = D 272 | D = C 273 | C = B 274 | B = B &+ rotateLeft((A &+ F &+ sines[j] &+ M[g]), bits: shifts[j]) 275 | A = dTemp 276 | } 277 | 278 | hh[0] = hh[0] &+ A 279 | hh[1] = hh[1] &+ B 280 | hh[2] = hh[2] &+ C 281 | hh[3] = hh[3] &+ D 282 | } 283 | 284 | var result = [UInt8]() 285 | result.reserveCapacity(hh.count / 4) 286 | 287 | hh.forEach { 288 | let itemLE = $0.littleEndian 289 | let r1 = UInt8(itemLE & 0xff) 290 | let r2 = UInt8((itemLE >> 8) & 0xff) 291 | let r3 = UInt8((itemLE >> 16) & 0xff) 292 | let r4 = UInt8((itemLE >> 24) & 0xff) 293 | result += [r1, r2, r3, r4] 294 | } 295 | return result 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/ThreadHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadHelper.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/10/9. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | extension DispatchQueue { 30 | // This method will dispatch the `block` to self. 31 | // If `self` is the main queue, and current thread is main thread, the block 32 | // will be invoked immediately instead of being dispatched. 33 | func safeAsync(_ block: @escaping ()->()) { 34 | if self === DispatchQueue.main && Thread.isMainThread { 35 | block() 36 | } else { 37 | async { block() } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/UIButton+Kingfisher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+Kingfisher.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/13. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | // MARK: - Set Images 30 | /** 31 | * Set image to use in button from web for a specified state. 32 | */ 33 | extension Kingfisher where Base: UIButton { 34 | /** 35 | Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and 36 | completion handler. 37 | 38 | - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. 39 | - parameter state: The state that uses the specified image. 40 | - parameter placeholder: A placeholder image when retrieving the image at URL. 41 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. 42 | - parameter progressBlock: Called when the image downloading progress gets updated. 43 | - parameter completionHandler: Called when the image retrieved and set. 44 | 45 | - returns: A task represents the retrieving process. 46 | 47 | - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. 48 | The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. 49 | 50 | If `resource` is `nil`, the `placeholder` image will be set and 51 | `completionHandler` will be called with both `error` and `image` being `nil`. 52 | */ 53 | @discardableResult 54 | public func setImage(with resource: Resource?, 55 | for state: UIControlState, 56 | placeholder: UIImage? = nil, 57 | options: KingfisherOptionsInfo? = nil, 58 | progressBlock: DownloadProgressBlock? = nil, 59 | completionHandler: CompletionHandler? = nil) -> RetrieveImageTask 60 | { 61 | guard let resource = resource else { 62 | base.setImage(placeholder, for: state) 63 | setWebURL(nil, for: state) 64 | completionHandler?(nil, nil, .none, nil) 65 | return .empty 66 | } 67 | 68 | let options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo) 69 | if !options.keepCurrentImageWhileLoading { 70 | base.setImage(placeholder, for: state) 71 | } 72 | 73 | setWebURL(resource.downloadURL, for: state) 74 | let task = KingfisherManager.shared.retrieveImage( 75 | with: resource, 76 | options: options, 77 | progressBlock: { receivedSize, totalSize in 78 | guard resource.downloadURL == self.webURL(for: state) else { 79 | return 80 | } 81 | if let progressBlock = progressBlock { 82 | progressBlock(receivedSize, totalSize) 83 | } 84 | }, 85 | completionHandler: {[weak base] image, error, cacheType, imageURL in 86 | DispatchQueue.main.safeAsync { 87 | guard let strongBase = base, imageURL == self.webURL(for: state) else { 88 | completionHandler?(image, error, cacheType, imageURL) 89 | return 90 | } 91 | self.setImageTask(nil) 92 | if image != nil { 93 | strongBase.setImage(image, for: state) 94 | } 95 | 96 | completionHandler?(image, error, cacheType, imageURL) 97 | } 98 | }) 99 | 100 | setImageTask(task) 101 | return task 102 | } 103 | 104 | /** 105 | Cancel the image download task bounded to the image view if it is running. 106 | Nothing will happen if the downloading has already finished. 107 | */ 108 | public func cancelImageDownloadTask() { 109 | imageTask?.cancel() 110 | } 111 | 112 | /** 113 | Set the background image to use for a specified state with a resource, 114 | a placeholder image, options progress handler and completion handler. 115 | 116 | - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. 117 | - parameter state: The state that uses the specified image. 118 | - parameter placeholder: A placeholder image when retrieving the image at URL. 119 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. 120 | - parameter progressBlock: Called when the image downloading progress gets updated. 121 | - parameter completionHandler: Called when the image retrieved and set. 122 | 123 | - returns: A task represents the retrieving process. 124 | 125 | - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. 126 | The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. 127 | 128 | If `resource` is `nil`, the `placeholder` image will be set and 129 | `completionHandler` will be called with both `error` and `image` being `nil`. 130 | */ 131 | @discardableResult 132 | public func setBackgroundImage(with resource: Resource?, 133 | for state: UIControlState, 134 | placeholder: UIImage? = nil, 135 | options: KingfisherOptionsInfo? = nil, 136 | progressBlock: DownloadProgressBlock? = nil, 137 | completionHandler: CompletionHandler? = nil) -> RetrieveImageTask 138 | { 139 | guard let resource = resource else { 140 | base.setBackgroundImage(placeholder, for: state) 141 | setBackgroundWebURL(nil, for: state) 142 | completionHandler?(nil, nil, .none, nil) 143 | return .empty 144 | } 145 | 146 | let options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo) 147 | if !options.keepCurrentImageWhileLoading { 148 | base.setBackgroundImage(placeholder, for: state) 149 | } 150 | 151 | setBackgroundWebURL(resource.downloadURL, for: state) 152 | let task = KingfisherManager.shared.retrieveImage( 153 | with: resource, 154 | options: options, 155 | progressBlock: { receivedSize, totalSize in 156 | guard resource.downloadURL == self.backgroundWebURL(for: state) else { 157 | return 158 | } 159 | if let progressBlock = progressBlock { 160 | progressBlock(receivedSize, totalSize) 161 | } 162 | }, 163 | completionHandler: { [weak base] image, error, cacheType, imageURL in 164 | DispatchQueue.main.safeAsync { 165 | guard let strongBase = base, imageURL == self.backgroundWebURL(for: state) else { 166 | completionHandler?(image, error, cacheType, imageURL) 167 | return 168 | } 169 | self.setBackgroundImageTask(nil) 170 | if image != nil { 171 | strongBase.setBackgroundImage(image, for: state) 172 | } 173 | completionHandler?(image, error, cacheType, imageURL) 174 | } 175 | }) 176 | 177 | setBackgroundImageTask(task) 178 | return task 179 | } 180 | 181 | /** 182 | Cancel the background image download task bounded to the image view if it is running. 183 | Nothing will happen if the downloading has already finished. 184 | */ 185 | public func cancelBackgroundImageDownloadTask() { 186 | backgroundImageTask?.cancel() 187 | } 188 | 189 | } 190 | 191 | // MARK: - Associated Object 192 | private var lastURLKey: Void? 193 | private var imageTaskKey: Void? 194 | 195 | extension Kingfisher where Base: UIButton { 196 | /** 197 | Get the image URL binded to this button for a specified state. 198 | 199 | - parameter state: The state that uses the specified image. 200 | 201 | - returns: Current URL for image. 202 | */ 203 | public func webURL(for state: UIControlState) -> URL? { 204 | return webURLs[NSNumber(value:state.rawValue)] as? URL 205 | } 206 | 207 | fileprivate func setWebURL(_ url: URL?, for state: UIControlState) { 208 | webURLs[NSNumber(value:state.rawValue)] = url 209 | } 210 | 211 | fileprivate var webURLs: NSMutableDictionary { 212 | var dictionary = objc_getAssociatedObject(base, &lastURLKey) as? NSMutableDictionary 213 | if dictionary == nil { 214 | dictionary = NSMutableDictionary() 215 | setWebURLs(dictionary!) 216 | } 217 | return dictionary! 218 | } 219 | 220 | fileprivate func setWebURLs(_ URLs: NSMutableDictionary) { 221 | objc_setAssociatedObject(base, &lastURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 222 | } 223 | 224 | fileprivate var imageTask: RetrieveImageTask? { 225 | return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask 226 | } 227 | 228 | fileprivate func setImageTask(_ task: RetrieveImageTask?) { 229 | objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 230 | } 231 | } 232 | 233 | 234 | private var lastBackgroundURLKey: Void? 235 | private var backgroundImageTaskKey: Void? 236 | 237 | 238 | extension Kingfisher where Base: UIButton { 239 | /** 240 | Get the background image URL binded to this button for a specified state. 241 | 242 | - parameter state: The state that uses the specified background image. 243 | 244 | - returns: Current URL for background image. 245 | */ 246 | public func backgroundWebURL(for state: UIControlState) -> URL? { 247 | return backgroundWebURLs[NSNumber(value:state.rawValue)] as? URL 248 | } 249 | 250 | fileprivate func setBackgroundWebURL(_ url: URL?, for state: UIControlState) { 251 | backgroundWebURLs[NSNumber(value:state.rawValue)] = url 252 | } 253 | 254 | fileprivate var backgroundWebURLs: NSMutableDictionary { 255 | var dictionary = objc_getAssociatedObject(base, &lastBackgroundURLKey) as? NSMutableDictionary 256 | if dictionary == nil { 257 | dictionary = NSMutableDictionary() 258 | setBackgroundWebURLs(dictionary!) 259 | } 260 | return dictionary! 261 | } 262 | 263 | fileprivate func setBackgroundWebURLs(_ URLs: NSMutableDictionary) { 264 | objc_setAssociatedObject(base, &lastBackgroundURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 265 | } 266 | 267 | fileprivate var backgroundImageTask: RetrieveImageTask? { 268 | return objc_getAssociatedObject(base, &backgroundImageTaskKey) as? RetrieveImageTask 269 | } 270 | 271 | fileprivate func setBackgroundImageTask(_ task: RetrieveImageTask?) { 272 | objc_setAssociatedObject(base, &backgroundImageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/LTAutoScrollView.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LTAutoScrollView", 3 | "version": "0.1.0", 4 | "summary": "LTAutoScrollView", 5 | "description": "自定义轮播图,不依赖任何图片加载框架,可实现自定义pageControl,自定义轮播View,自定义滚动样式等等", 6 | "homepage": "https://github.com/gltwy/LTAutoScrollView", 7 | "license": { 8 | "type": "MIT", 9 | "file": "LICENSE" 10 | }, 11 | "authors": { 12 | "1282990794@qq.com": "1282990794@qq.com" 13 | }, 14 | "source": { 15 | "git": "https://github.com/gltwy/LTAutoScrollView.git", 16 | "tag": "0.1.0" 17 | }, 18 | "platforms": { 19 | "ios": "8.0" 20 | }, 21 | "source_files": "LTAutoScrollView/Classes/**/*" 22 | } 23 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Kingfisher (4.7.0) 3 | - LTAutoScrollView (0.1.0) 4 | 5 | DEPENDENCIES: 6 | - Kingfisher (~> 4.7.0) 7 | - LTAutoScrollView (from `../`) 8 | 9 | EXTERNAL SOURCES: 10 | LTAutoScrollView: 11 | :path: "../" 12 | 13 | SPEC CHECKSUMS: 14 | Kingfisher: da6b005aa96d37698e3e4f1ccfe96a5b9bbf27d6 15 | LTAutoScrollView: 457e1fb733fd4ba33d36d4dd6e21a07891022c1d 16 | 17 | PODFILE CHECKSUM: 084a963cfe4e84dec4a03d4df38492d4e14e1424 18 | 19 | COCOAPODS: 1.2.0 20 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 4.7.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Kingfisher : NSObject 3 | @end 4 | @implementation PodsDummy_Kingfisher 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "Kingfisher.h" 14 | 15 | FOUNDATION_EXPORT double KingfisherVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; 17 | 18 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap: -------------------------------------------------------------------------------- 1 | framework module Kingfisher { 2 | umbrella header "Kingfisher-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Kingfisher 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = -framework "CFNetwork" 5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Kingfisher 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LTAutoScrollView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LTAutoScrollView/LTAutoScrollView-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_LTAutoScrollView : NSObject 3 | @end 4 | @implementation PodsDummy_LTAutoScrollView 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LTAutoScrollView/LTAutoScrollView-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LTAutoScrollView/LTAutoScrollView-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double LTAutoScrollViewVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char LTAutoScrollViewVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LTAutoScrollView/LTAutoScrollView.modulemap: -------------------------------------------------------------------------------- 1 | framework module LTAutoScrollView { 2 | umbrella header "LTAutoScrollView-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LTAutoScrollView/LTAutoScrollView.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/LTAutoScrollView 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Kingfisher 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2018 Wei Wang 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | 30 | ## LTAutoScrollView 31 | 32 | Copyright (c) 2018 1282990794@qq.com <1282990794@qq.com> 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy 35 | of this software and associated documentation files (the "Software"), to deal 36 | in the Software without restriction, including without limitation the rights 37 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 38 | copies of the Software, and to permit persons to whom the Software is 39 | furnished to do so, subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in 42 | all copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 49 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 50 | THE SOFTWARE. 51 | 52 | Generated by CocoaPods - https://cocoapods.org 53 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2018 Wei Wang 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | 40 | License 41 | MIT 42 | Title 43 | Kingfisher 44 | Type 45 | PSGroupSpecifier 46 | 47 | 48 | FooterText 49 | Copyright (c) 2018 1282990794@qq.com <1282990794@qq.com> 50 | 51 | Permission is hereby granted, free of charge, to any person obtaining a copy 52 | of this software and associated documentation files (the "Software"), to deal 53 | in the Software without restriction, including without limitation the rights 54 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 55 | copies of the Software, and to permit persons to whom the Software is 56 | furnished to do so, subject to the following conditions: 57 | 58 | The above copyright notice and this permission notice shall be included in 59 | all copies or substantial portions of the Software. 60 | 61 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 62 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 63 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 64 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 65 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 66 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 67 | THE SOFTWARE. 68 | 69 | License 70 | MIT 71 | Title 72 | LTAutoScrollView 73 | Type 74 | PSGroupSpecifier 75 | 76 | 77 | FooterText 78 | Generated by CocoaPods - https://cocoapods.org 79 | Title 80 | 81 | Type 82 | PSGroupSpecifier 83 | 84 | 85 | StringsTable 86 | Acknowledgements 87 | Title 88 | Acknowledgements 89 | 90 | 91 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_LTAutoScrollView_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_LTAutoScrollView_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | 91 | if [[ "$CONFIGURATION" == "Debug" ]]; then 92 | install_framework "$BUILT_PRODUCTS_DIR/Kingfisher/Kingfisher.framework" 93 | install_framework "$BUILT_PRODUCTS_DIR/LTAutoScrollView/LTAutoScrollView.framework" 94 | fi 95 | if [[ "$CONFIGURATION" == "Release" ]]; then 96 | install_framework "$BUILT_PRODUCTS_DIR/Kingfisher/Kingfisher.framework" 97 | install_framework "$BUILT_PRODUCTS_DIR/LTAutoScrollView/LTAutoScrollView.framework" 98 | fi 99 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 100 | wait 101 | fi 102 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | 3) 22 | TARGET_DEVICE_ARGS="--target-device tv" 23 | ;; 24 | *) 25 | TARGET_DEVICE_ARGS="--target-device mac" 26 | ;; 27 | esac 28 | 29 | install_resource() 30 | { 31 | if [[ "$1" = /* ]] ; then 32 | RESOURCE_PATH="$1" 33 | else 34 | RESOURCE_PATH="${PODS_ROOT}/$1" 35 | fi 36 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 37 | cat << EOM 38 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 39 | EOM 40 | exit 1 41 | fi 42 | case $RESOURCE_PATH in 43 | *.storyboard) 44 | 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}" 45 | 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} 46 | ;; 47 | *.xib) 48 | 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}" 49 | 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} 50 | ;; 51 | *.framework) 52 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 53 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 54 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 55 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | ;; 57 | *.xcdatamodel) 58 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 59 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 60 | ;; 61 | *.xcdatamodeld) 62 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 63 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 64 | ;; 65 | *.xcmappingmodel) 66 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 67 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 68 | ;; 69 | *.xcassets) 70 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 71 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 72 | ;; 73 | *) 74 | echo "$RESOURCE_PATH" 75 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 76 | ;; 77 | esac 78 | } 79 | 80 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 81 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 82 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 83 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | fi 86 | rm -f "$RESOURCES_TO_COPY" 87 | 88 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 89 | then 90 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 91 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 92 | while read line; do 93 | if [[ $line != "${PODS_ROOT}*" ]]; then 94 | XCASSET_FILES+=("$line") 95 | fi 96 | done <<<"$OTHER_XCASSETS" 97 | 98 | 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}" 99 | fi 100 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_LTAutoScrollView_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_LTAutoScrollView_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" "$PODS_CONFIGURATION_BUILD_DIR/LTAutoScrollView" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher/Kingfisher.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/LTAutoScrollView/LTAutoScrollView.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Kingfisher" -framework "LTAutoScrollView" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_LTAutoScrollView_Example { 2 | umbrella header "Pods-LTAutoScrollView_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" "$PODS_CONFIGURATION_BUILD_DIR/LTAutoScrollView" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher/Kingfisher.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/LTAutoScrollView/LTAutoScrollView.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Kingfisher" -framework "LTAutoScrollView" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class Tests: XCTestCase { 4 | 5 | override func setUp() { 6 | super.setUp() 7 | // Put setup code here. This method is called before the invocation of each test method in the class. 8 | } 9 | 10 | override func tearDown() { 11 | // Put teardown code here. This method is called after the invocation of each test method in the class. 12 | super.tearDown() 13 | } 14 | 15 | func testExample() { 16 | // This is an example of a functional test case. 17 | XCTAssert(true, "Pass") 18 | } 19 | 20 | func testPerformanceExample() { 21 | // This is an example of a performance test case. 22 | self.measure() { 23 | // Put the code you want to measure the time of here. 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 1282990794@qq.com <1282990794@qq.com> 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /LTAutoScrollView.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint LTAutoScrollView.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'LTAutoScrollView' 11 | s.version = '0.2.0' 12 | s.summary = 'LTAutoScrollView' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | 自定义轮播图,不依赖任何图片加载框架,可实现自定义pageControl,自定义轮播View,自定义滚动样式等等 22 | DESC 23 | 24 | s.homepage = 'https://github.com/gltwy/LTAutoScrollView' 25 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' 26 | s.license = { :type => 'MIT', :file => 'LICENSE' } 27 | s.author = { '1282990794@qq.com' => '1282990794@qq.com' } 28 | s.source = { :git => 'https://github.com/gltwy/LTAutoScrollView.git', :tag => s.version.to_s } 29 | # s.social_media_url = 'https://twitter.com/' 30 | 31 | s.ios.deployment_target = '8.0' 32 | 33 | s.source_files = 'LTAutoScrollView/Classes/**/*' 34 | 35 | # s.resource_bundles = { 36 | # 'LTAutoScrollView' => ['LTAutoScrollView/Assets/*.png'] 37 | # } 38 | 39 | # s.public_header_files = 'Pod/Classes/**/*.h' 40 | # s.frameworks = 'UIKit', 'MapKit' 41 | # s.dependency 'AFNetworking', '~> 2.3' 42 | end 43 | -------------------------------------------------------------------------------- /LTAutoScrollView/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glthello/LTAutoScrollView/5bdf6b3b3c3c99df5313326406d1a02e51cbdd4d/LTAutoScrollView/Assets/.gitkeep -------------------------------------------------------------------------------- /LTAutoScrollView/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glthello/LTAutoScrollView/5bdf6b3b3c3c99df5313326406d1a02e51cbdd4d/LTAutoScrollView/Classes/.gitkeep -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LTAutoScrollView 2 | 3 | [![CI Status](http://img.shields.io/travis/1282990794@qq.com/LTAutoScrollView.svg?style=flat)](https://travis-ci.org/1282990794@qq.com/LTAutoScrollView) 4 | [![Version](https://img.shields.io/cocoapods/v/LTAutoScrollView.svg?style=flat)](http://cocoapods.org/pods/LTAutoScrollView) 5 | [![License](https://img.shields.io/cocoapods/l/LTAutoScrollView.svg?style=flat)](http://cocoapods.org/pods/LTAutoScrollView) 6 | [![Platform](https://img.shields.io/cocoapods/p/LTAutoScrollView.svg?style=flat)](http://cocoapods.org/pods/LTAutoScrollView) 7 | 8 | ![image](https://github.com/gltwy/LTAutoScrollView/blob/master/gltauto.gif) 9 | 10 | ## Example 11 | 12 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 13 | 14 | ## Installation with CocoaPods 15 | 16 | [CocoaPods](http://cocoapods.org) is a dependency manager for Swift, which automates and simplifies the process of using 3rd-party libraries like LTAutoScrollView in your projects. You can install it with the following command: 17 | 18 | ```bash 19 | $ gem install cocoapods 20 | ``` 21 | 22 | #### Podfile 23 | 24 | To integrate LTAutoScrollView into your Xcode project using CocoaPods, specify it in your `Podfile`: 25 | 26 | ```ruby 27 | source 'https://github.com/CocoaPods/Specs.git' 28 | platform :ios, '8.0' 29 | 30 | target 'TargetName' do 31 | pod 'LTAutoScrollView' 32 | end 33 | ``` 34 | 35 | Then, run the following command: 36 | 37 | ```bash 38 | $ pod install 39 | ``` 40 | 41 | ## Usage 42 | 43 | #### 创建LTAutoScrollView 44 | 45 | ```swift 46 | let autoScrollView = LTAutoScrollView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 150)) 47 | 48 | //设置滚动时间间隔 默认2.0s 49 | autoScrollView.glt_timeInterval = 1.5 50 | 51 | //设置轮播图的方向 默认水平 52 | autoScrollView.scrollDirection = .vertical 53 | 54 | //加载网络图片传入图片url数组, 加载本地图片传入图片名称数组 55 | autoScrollView.images = images 56 | 57 | //加载图片,内部不依赖任何图片加载框架 58 | autoScrollView.imageHandle = {(imageView, imageName) in 59 | //加载本地图片(根据传入的images数组来决定加载方式) 60 | imageView.image = UIImage(named: imageName) 61 | //加载网络图片(根据传入的images数组来决定加载方式) 62 | //imageView.kf.setImage(with: URL(string: imageName)) 63 | } 64 | 65 | // 滚动手势禁用(文字轮播较实用) 默认为false 66 | autoScrollView.isDisableScrollGesture = false 67 | 68 | //设置pageControl View的高度 默认为20 69 | autoScrollView.gltPageControlHeight = 20; 70 | 71 | // 是否自动轮播 默认true 72 | autoScrollView.isAutoScroll = true 73 | 74 | //dot在轮播图的位置 中心 左侧 右侧 默认居中 75 | autoScrollView.dotDirection = .default 76 | 77 | //点击事件 78 | autoScrollView.didSelectItemHandle = { 79 | print("autoScrollView1 点击了第 \($0) 个索引") 80 | } 81 | 82 | //自动滚动到当前索引事件 83 | autoScrollView.autoDidSelectItemHandle = { index in 84 | print("autoScrollView1 自动滚动到了第 \(index) 个索引") 85 | } 86 | 87 | //PageControl点击事件 88 | autoScrollView.pageControlDidSelectIndexHandle = { index in 89 | print("autoScrollView1 pageControl点击了第 \(index) 个索引") 90 | } 91 | 92 | //设置pageControl的位置 93 | autoScrollView.dotDirection = .right 94 | //dot在轮播图的位置 左侧 或 右侧时,距离最屏幕最左边或最最右边的距离,默认0 95 | autoScrollView.adjustValue = 15.0 96 | //pageControl高度调整从而改变pageControl位置 默认20 97 | autoScrollView.gltPageControlHeight = 25 98 | 99 | //设置LTDotLayout,更多dot使用见LTDotLayout属性说明 100 | let layout = LTDotLayout(dotImage: dotImage, dotSelectImage: dotSelectImage) 101 | layout.dotMargin = 10.0 102 | autoScrollView.dotLayout = layout 103 | ``` 104 | 105 | #### LTDotLayout属性说明 106 | 107 | ```swift 108 | /* dot单独的一个的宽度 */ 109 | public var dotWidth: CGFloat = isPostDotSize 110 | /* dot单独的一个的高度 */ 111 | public var dotHeight: CGFloat = isPostDotSize 112 | /* dot之间的间距 */ 113 | public var dotMargin: CGFloat = 15.0 114 | /* dot未选中的图片 */ 115 | public var dotImage: UIImage? 116 | /* dot选中后的图片 */ 117 | public var dotSelectImage: UIImage? 118 | /* dot未选中的颜色 */ 119 | public var dotColor: UIColor = UIColor.clear 120 | /* dot选中的后颜色 */ 121 | public var dotSelectColor: UIColor = UIColor.clear 122 | /* custom为默认是自定义 , 想使用类似系统样式传入default */ 123 | public var dotType: LTAutoScrollViewType = .custom 124 | /* 滚动过程是否放大当前dot */ 125 | public var isScale: Bool = true 126 | /* 滚动过程dot放大倍率 */ 127 | public var scaleXY: CGFloat = 1.4 128 | ``` 129 | 130 | ## Author 131 | 132 | 1282990794@qq.com 133 | 134 | ## License 135 | 136 | LTAutoScrollView is available under the MIT license. See the LICENSE file for more info. 137 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj -------------------------------------------------------------------------------- /gltauto.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glthello/LTAutoScrollView/5bdf6b3b3c3c99df5313326406d1a02e51cbdd4d/gltauto.gif --------------------------------------------------------------------------------