27 |
28 | Kingfisher is a lightweight and pure Swift implemented library for downloading and caching image from the web. This project is heavily inspired by the popular [SDWebImage](https://github.com/rs/SDWebImage). And it provides you a chance to use pure Swift alternative in your next app.
29 |
30 | ## Features
31 |
32 | * Everything in Kingfisher is asynchronous, not only downloading, but also caching. That means you never need to worry about blocking your UI thread.
33 | * Multiple-layer cache. Downloaded images will be cached in both memory and disk. So there is no need to download again, this could boost your app's perceptual speed dramatically.
34 | * Cache management. You can set the max duration or size the cache takes. From this, the cache will be cleaned automatically to prevent taking too many resources.
35 | * Modern framework. Kingfisher uses `NSURLSession` and the latest technology of GCD, which makes it a strong and swift framework. It also provides you easy APIs to use.
36 | * Cancelable processing task. You can cancel the downloading process if it is not needed anymore.
37 | * Prefetching. You can prefetch and cache the images which might soon appear in the page. It will bring your users great experience.
38 | * Independent components. You can use the downloader or caching system separately. Or even create your own cache based on Kingfisher's code.
39 | * Options to decompress the image in background before rendering it, which could improve the UI performance.
40 | * Categories over `UIImageView`, `NSImage` and `UIButton` for setting image from a URL directly. Use the same code across all Apple platforms.
41 | * Support GIF seamlessly. You could just download and set your GIF images as the same as you do for PNG/JPEG format using `AnimatedImageView`.
42 | * You could set `Activity Indicator` for your UIImageView or NSImageView to enable the indicator during loading image from web.
43 |
44 | ## Requirements
45 |
46 | * iOS 8.0+, tvOS 9.0+, watchOS 2.0+ or OS X 10.10+
47 | * Xcode 7.3 or above
48 |
49 | If you are upgrading to Kingfisher 2.x from 1.x, please read the [Kingfisher 2.0 Migration Guide](https://github.com/onevcat/Kingfisher/wiki/Kingfisher-2.0-Migration-Guide) for more information.
50 |
51 | Kingfisher is now supporting Swift 2.2. If you need to use Kingfisher in Swift 2.1, you need to pin the version to 2.1.0.
52 |
53 | ### Swift 3
54 |
55 | Kingfisher is now supporting Swift 3 in the [swift3](https://github.com/onevcat/Kingfisher/tree/swift3) branch. It is now under development and not be officially released yet. You could specify to that branch if you are working in a Swift 3 project. However, please reconsider if you want to use it in a releasing orientation product, since more breaking change would be applied later.
56 |
57 | ## Installation
58 |
59 | ### CocoaPods
60 |
61 | [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command:
62 |
63 | ``` bash
64 | $ gem install cocoapods
65 | ```
66 |
67 | To integrate Kingfisher into your Xcode project using CocoaPods, specify it in your `Podfile`:
68 |
69 | ``` ruby
70 | source 'https://github.com/CocoaPods/Specs.git'
71 | platform :ios, '8.0'
72 | use_frameworks!
73 |
74 | pod 'Kingfisher', '~> 2.4'
75 | ```
76 |
77 | Then, run the following command:
78 |
79 | ``` bash
80 | $ pod install
81 | ```
82 |
83 | You should open the `{Project}.xcworkspace` instead of the `{Project}.xcodeproj` after you installed anything from CocoaPods.
84 |
85 | For more information about how to use CocoaPods, I suggest [this tutorial](http://www.raywenderlich.com/64546/introduction-to-cocoapods-2).
86 |
87 | ### Carthage
88 |
89 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager for Cocoa application. To install the carthage tool, you can use [Homebrew](http://brew.sh).
90 |
91 | ``` bash
92 | $ brew update
93 | $ brew install carthage
94 | ```
95 |
96 | To integrate Kingfisher into your Xcode project using Carthage, specify it in your `Cartfile`:
97 |
98 | ``` ogdl
99 | github "onevcat/Kingfisher" ~> 2.4
100 | ```
101 |
102 | Then, run the following command to build the Kingfisher framework:
103 |
104 | ``` bash
105 | $ carthage update
106 |
107 | ```
108 |
109 | At last, you need to set up your Xcode project manually to add the Kingfisher framework.
110 |
111 | On your application targets’ “General” settings tab, in the “Linked Frameworks and Libraries” section, drag and drop each framework you want to use from the Carthage/Build folder on disk.
112 |
113 | On your application targets’ “Build Phases” settings tab, click the “+” icon and choose “New Run Script Phase”. Create a Run Script with the following content:
114 |
115 | ```
116 | /usr/local/bin/carthage copy-frameworks
117 | ```
118 |
119 | and add the paths to the frameworks you want to use under “Input Files”:
120 |
121 | ```
122 | $(SRCROOT)/Carthage/Build/iOS/Kingfisher.framework
123 | ```
124 |
125 | For more information about how to use Carthage, please see its [project page](https://github.com/Carthage/Carthage).
126 |
127 | ### Manually
128 |
129 | It is not recommended to install the framework manually, but if you prefer not to use either of the aforementioned dependency managers, you can integrate Kingfisher into your project manually. A regular way to use Kingfisher in your project would be using Embedded Framework.
130 |
131 | - Add Kingfisher as a [submodule](http://git-scm.com/docs/git-submodule). In your favorite terminal, `cd` into your top-level project directory, and entering the following command:
132 |
133 | ``` bash
134 | $ git submodule add https://github.com/onevcat/Kingfisher.git
135 | ```
136 |
137 | - Open the `Kingfisher` folder, and drag `Kingfisher.xcodeproj` into the file navigator of your app project, under your app project.
138 | - In Xcode, navigate to the target configuration window by clicking on the blue project icon, and selecting the application target under the "Targets" heading in the sidebar.
139 | - In the tab bar at the top of that window, open the "Build Phases" panel.
140 | - Expand the "Target Dependencies" group, and add `Kingfisher.framework`.
141 | - Click on the `+` button at the top left of "Build Phases" panel and select "New Copy Files Phase". Rename this new phase to "Copy Frameworks", set the "Destination" to "Frameworks", and add `Kingfisher.framework` of the platform you need.
142 |
143 | ## Usage
144 |
145 | You can find the full API documentation at [CocoaDocs](http://cocoadocs.org/docsets/Kingfisher/).
146 |
147 | ### UIImageView and NSImageView category
148 |
149 | Use Kingfisher in your project is as easy as a pie. You can use the `UIImageView` or `NSImageView` category and trust Kingfisher to manage downloading and cache images.
150 |
151 | #### Basic
152 |
153 | In your source files, add the following code:
154 |
155 | ``` swift
156 | import Kingfisher
157 |
158 | imageView.kf_setImageWithURL(NSURL(string: "http://your_image_url.png")!)
159 | ```
160 |
161 | In most cases, Kingfisher is used in a reusable cell. Since the downloading process is asynchronous, the earlier image will be remained during the downloading of newer one. The placeholder version of this API could help:
162 |
163 | ``` swift
164 | imageView.kf_setImageWithURL(NSURL(string: "http://your_image_url.png")!, placeholderImage: nil)
165 | ```
166 |
167 | By default, `Kingfisher` will use `absoluteString` of the URL as the key for cache. If you need another key instead of URL's `absoluteString`, there is another set of APIs accepting `Resource` as parameter:
168 |
169 | ``` swift
170 | let URL = NSURL(string: "http://your_image_url.png")!
171 | let resource = Resource(downloadURL: URL, cacheKey: "your_customized_key")
172 |
173 | imageView.kf_setImageWithResource(resource)
174 | ```
175 |
176 | It will ask Kingfisher's manager to get the image for the "your_customized_key" from memory and disk first. If the manager does not find it, it will try to download the image at the URL, and store it with `cacheKey` ("your_customized_key" here) for next use.
177 |
178 | #### Options
179 |
180 | Kingfisher will search in cache (both memory and disk) first with the URL, if no image found, it will try to download and store the image in the cache. You can change this behavior by passing an option, to let it ignore the cache.
181 |
182 | ``` swift
183 | imageView.kf_setImageWithURL(NSURL(string: "your_image_url")!,
184 | placeholderImage: nil,
185 | optionsInfo: [.ForceRefresh])
186 | ```
187 |
188 | There are also other options to control the cache level, downloading priority, etc. Take some other examples:
189 |
190 | If you need to cache the downloaded image to a customized cache instead of the default one:
191 |
192 | ``` swift
193 | let myCache = ImageCache(name: "my_cache")
194 |
195 | imageView.kf_setImageWithURL(NSURL(string: "your_image_url")!,
196 | placeholderImage: nil,
197 | optionsInfo: [.TargetCache(myCache)])
198 | ```
199 |
200 | This is useful if you want to use a specified cache for some reasons.
201 |
202 | And if you need to fade in the image to image view during 1 second (image transition only works for iOS platform now):
203 |
204 | ```
205 | imageView.kf_setImageWithURL(NSURL(string: "your_image_url")!,
206 | placeholderImage: nil,
207 | optionsInfo: [.Transition(ImageTransition.Fade(1))])
208 | ```
209 |
210 | You are also free to combine these options to customize the behavior:
211 |
212 | ```swift
213 | let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
214 | let optionInfo: KingfisherOptionsInfo = [
215 | .ForceRefresh,
216 | .DownloadPriority(0.5),
217 | .CallbackDispatchQueue(queue),
218 | .Transition(ImageTransition.Fade(1))
219 | ]
220 | ```
221 |
222 | For more information about options, please see the `KingfisherOptionsInfo` in the [documentation](http://cocoadocs.org/docsets/Kingfisher/index.html).
223 |
224 | #### Callbacks
225 |
226 | You can get a chance during Kingfisher downloading images and when the process is done:
227 |
228 | ``` swift
229 | imageView.kf_setImageWithURL(NSURL(string: "your_image_url")!,
230 | placeholderImage: nil,
231 | optionsInfo: nil,
232 | progressBlock: { (receivedSize, totalSize) -> () in
233 | print("Download Progress: \(receivedSize)/\(totalSize)")
234 | },
235 | completionHandler: { (image, error, cacheType, imageURL) -> () in
236 | print("Downloaded and set!")
237 | }
238 | )
239 | ```
240 |
241 | #### Cancel Task
242 |
243 | You can `cancel` the task if the images are not needed anymore.
244 | It could be useful when you use Kingfisher to set an image in a cell of table view or collection view, but users scroll the view and the cells disappeared before downloading finishing.
245 |
246 | ``` swift
247 | imageView.kf_setImageWithURL(NSURL(string: "http://your_image_url.png")!)
248 |
249 | // The image retrieving will stop.
250 | imageView.kf_cancelDownloadTask()
251 | ```
252 |
253 | If you need more control and want to do some check before cancelling, all `kf_setImageWithURL` methods return a `RetrieveImageTask` object as well. You can also hold and manage it, then call cancel on the task:
254 |
255 | ``` swift
256 | let task = imageView.kf_setImageWithURL(NSURL(string: "http://your_image_url.png")!)
257 |
258 | let urlShouldNotBeCancelled: URL = ...
259 |
260 | if task.downloadTask?.URL != urlShouldNotBeCancelled {
261 | task.cancel()
262 | }
263 | ```
264 |
265 | ### Downloader & Cache system
266 |
267 | Kingfisher will use the default downloader and cache if you do not specify them by yourself. You can access them by using `KingfisherManager.sharedManager.downloader` and `KingfisherManager.sharedManager.cache`. You can adjust some parameters to meet your demands:
268 |
269 | ``` swift
270 | let downloader = KingfisherManager.sharedManager.downloader
271 |
272 | // Download process will timeout after 5 seconds. Default is 15.
273 | downloader.downloadTimeout = 5
274 |
275 | // requestModifier will be called before image download request made.
276 | downloader.requestModifier = {
277 | (request: NSMutableURLRequest) in
278 | // Do what you need to modify the download request. Maybe add your HTTP basic authentication for example.
279 | }
280 |
281 | // Hosts in trustedHosts will be ignore the received challenge.
282 | // You can add the host of your self-signed site to it to bypass the SSL.
283 | // (Do not do it unless you know what you are doing)
284 | downloader.trustedHosts = Set(["your_self_signed_host"])
285 | ```
286 |
287 | ``` swift
288 | let cache = KingfisherManager.sharedManager.cache
289 |
290 | // Set max disk cache to 50 mb. Default is no limit.
291 | cache.maxDiskCacheSize = 50 * 1024 * 1024
292 |
293 | // Set max disk cache to duration to 3 days, Default is 1 week.
294 | cache.maxCachePeriodInSecond = 60 * 60 * 24 * 3
295 |
296 | // Get the disk size taken by the cache.
297 | cache.calculateDiskCacheSizeWithCompletionHandler { (size) -> () in
298 | print("disk size in bytes: \(size)")
299 | }
300 | ```
301 |
302 | The memory cache will be purged whenever the app switched to background or receiving a memory warning. Disk cache will be cleaned when the conditions are met. You can also clear these caches manually:
303 |
304 | ``` swift
305 | // Clear memory cache right away.
306 | cache.clearMemoryCache()
307 |
308 | // Clear disk cache. This is an async operation.
309 | cache.clearDiskCache()
310 |
311 | // Clean expired or size exceeded disk cache. This is an async operation.
312 | cache.cleanExpiredDiskCache()
313 | ```
314 |
315 | ### Prefetching
316 |
317 | You could prefetch some images and cache them before you display them on the screen. This is useful when you know a list of image resources you know they would probably be shown later. Since the prefetched images are already in the cache system, there is no need to request them again when you really need to display them in a image view. It will boost your UI and bring your users great experience.
318 |
319 | To prefetch some images, you could use the `ImagePrefetcher`:
320 |
321 | ```swift
322 | let urls = ["http://example.com/image1.jpg", "http://example.com/image2.jpg"].map { NSURL(string: $0)! }
323 | let prefetcher = ImagePrefetcher(urls: urls, optionsInfo: nil, progressBlock: nil, completionHandler: {
324 | (skippedResources, failedResources, completedResources) -> () in
325 | print("These resources are prefetched: \(completedResources)")
326 | })
327 | prefetcher.start()
328 | ```
329 |
330 | You can also stop a prefetch whenever you need:
331 |
332 | ```swift
333 | prefetcher.stop()
334 | ```
335 |
336 | After prefetching, you could retrieve image or set the image view with other Kingfisher's methods, with the same `ImageCache` object you used for the prefetching.
337 |
338 | ### Animated GIF
339 |
340 | You can load animated GIF by replacing `UIImageView` with `AnimatedImageView`, and then using the same API for a regular image view like this:
341 |
342 | ```swift
343 | let imageView = AnimatedImageView()
344 | imageView.kf_setImageWithURL(NSURL(string: "your_animated_gif_image_url")!)
345 | ```
346 |
347 | `AnimatedImageView` will only decode some frames of your GIF image to get a smaller memory footprint. You can set the frame count you need to pre-load by setting the `framePreloadCount` property of an `AnimatedImageView` (default is 10).
348 |
349 | You can also load a GIF file by a regular `UIImageView`. However, all frames will be loaded and decoded into memory. It is probably not suitable if you are loading a large GIF image. For most cases, you may want to use `AnimatedImageView`. The GIF support in Kingfisher's `UIImageView` only fits the small files, and now is mostly serving for back compatibility.
350 |
351 | ## Future of Kingfisher
352 |
353 | I want to keep Kingfisher slim. This framework will focus on providing a simple solution for image downloading and caching. But that does not mean the framework will not be improved. Kingfisher is far away from perfect, and necessary and useful features will be added later to make it better.
354 |
355 | ## About the logo
356 |
357 | The logo of Kingfisher is inspired by [Tangram (七巧板)](http://en.wikipedia.org/wiki/Tangram), a dissection puzzle consisting of seven flat shapes from China. I believe she's a kingfisher bird instead of a swift, but someone insists that she is a pigeon. I guess I should give her a name. Hi, guys, do you have any suggestion?
358 |
359 | ## Contact
360 |
361 | Follow and contact me on [Twitter](http://twitter.com/onevcat) or [Sina Weibo](http://weibo.com/onevcat). If you find an issue, just [open a ticket](https://github.com/onevcat/Kingfisher/issues/new) on it. Pull requests are warmly welcome as well.
362 |
363 | ## License
364 |
365 | Kingfisher is released under the MIT license. See LICENSE for details.
366 |
367 |
368 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/AnimatedImageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AnimatableImageView.swift
3 | // Kingfisher
4 | //
5 | // Created by bl4ckra1sond3tre on 4/22/16.
6 | //
7 | // The AnimatableImageView, AnimatedFrame and Animator is a modified version of
8 | // some classes from kaishin's Gifu project (https://github.com/kaishin/Gifu)
9 | //
10 | // The MIT License (MIT)
11 | //
12 | // Copyright (c) 2014-2016 Reda Lemeden.
13 | //
14 | // Permission is hereby granted, free of charge, to any person obtaining a copy of
15 | // this software and associated documentation files (the "Software"), to deal in
16 | // the Software without restriction, including without limitation the rights to
17 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
18 | // the Software, and to permit persons to whom the Software is furnished to do so,
19 | // subject to the following conditions:
20 | //
21 | // The above copyright notice and this permission notice shall be included in all
22 | // copies or substantial portions of the Software.
23 | //
24 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
26 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
27 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
28 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 | //
31 | // The name and characters used in the demo of this software are property of their
32 | // respective owners.
33 |
34 | import UIKit
35 | import ImageIO
36 |
37 | /// `AnimatedImageView` is a subclass of `UIImageView` for displaying animated image.
38 | public class AnimatedImageView: UIImageView {
39 |
40 | /// Proxy object for prevending a reference cycle between the CADDisplayLink and AnimatedImageView.
41 | class TargetProxy {
42 | private weak var target: AnimatedImageView?
43 |
44 | init(target: AnimatedImageView) {
45 | self.target = target
46 | }
47 |
48 | @objc func onScreenUpdate() {
49 | target?.updateFrame()
50 | }
51 | }
52 |
53 | // MARK: - Public property
54 | /// Whether automatically play the animation when the view become visible. Default is true.
55 | public var autoPlayAnimatedImage = true
56 |
57 | /// The size of the frame cache.
58 | public var framePreloadCount = 10
59 |
60 | /// Specifies whether the GIF frames should be pre-scaled to save memory. Default is true.
61 | public var needsPrescaling = true
62 |
63 | /// The animation timer's run loop mode. Default is `NSRunLoopCommonModes`. Set this property to `NSDefaultRunLoopMode` will make the animation pause during UIScrollView scrolling.
64 | public var runLoopMode = NSRunLoopCommonModes {
65 | willSet {
66 | if runLoopMode == newValue {
67 | return
68 | } else {
69 | stopAnimating()
70 | displayLink.removeFromRunLoop(NSRunLoop.mainRunLoop(), forMode: runLoopMode)
71 | displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: newValue)
72 | startAnimating()
73 | }
74 | }
75 | }
76 |
77 | // MARK: - Private property
78 | /// `Animator` instance that holds the frames of a specific image in memory.
79 | private var animator: Animator?
80 |
81 | /// A flag to avoid invalidating the displayLink on deinit if it was never created, because displayLink is so lazy. :D
82 | private var displayLinkInitialized: Bool = false
83 |
84 | /// A display link that keeps calling the `updateFrame` method on every screen refresh.
85 | private lazy var displayLink: CADisplayLink = {
86 | self.displayLinkInitialized = true
87 | let displayLink = CADisplayLink(target: TargetProxy(target: self), selector: #selector(TargetProxy.onScreenUpdate))
88 | displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: self.runLoopMode)
89 | displayLink.paused = true
90 | return displayLink
91 | }()
92 |
93 | // MARK: - Override
94 | override public var image: Image? {
95 | didSet {
96 | if image != oldValue {
97 | reset()
98 | }
99 | setNeedsDisplay()
100 | layer.setNeedsDisplay()
101 | }
102 | }
103 |
104 | deinit {
105 | if displayLinkInitialized {
106 | displayLink.invalidate()
107 | }
108 | }
109 |
110 | override public func isAnimating() -> Bool {
111 | if displayLinkInitialized {
112 | return !displayLink.paused
113 | } else {
114 | return super.isAnimating()
115 | }
116 | }
117 |
118 | /// Starts the animation.
119 | override public func startAnimating() {
120 | if self.isAnimating() {
121 | return
122 | } else {
123 | displayLink.paused = false
124 | }
125 | }
126 |
127 | /// Stops the animation.
128 | override public func stopAnimating() {
129 | super.stopAnimating()
130 | if displayLinkInitialized {
131 | displayLink.paused = true
132 | }
133 | }
134 |
135 | override public func displayLayer(layer: CALayer) {
136 | if let currentFrame = animator?.currentFrame {
137 | layer.contents = currentFrame.CGImage
138 | } else {
139 | layer.contents = image?.CGImage
140 | }
141 | }
142 |
143 | override public func didMoveToWindow() {
144 | super.didMoveToWindow()
145 | didMove()
146 | }
147 |
148 | override public func didMoveToSuperview() {
149 | super.didMoveToSuperview()
150 | didMove()
151 | }
152 |
153 | // This is for back compatibility that using regular UIImageView to show GIF.
154 | override func shouldPreloadAllGIF() -> Bool {
155 | return false
156 | }
157 |
158 | // MARK: - Private method
159 | /// Reset the animator.
160 | private func reset() {
161 | animator = nil
162 | if let imageSource = image?.kf_imageSource?.imageRef {
163 | animator = Animator(imageSource: imageSource, contentMode: contentMode, size: bounds.size, framePreloadCount: framePreloadCount)
164 | animator?.needsPrescaling = needsPrescaling
165 | animator?.prepareFrames()
166 | }
167 | didMove()
168 | }
169 |
170 | private func didMove() {
171 | if autoPlayAnimatedImage && animator != nil {
172 | if let _ = superview, _ = window {
173 | startAnimating()
174 | } else {
175 | stopAnimating()
176 | }
177 | }
178 | }
179 |
180 | /// Update the current frame with the displayLink duration.
181 | private func updateFrame() {
182 | if animator?.updateCurrentFrame(displayLink.duration) ?? false {
183 | layer.setNeedsDisplay()
184 | }
185 | }
186 | }
187 |
188 | /// Keeps a reference to an `Image` instance and its duration as a GIF frame.
189 | struct AnimatedFrame {
190 | var image: Image?
191 | let duration: NSTimeInterval
192 |
193 | static func null() -> AnimatedFrame {
194 | return AnimatedFrame(image: .None, duration: 0.0)
195 | }
196 | }
197 |
198 | // MARK: - Animator
199 | ///
200 | class Animator {
201 | // MARK: Private property
202 | private let size: CGSize
203 | private let maxFrameCount: Int
204 | private let imageSource: CGImageSourceRef
205 |
206 | private var animatedFrames = [AnimatedFrame]()
207 | private let maxTimeStep: NSTimeInterval = 1.0
208 | private var frameCount = 0
209 | private var currentFrameIndex = 0
210 | private var currentPreloadIndex = 0
211 | private var timeSinceLastFrameChange: NSTimeInterval = 0.0
212 | private var needsPrescaling = true
213 |
214 | /// Loop count of animatd image.
215 | private var loopCount = 0
216 |
217 | var currentFrame: UIImage? {
218 | return frameAtIndex(currentFrameIndex)
219 | }
220 |
221 | var contentMode: UIViewContentMode = .ScaleToFill
222 |
223 | /**
224 | Init an animator with image source reference.
225 |
226 | - parameter imageSource: The reference of animated image.
227 |
228 | - parameter contentMode: Content mode of AnimatedImageView.
229 |
230 | - parameter size: Size of AnimatedImageView.
231 |
232 | - framePreloadCount: Frame cache size.
233 |
234 | - returns: The animator object.
235 | */
236 | init(imageSource src: CGImageSourceRef, contentMode mode: UIViewContentMode, size: CGSize, framePreloadCount: Int) {
237 | self.imageSource = src
238 | self.contentMode = mode
239 | self.size = size
240 | self.maxFrameCount = framePreloadCount
241 | }
242 |
243 | func frameAtIndex(index: Int) -> Image? {
244 | return animatedFrames[index].image
245 | }
246 |
247 | func prepareFrames() {
248 | frameCount = CGImageSourceGetCount(imageSource)
249 |
250 | if let properties = CGImageSourceCopyProperties(imageSource, nil),
251 | gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary,
252 | loopCount = gifInfo[kCGImagePropertyGIFLoopCount as String] as? Int {
253 | self.loopCount = loopCount
254 | }
255 |
256 | let frameToProcess = min(frameCount, maxFrameCount)
257 | animatedFrames.reserveCapacity(frameToProcess)
258 | animatedFrames = (0.. AnimatedFrame {
262 | guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, index, nil) else {
263 | return AnimatedFrame.null()
264 | }
265 |
266 | let frameDuration = imageSource.kf_GIFPropertiesAtIndex(index).flatMap { (gifInfo) -> Double? in
267 | let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as Double?
268 | let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as Double?
269 | let duration = unclampedDelayTime ?? delayTime
270 | /**
271 | http://opensource.apple.com/source/WebCore/WebCore-7600.1.25/platform/graphics/cg/ImageSourceCG.cpp
272 | Many annoying ads specify a 0 duration to make an image flash as quickly as
273 | possible. We follow Safari and Firefox's behavior and use a duration of 100 ms
274 | for any frames that specify a duration of <= 10 ms.
275 | See and for more information.
276 |
277 | See also: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser.
278 | */
279 | return duration > 0.011 ? duration : 0.100
280 | }
281 |
282 | let image = Image(CGImage: imageRef)
283 | let scaledImage: Image?
284 |
285 | if needsPrescaling {
286 | scaledImage = image.kf_resizeToSize(size, contentMode: contentMode)
287 | } else {
288 | scaledImage = image
289 | }
290 |
291 | return AnimatedFrame(image: scaledImage, duration: frameDuration ?? 0.0)
292 | }
293 |
294 | /**
295 | Updates the current frame if necessary using the frame timer and the duration of each frame in `animatedFrames`.
296 | */
297 | func updateCurrentFrame(duration: CFTimeInterval) -> Bool {
298 | timeSinceLastFrameChange += min(maxTimeStep, duration)
299 | guard let frameDuration = animatedFrames[safe: currentFrameIndex]?.duration where frameDuration <= timeSinceLastFrameChange else {
300 | return false
301 | }
302 |
303 | timeSinceLastFrameChange -= frameDuration
304 | let lastFrameIndex = currentFrameIndex
305 | currentFrameIndex += 1
306 | currentFrameIndex = currentFrameIndex % animatedFrames.count
307 |
308 | if animatedFrames.count < frameCount {
309 | animatedFrames[lastFrameIndex] = prepareFrame(currentPreloadIndex)
310 | currentPreloadIndex += 1
311 | currentPreloadIndex = currentPreloadIndex % frameCount
312 | }
313 | return true
314 | }
315 | }
316 |
317 | // MARK: - Resize
318 | extension Image {
319 | func kf_resizeToSize(size: CGSize, contentMode: UIViewContentMode) -> Image {
320 | switch contentMode {
321 | case .ScaleAspectFit:
322 | let newSize = self.size.kf_sizeConstrainedSize(size)
323 | return kf_resizeToSize(newSize)
324 | case .ScaleAspectFill:
325 | let newSize = self.size.kf_sizeFillingSize(size)
326 | return kf_resizeToSize(newSize)
327 | default:
328 | return kf_resizeToSize(size)
329 | }
330 | }
331 |
332 | private func kf_resizeToSize(size: CGSize) -> Image {
333 | UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
334 | drawInRect(CGRect(origin: CGPoint.zero, size: size))
335 | let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
336 | UIGraphicsEndImageContext()
337 | return resizedImage ?? self
338 | }
339 | }
340 |
341 | extension CGSize {
342 | func kf_sizeConstrainedSize(size: CGSize) -> CGSize {
343 | let aspectWidth = round(kf_aspectRatio * size.height)
344 | let aspectHeight = round(size.width / kf_aspectRatio)
345 |
346 | return aspectWidth > size.width ? CGSize(width: size.width, height: aspectHeight) : CGSize(width: aspectWidth, height: size.height)
347 | }
348 |
349 | func kf_sizeFillingSize(size: CGSize) -> CGSize {
350 | let aspectWidth = round(kf_aspectRatio * size.height)
351 | let aspectHeight = round(size.width / kf_aspectRatio)
352 |
353 | return aspectWidth < size.width ? CGSize(width: size.width, height: aspectHeight) : CGSize(width: aspectWidth, height: size.height)
354 | }
355 | private var kf_aspectRatio: CGFloat {
356 | return height == 0.0 ? 1.0 : width / height
357 | }
358 | }
359 |
360 | extension CGImageSourceRef {
361 | func kf_GIFPropertiesAtIndex(index: Int) -> [String: Double]? {
362 | let properties = CGImageSourceCopyPropertiesAtIndex(self, index, nil) as Dictionary?
363 | return properties?[kCGImagePropertyGIFDictionary as String] as? [String: Double]
364 | }
365 | }
366 |
367 | extension Array {
368 | subscript(safe index: Int) -> Element? {
369 | return indices ~= index ? self[index] : .None
370 | }
371 | }
372 |
373 | private func pure(value: T) -> [T] {
374 | return [value]
375 | }
376 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Image.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Image.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 16/1/6.
6 | //
7 | // Copyright (c) 2016 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(OSX)
29 | import AppKit.NSImage
30 | public typealias Image = NSImage
31 |
32 | private var imagesKey: Void?
33 | private var durationKey: Void?
34 | #else
35 | import UIKit.UIImage
36 | import MobileCoreServices
37 | public typealias Image = UIImage
38 |
39 | private var imageSourceKey: Void?
40 | private var animatedImageDataKey: Void?
41 | #endif
42 |
43 | import ImageIO
44 |
45 | // MARK: - Image Properties
46 | extension Image {
47 | #if os(OSX)
48 |
49 | var CGImage: CGImageRef! {
50 | return CGImageForProposedRect(nil, context: nil, hints: nil)
51 | }
52 |
53 | var kf_scale: CGFloat {
54 | return 1.0
55 | }
56 |
57 | private(set) var kf_images: [Image]? {
58 | get {
59 | return objc_getAssociatedObject(self, &imagesKey) as? [Image]
60 | }
61 | set {
62 | objc_setAssociatedObject(self, &imagesKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
63 | }
64 | }
65 |
66 | private(set) var kf_duration: NSTimeInterval {
67 | get {
68 | return objc_getAssociatedObject(self, &durationKey) as? NSTimeInterval ?? 0.0
69 | }
70 | set {
71 | objc_setAssociatedObject(self, &durationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
72 | }
73 | }
74 |
75 | #else
76 | var kf_scale: CGFloat {
77 | return scale
78 | }
79 |
80 | var kf_images: [Image]? {
81 | return images
82 | }
83 |
84 | var kf_duration: NSTimeInterval {
85 | return duration
86 | }
87 |
88 | private(set) var kf_imageSource: ImageSource? {
89 | get {
90 | return objc_getAssociatedObject(self, &imageSourceKey) as? ImageSource
91 | }
92 | set {
93 | objc_setAssociatedObject(self, &imageSourceKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
94 | }
95 | }
96 |
97 | private(set) var kf_animatedImageData: NSData? {
98 | get {
99 | return objc_getAssociatedObject(self, &animatedImageDataKey) as? NSData
100 | }
101 | set {
102 | objc_setAssociatedObject(self, &animatedImageDataKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
103 | }
104 | }
105 | #endif
106 | }
107 |
108 | // MARK: - Image Conversion
109 | extension Image {
110 | #if os(OSX)
111 | static func kf_imageWithCGImage(cgImage: CGImageRef, scale: CGFloat, refImage: Image?) -> Image {
112 | return Image(CGImage: cgImage, size: CGSize.zero)
113 | }
114 |
115 | /**
116 | Normalize the image. This method does nothing in OS X.
117 |
118 | - returns: The image itself.
119 | */
120 | public func kf_normalizedImage() -> Image {
121 | return self
122 | }
123 |
124 | static func kf_animatedImageWithImages(images: [Image], duration: NSTimeInterval) -> Image? {
125 | return nil
126 | }
127 | #else
128 | static func kf_imageWithCGImage(cgImage: CGImageRef, scale: CGFloat, refImage: Image?) -> Image {
129 | if let refImage = refImage {
130 | return Image(CGImage: cgImage, scale: scale, orientation: refImage.imageOrientation)
131 | } else {
132 | return Image(CGImage: cgImage, scale: scale, orientation: .Up)
133 | }
134 | }
135 |
136 | /**
137 | Normalize the image. This method will try to redraw an image with orientation and scale considered.
138 |
139 | - returns: The normalized image with orientation set to up and correct scale.
140 | */
141 | public func kf_normalizedImage() -> Image {
142 | // prevent animated image (GIF) lose it's images
143 | if images != nil {
144 | return self
145 | }
146 |
147 | if imageOrientation == .Up {
148 | return self
149 | }
150 |
151 | UIGraphicsBeginImageContextWithOptions(size, false, scale)
152 | drawInRect(CGRect(origin: CGPoint.zero, size: size))
153 | let normalizedImage = UIGraphicsGetImageFromCurrentImageContext()
154 | UIGraphicsEndImageContext()
155 |
156 | return normalizedImage
157 | }
158 |
159 | static func kf_animatedImageWithImages(images: [Image], duration: NSTimeInterval) -> Image? {
160 | return Image.animatedImageWithImages(images, duration: duration)
161 | }
162 | #endif
163 | }
164 |
165 |
166 | // MARK: - PNG
167 | func ImagePNGRepresentation(image: Image) -> NSData? {
168 | #if os(OSX)
169 | if let cgimage = image.CGImage {
170 | let rep = NSBitmapImageRep(CGImage: cgimage)
171 | return rep.representationUsingType(.NSPNGFileType, properties:[:])
172 | }
173 | return nil
174 | #else
175 | return UIImagePNGRepresentation(image)
176 | #endif
177 | }
178 |
179 | // MARK: - JPEG
180 | func ImageJPEGRepresentation(image: Image, _ compressionQuality: CGFloat) -> NSData? {
181 | #if os(OSX)
182 | let rep = NSBitmapImageRep(CGImage: image.CGImage)
183 | return rep.representationUsingType(.NSJPEGFileType, properties: [NSImageCompressionFactor: compressionQuality])
184 | #else
185 | return UIImageJPEGRepresentation(image, compressionQuality)
186 | #endif
187 | }
188 |
189 | // MARK: - GIF
190 | func ImageGIFRepresentation(image: Image) -> NSData? {
191 | #if os(OSX)
192 | return ImageGIFRepresentation(image, duration: 0.0, repeatCount: 0)
193 | #else
194 | return image.kf_animatedImageData
195 | #endif
196 | }
197 |
198 | func ImageGIFRepresentation(image: Image, duration: NSTimeInterval, repeatCount: Int) -> NSData? {
199 | guard let images = image.kf_images else {
200 | return nil
201 | }
202 |
203 | let frameCount = images.count
204 | let gifDuration = duration <= 0.0 ? image.kf_duration / Double(frameCount) : duration / Double(frameCount)
205 |
206 | let frameProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: gifDuration]]
207 | let imageProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: repeatCount]]
208 |
209 | let data = NSMutableData()
210 |
211 | guard let destination = CGImageDestinationCreateWithData(data, kUTTypeGIF, frameCount, nil) else {
212 | return nil
213 | }
214 | CGImageDestinationSetProperties(destination, imageProperties)
215 |
216 | for image in images {
217 | CGImageDestinationAddImage(destination, image.CGImage!, frameProperties)
218 | }
219 |
220 | return CGImageDestinationFinalize(destination) ? NSData(data: data) : nil
221 | }
222 |
223 | func ImagesCountWithImageSource(ref: CGImageSourceRef) -> Int {
224 | return CGImageSourceGetCount(ref)
225 | }
226 |
227 | extension Image {
228 | static func kf_animatedImageWithGIFData(gifData data: NSData, preloadAll: Bool) -> Image? {
229 | return kf_animatedImageWithGIFData(gifData: data, scale: 1.0, duration: 0.0, preloadAll: preloadAll)
230 | }
231 |
232 | static func kf_animatedImageWithGIFData(gifData data: NSData, scale: CGFloat, duration: NSTimeInterval, preloadAll: Bool) -> Image? {
233 |
234 | func decodeFromSource(imageSource: CGImageSource, options: NSDictionary) -> ([Image], NSTimeInterval)? {
235 |
236 | let frameCount = CGImageSourceGetCount(imageSource)
237 | var images = [Image]()
238 | var gifDuration = 0.0
239 | for i in 0 ..< frameCount {
240 |
241 | guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, options) else {
242 | return nil
243 | }
244 |
245 | if frameCount == 1 {
246 | // Single frame
247 | gifDuration = Double.infinity
248 | } else {
249 | // Animated GIF
250 | guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil),
251 | gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary,
252 | frameDuration = (gifInfo[kCGImagePropertyGIFDelayTime as String] as? NSNumber) else
253 | {
254 | return nil
255 | }
256 | gifDuration += frameDuration.doubleValue
257 | }
258 |
259 | images.append(Image.kf_imageWithCGImage(imageRef, scale: scale, refImage: nil))
260 | }
261 |
262 | return (images, gifDuration)
263 | }
264 |
265 | // Start of kf_animatedImageWithGIFData
266 | let options: NSDictionary = [kCGImageSourceShouldCache as String: NSNumber(bool: true), kCGImageSourceTypeIdentifierHint as String: kUTTypeGIF]
267 | guard let imageSource = CGImageSourceCreateWithData(data, options) else {
268 | return nil
269 | }
270 |
271 | #if os(OSX)
272 | guard let (images, gifDuration) = decodeFromSource(imageSource, options: options) else {
273 | return nil
274 | }
275 | let image = Image(data: data)
276 | image?.kf_images = images
277 | image?.kf_duration = gifDuration
278 |
279 | return image
280 | #else
281 |
282 | if preloadAll {
283 | guard let (images, gifDuration) = decodeFromSource(imageSource, options: options) else {
284 | return nil
285 | }
286 | let image = Image.kf_animatedImageWithImages(images, duration: duration <= 0.0 ? gifDuration : duration)
287 | image?.kf_animatedImageData = data
288 | return image
289 | } else {
290 | let image = Image(data: data)
291 | image?.kf_animatedImageData = data
292 | image?.kf_imageSource = ImageSource(ref: imageSource)
293 | return image
294 | }
295 | #endif
296 |
297 | }
298 | }
299 |
300 | // MARK: - Create images from data
301 | extension Image {
302 | static func kf_imageWithData(data: NSData, scale: CGFloat, preloadAllGIFData: Bool) -> Image? {
303 | var image: Image?
304 | #if os(OSX)
305 | switch data.kf_imageFormat {
306 | case .JPEG: image = Image(data: data)
307 | case .PNG: image = Image(data: data)
308 | case .GIF: image = Image.kf_animatedImageWithGIFData(gifData: data, scale: scale, duration: 0.0, preloadAll: preloadAllGIFData)
309 | case .Unknown: image = Image(data: data)
310 | }
311 | #else
312 | switch data.kf_imageFormat {
313 | case .JPEG: image = Image(data: data, scale: scale)
314 | case .PNG: image = Image(data: data, scale: scale)
315 | case .GIF: image = Image.kf_animatedImageWithGIFData(gifData: data, scale: scale, duration: 0.0, preloadAll: preloadAllGIFData)
316 | case .Unknown: image = Image(data: data, scale: scale)
317 | }
318 | #endif
319 |
320 | return image
321 | }
322 | }
323 |
324 | // MARK: - Decode
325 | extension Image {
326 | func kf_decodedImage() -> Image? {
327 | return self.kf_decodedImage(scale: kf_scale)
328 | }
329 |
330 | func kf_decodedImage(scale scale: CGFloat) -> Image? {
331 | // prevent animated image (GIF) lose it's images
332 | #if os(iOS)
333 | if kf_imageSource != nil {
334 | return self
335 | }
336 | #else
337 | if kf_images != nil {
338 | return self
339 | }
340 | #endif
341 |
342 | let imageRef = self.CGImage
343 | let colorSpace = CGColorSpaceCreateDeviceRGB()
344 | let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue).rawValue
345 |
346 | let context = CGBitmapContextCreate(nil, CGImageGetWidth(imageRef), CGImageGetHeight(imageRef), 8, 0, colorSpace, bitmapInfo)
347 | if let context = context {
348 | let rect = CGRect(x: 0, y: 0, width: CGImageGetWidth(imageRef), height: CGImageGetHeight(imageRef))
349 | CGContextDrawImage(context, rect, imageRef)
350 | let decompressedImageRef = CGBitmapContextCreateImage(context)
351 | return Image.kf_imageWithCGImage(decompressedImageRef!, scale: scale, refImage: self)
352 | } else {
353 | return nil
354 | }
355 | }
356 | }
357 |
358 | /// Reference the source image reference
359 | class ImageSource {
360 | var imageRef: CGImageSourceRef?
361 | init(ref: CGImageSourceRef) {
362 | self.imageRef = ref
363 | }
364 | }
365 |
366 | // MARK: - Image format
367 | private let pngHeader: [UInt8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
368 | private let jpgHeaderSOI: [UInt8] = [0xFF, 0xD8]
369 | private let jpgHeaderIF: [UInt8] = [0xFF]
370 | private let gifHeader: [UInt8] = [0x47, 0x49, 0x46]
371 |
372 | enum ImageFormat {
373 | case Unknown, PNG, JPEG, GIF
374 | }
375 |
376 | extension NSData {
377 | var kf_imageFormat: ImageFormat {
378 | var buffer = [UInt8](count: 8, repeatedValue: 0)
379 | self.getBytes(&buffer, length: 8)
380 | if buffer == pngHeader {
381 | return .PNG
382 | } else if buffer[0] == jpgHeaderSOI[0] &&
383 | buffer[1] == jpgHeaderSOI[1] &&
384 | buffer[2] == jpgHeaderIF[0]
385 | {
386 | return .JPEG
387 | } else if buffer[0] == gifHeader[0] &&
388 | buffer[1] == gifHeader[1] &&
389 | buffer[2] == gifHeader[2]
390 | {
391 | return .GIF
392 | }
393 |
394 | return .Unknown
395 | }
396 | }
397 |
--------------------------------------------------------------------------------
/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) 2016 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(OSX)
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]) -> ())
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]) -> ())
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 = [NSURL: RetrieveImageDownloadTask]()
62 |
63 | private var skippedResources = [Resource]()
64 | private var completedResources = [Resource]()
65 | private var failedResources = [Resource]()
66 |
67 | private var requestedCount = 0
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
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 optionsInfo: 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: [NSURL],
96 | optionsInfo: KingfisherOptionsInfo? = nil,
97 | progressBlock: PrefetcherProgressBlock? = nil,
98 | completionHandler: PrefetcherCompletionHandler? = nil)
99 | {
100 | let resources = urls.map { Resource(downloadURL: $0) }
101 | self.init(resources: resources, optionsInfo: optionsInfo, 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 optionsInfo: 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 | optionsInfo: KingfisherOptionsInfo? = nil,
124 | progressBlock: PrefetcherProgressBlock? = nil,
125 | completionHandler: PrefetcherCompletionHandler? = nil)
126 | {
127 | prefetchResources = resources
128 |
129 | // We want all callbacks from main queue, so we ignore the call back queue in options
130 | let optionsInfoWithoutQueue = optionsInfo?.kf_removeAllMatchesIgnoringAssociatedValue(.CallbackDispatchQueue(nil))
131 | self.optionsInfo = optionsInfoWithoutQueue ?? KingfisherEmptyOptionsInfo
132 |
133 | let cache = self.optionsInfo.targetCache ?? ImageCache.defaultCache
134 | let downloader = self.optionsInfo.downloader ?? ImageDownloader.defaultDownloader
135 | manager = KingfisherManager(downloader: downloader, cache: cache)
136 |
137 | self.progressBlock = progressBlock
138 | self.completionHandler = completionHandler
139 | }
140 |
141 | /**
142 | Start to download the resources and cache them. This can be useful for background downloading
143 | of assets that are required for later use in an app. This code will not try and update any UI
144 | with the results of the process.
145 | */
146 | public func start()
147 | {
148 | // Since we want to handle the resources cancellation in main thread only.
149 | dispatch_async_safely_to_main_queue { () -> () in
150 |
151 | guard !self.stopped else {
152 | assertionFailure("You can not restart the same prefetcher. Try to create a new prefetcher.")
153 | self.handleComplete()
154 | return
155 | }
156 |
157 | guard self.maxConcurrentDownloads > 0 else {
158 | assertionFailure("There should be concurrent downloads value should be at least 1.")
159 | self.handleComplete()
160 | return
161 | }
162 |
163 | guard self.prefetchResources.count > 0 else {
164 | self.handleComplete()
165 | return
166 | }
167 |
168 | let initialConcurentDownloads = min(self.prefetchResources.count, self.maxConcurrentDownloads)
169 | for i in 0 ..< initialConcurentDownloads {
170 | self.startPrefetchingResource(self.prefetchResources[i])
171 | }
172 | }
173 | }
174 |
175 |
176 | /**
177 | Stop current downloading progress, and cancel any future prefetching activity that might be occuring.
178 | */
179 | public func stop() {
180 | dispatch_async_safely_to_main_queue {
181 |
182 | if self.finished {
183 | return
184 | }
185 |
186 | self.stopped = true
187 | self.tasks.forEach { (_, task) -> () in
188 | task.cancel()
189 | }
190 | }
191 | }
192 |
193 | func downloadAndCacheResource(resource: Resource) {
194 |
195 | let task = RetrieveImageTask()
196 | let downloadTask = manager.downloadAndCacheImageWithURL(
197 | resource.downloadURL,
198 | forKey: resource.cacheKey,
199 | retrieveImageTask: task,
200 | progressBlock: nil,
201 | completionHandler: {
202 | (image, error, _, _) -> () in
203 |
204 | self.tasks.removeValueForKey(resource.downloadURL)
205 |
206 | if let _ = error {
207 | self.failedResources.append(resource)
208 | } else {
209 | self.completedResources.append(resource)
210 | }
211 |
212 | self.reportProgress()
213 |
214 | if self.stopped {
215 | if self.tasks.isEmpty {
216 | let pendingResources = self.prefetchResources[self.requestedCount..
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(OSX)
28 | // Not implemented for OSX and watchOS yet.
29 |
30 | import AppKit
31 |
32 | public enum ImageTransition {
33 | case None
34 | var duration: NSTimeInterval {
35 | return 0
36 | }
37 | }
38 |
39 | #elseif os(watchOS)
40 | import UIKit
41 | public enum ImageTransition {
42 | case None
43 | var duration: NSTimeInterval {
44 | return 0
45 | }
46 | }
47 | #else
48 | import UIKit
49 |
50 | /**
51 | Transition effect to use when an image downloaded and set by `UIImageView` extension API in Kingfisher.
52 | You can assign an enum value with transition duration as an item in `KingfisherOptionsInfo`
53 | to enable the animation transition.
54 |
55 | Apple's UIViewAnimationOptions is used under the hood.
56 | For custom transition, you should specified your own transition options, animations and
57 | comletion handler as well.
58 |
59 | - None: No animation transistion.
60 | - Fade: Fade in the loaded image.
61 | - FlipFromLeft: Flip from left transition.
62 | - FlipFromRight: Flip from right transition.
63 | - FlipFromTop: Flip from top transition.
64 | - FlipFromBottom: Flip from bottom transition.
65 | - Custom: Custom transition.
66 | */
67 | public enum ImageTransition {
68 | case None
69 | case Fade(NSTimeInterval)
70 |
71 | case FlipFromLeft(NSTimeInterval)
72 | case FlipFromRight(NSTimeInterval)
73 | case FlipFromTop(NSTimeInterval)
74 | case FlipFromBottom(NSTimeInterval)
75 |
76 | case Custom(duration: NSTimeInterval,
77 | options: UIViewAnimationOptions,
78 | animations: ((UIImageView, UIImage) -> Void)?,
79 | completion: ((Bool) -> Void)?)
80 |
81 | var duration: NSTimeInterval {
82 | switch self {
83 | case .None: return 0
84 | case .Fade(let duration): return duration
85 |
86 | case .FlipFromLeft(let duration): return duration
87 | case .FlipFromRight(let duration): return duration
88 | case .FlipFromTop(let duration): return duration
89 | case .FlipFromBottom(let duration): return duration
90 |
91 | case .Custom(let duration, _, _, _): return duration
92 | }
93 | }
94 |
95 | var animationOptions: UIViewAnimationOptions {
96 | switch self {
97 | case .None: return .TransitionNone
98 | case .Fade(_): return .TransitionCrossDissolve
99 |
100 | case .FlipFromLeft(_): return .TransitionFlipFromLeft
101 | case .FlipFromRight(_): return .TransitionFlipFromRight
102 | case .FlipFromTop(_): return .TransitionFlipFromTop
103 | case .FlipFromBottom(_): return .TransitionFlipFromBottom
104 |
105 | case .Custom(_, let options, _, _): return options
106 | }
107 | }
108 |
109 | var animations: ((UIImageView, UIImage) -> Void)? {
110 | switch self {
111 | case .Custom(_, _, let animations, _): return animations
112 | default: return {$0.image = $1}
113 | }
114 | }
115 |
116 | var completion: ((Bool) -> Void)? {
117 | switch self {
118 | case .Custom(_, _, _, let completion): return completion
119 | default: return nil
120 | }
121 | }
122 | }
123 | #endif
124 |
--------------------------------------------------------------------------------
/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) 2016 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(OSX)
29 | import AppKit
30 | typealias ImageView = NSImageView
31 | public typealias IndicatorView = NSProgressIndicator
32 | #else
33 | import UIKit
34 | typealias ImageView = UIImageView
35 | public typealias IndicatorView = UIActivityIndicatorView
36 | #endif
37 |
38 | // MARK: - Set Images
39 | /**
40 | * Set image to use from web.
41 | */
42 | extension ImageView {
43 |
44 | /**
45 | Set an image with a URL, a placeholder image, options, progress handler and completion handler.
46 |
47 | - parameter URL: The URL of image.
48 | - parameter placeholderImage: A placeholder image when retrieving the image at URL.
49 | - parameter optionsInfo: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
50 | - parameter progressBlock: Called when the image downloading progress gets updated.
51 | - parameter completionHandler: Called when the image retrieved and set.
52 |
53 | - returns: A task represents the retrieving process.
54 |
55 | - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
56 | The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
57 | */
58 |
59 | public func kf_setImageWithURL(URL: NSURL?,
60 | placeholderImage: Image? = nil,
61 | optionsInfo: KingfisherOptionsInfo? = nil,
62 | progressBlock: DownloadProgressBlock? = nil,
63 | completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
64 | {
65 | let resource = URL.map { Resource(downloadURL: $0) }
66 | return kf_setImageWithResource(resource,
67 | placeholderImage: placeholderImage,
68 | optionsInfo: optionsInfo,
69 | progressBlock: progressBlock,
70 | completionHandler: completionHandler)
71 | }
72 |
73 |
74 | /**
75 | Set an image with a URL, a placeholder image, options, progress handler and completion handler.
76 |
77 | - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`.
78 | - parameter placeholderImage: A placeholder image when retrieving the image at URL.
79 | - parameter optionsInfo: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
80 | - parameter progressBlock: Called when the image downloading progress gets updated.
81 | - parameter completionHandler: Called when the image retrieved and set.
82 |
83 | - returns: A task represents the retrieving process.
84 |
85 | - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
86 | The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
87 | */
88 | public func kf_setImageWithResource(resource: Resource?,
89 | placeholderImage: Image? = nil,
90 | optionsInfo: KingfisherOptionsInfo? = nil,
91 | progressBlock: DownloadProgressBlock? = nil,
92 | completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
93 | {
94 | image = placeholderImage
95 |
96 | guard let resource = resource else {
97 | completionHandler?(image: nil, error: nil, cacheType: .None, imageURL: nil)
98 | return RetrieveImageTask.emptyTask
99 | }
100 |
101 | let showIndicatorWhenLoading = kf_showIndicatorWhenLoading
102 | var indicator: IndicatorView? = nil
103 | if showIndicatorWhenLoading {
104 | indicator = kf_indicator
105 | indicator?.hidden = false
106 | indicator?.kf_startAnimating()
107 | }
108 |
109 | kf_setWebURL(resource.downloadURL)
110 |
111 | var options = optionsInfo ?? []
112 | if shouldPreloadAllGIF() {
113 | options.append(.PreloadAllGIFData)
114 | }
115 |
116 | let task = KingfisherManager.sharedManager.retrieveImageWithResource(resource, optionsInfo: options,
117 | progressBlock: { receivedSize, totalSize in
118 | if let progressBlock = progressBlock {
119 | progressBlock(receivedSize: receivedSize, totalSize: totalSize)
120 | }
121 | },
122 | completionHandler: {[weak self] image, error, cacheType, imageURL in
123 |
124 | dispatch_async_safely_to_main_queue {
125 | guard let sSelf = self where imageURL == sSelf.kf_webURL else {
126 | return
127 | }
128 |
129 | sSelf.kf_setImageTask(nil)
130 |
131 | guard let image = image else {
132 | indicator?.kf_stopAnimating()
133 | completionHandler?(image: nil, error: error, cacheType: cacheType, imageURL: imageURL)
134 | return
135 | }
136 |
137 | if let transitionItem = options.kf_firstMatchIgnoringAssociatedValue(.Transition(.None)),
138 | case .Transition(let transition) = transitionItem where ( options.forceTransition || cacheType == .None) {
139 | #if !os(OSX)
140 | UIView.transitionWithView(sSelf, duration: 0.0, options: [],
141 | animations: {
142 | indicator?.kf_stopAnimating()
143 | },
144 | completion: { finished in
145 | UIView.transitionWithView(sSelf, duration: transition.duration,
146 | options: [transition.animationOptions, .AllowUserInteraction],
147 | animations: {
148 | // Set image property in the animation.
149 | transition.animations?(sSelf, image)
150 | },
151 | completion: { finished in
152 | transition.completion?(finished)
153 | completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL)
154 | })
155 | })
156 | #endif
157 | } else {
158 | indicator?.kf_stopAnimating()
159 | sSelf.image = image
160 | completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL)
161 | }
162 | }
163 | })
164 |
165 | kf_setImageTask(task)
166 |
167 | return task
168 | }
169 | }
170 |
171 | extension ImageView {
172 | func shouldPreloadAllGIF() -> Bool {
173 | return true
174 | }
175 | }
176 |
177 | extension ImageView {
178 | /**
179 | Cancel the image download task bounded to the image view if it is running.
180 | Nothing will happen if the downloading has already finished.
181 | */
182 | public func kf_cancelDownloadTask() {
183 | kf_imageTask?.downloadTask?.cancel()
184 | }
185 | }
186 |
187 | // MARK: - Associated Object
188 | private var lastURLKey: Void?
189 | private var indicatorKey: Void?
190 | private var showIndicatorWhenLoadingKey: Void?
191 | private var imageTaskKey: Void?
192 |
193 | extension ImageView {
194 | /// Get the image URL binded to this image view.
195 | public var kf_webURL: NSURL? {
196 | return objc_getAssociatedObject(self, &lastURLKey) as? NSURL
197 | }
198 |
199 | private func kf_setWebURL(URL: NSURL) {
200 | objc_setAssociatedObject(self, &lastURLKey, URL, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
201 | }
202 |
203 | /// Whether show an animating indicator when the image view is loading an image or not.
204 | /// Default is false.
205 | public var kf_showIndicatorWhenLoading: Bool {
206 | get {
207 | if let result = objc_getAssociatedObject(self, &showIndicatorWhenLoadingKey) as? NSNumber {
208 | return result.boolValue
209 | } else {
210 | return false
211 | }
212 | }
213 |
214 | set {
215 | if kf_showIndicatorWhenLoading == newValue {
216 | return
217 | } else {
218 | if newValue {
219 |
220 | #if os(OSX)
221 | let indicator = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
222 | indicator.controlSize = .SmallControlSize
223 | indicator.style = .SpinningStyle
224 | #else
225 | #if os(tvOS)
226 | let indicatorStyle = UIActivityIndicatorViewStyle.White
227 | #else
228 | let indicatorStyle = UIActivityIndicatorViewStyle.Gray
229 | #endif
230 | let indicator = UIActivityIndicatorView(activityIndicatorStyle:indicatorStyle)
231 | indicator.autoresizingMask = [.FlexibleLeftMargin, .FlexibleRightMargin, .FlexibleBottomMargin, .FlexibleTopMargin]
232 | #endif
233 |
234 | indicator.kf_center = CGPoint(x: CGRectGetMidX(bounds), y: CGRectGetMidY(bounds))
235 | indicator.hidden = true
236 |
237 | self.addSubview(indicator)
238 |
239 | kf_setIndicator(indicator)
240 | } else {
241 | kf_indicator?.removeFromSuperview()
242 | kf_setIndicator(nil)
243 | }
244 |
245 | objc_setAssociatedObject(self, &showIndicatorWhenLoadingKey, NSNumber(bool: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
246 | }
247 | }
248 | }
249 |
250 | /// The indicator view showing when loading. This will be `nil` if `kf_showIndicatorWhenLoading` is false.
251 | /// You may want to use this to set the indicator style or color when you set `kf_showIndicatorWhenLoading` to true.
252 | public var kf_indicator: IndicatorView? {
253 | return objc_getAssociatedObject(self, &indicatorKey) as? IndicatorView
254 | }
255 |
256 | private func kf_setIndicator(indicator: IndicatorView?) {
257 | objc_setAssociatedObject(self, &indicatorKey, indicator, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
258 | }
259 |
260 | private var kf_imageTask: RetrieveImageTask? {
261 | return objc_getAssociatedObject(self, &imageTaskKey) as? RetrieveImageTask
262 | }
263 |
264 | private func kf_setImageTask(task: RetrieveImageTask?) {
265 | objc_setAssociatedObject(self, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
266 | }
267 | }
268 |
269 |
270 | extension IndicatorView {
271 | func kf_startAnimating() {
272 | #if os(OSX)
273 | startAnimation(nil)
274 | #else
275 | startAnimating()
276 | #endif
277 | hidden = false
278 | }
279 |
280 | func kf_stopAnimating() {
281 | #if os(OSX)
282 | stopAnimation(nil)
283 | #else
284 | stopAnimating()
285 | #endif
286 | hidden = true
287 | }
288 |
289 | #if os(OSX)
290 | var kf_center: CGPoint {
291 | get {
292 | return CGPoint(x: frame.origin.x + frame.size.width / 2.0, y: frame.origin.y + frame.size.height / 2.0 )
293 | }
294 | set {
295 | let newFrame = CGRect(x: newValue.x - frame.size.width / 2.0, y: newValue.y - frame.size.height / 2.0, width: frame.size.width, height: frame.size.height)
296 | frame = newFrame
297 | }
298 | }
299 | #else
300 | var kf_center: CGPoint {
301 | get {
302 | return center
303 | }
304 | set {
305 | center = newValue
306 | }
307 | }
308 | #endif
309 | }
310 |
--------------------------------------------------------------------------------
/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) 2016 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/KingfisherManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KingfisherManager.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 15/4/6.
6 | //
7 | // Copyright (c) 2016 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(OSX)
28 | import AppKit
29 | #else
30 | import UIKit
31 | #endif
32 |
33 | public typealias DownloadProgressBlock = ((receivedSize: Int64, totalSize: Int64) -> ())
34 | public typealias CompletionHandler = ((image: Image?, error: NSError?, cacheType: CacheType, imageURL: NSURL?) -> ())
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 class RetrieveImageTask {
39 |
40 | static let emptyTask = 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 disk retrieve task in this image task. Kingfisher will try to look up in cache first. This task represent the cache search task.
47 | public var diskRetrieveTask: RetrieveImageDiskTask?
48 |
49 | /// The network retrieve task in this image task.
50 | public var downloadTask: RetrieveImageDownloadTask?
51 |
52 | /**
53 | Cancel current task. If this task does not begin or already done, do nothing.
54 | */
55 | public func cancel() {
56 | // From Xcode 7 beta 6, the `dispatch_block_cancel` will crash at runtime.
57 | // It fixed in Xcode 7.1.
58 | // See https://github.com/onevcat/Kingfisher/issues/99 for more.
59 | if let diskRetrieveTask = diskRetrieveTask {
60 | dispatch_block_cancel(diskRetrieveTask)
61 | }
62 |
63 | if let downloadTask = downloadTask {
64 | downloadTask.cancel()
65 | } else {
66 | cancelledBeforeDownloadStarting = true
67 | }
68 | }
69 | }
70 |
71 | /// Error domain of Kingfisher
72 | public let KingfisherErrorDomain = "com.onevcat.Kingfisher.Error"
73 |
74 | private let instance = KingfisherManager()
75 |
76 | /// Main manager class of Kingfisher. It connects Kingfisher downloader and cache.
77 | /// You can use this class to retrieve an image via a specified URL from web or cache.
78 | public class KingfisherManager {
79 |
80 | /// Shared manager used by the extensions across Kingfisher.
81 | public class var sharedManager: KingfisherManager {
82 | return instance
83 | }
84 |
85 | /// Cache used by this manager
86 | public var cache: ImageCache
87 |
88 | /// Downloader used by this manager
89 | public var downloader: ImageDownloader
90 |
91 | /**
92 | Default init method
93 |
94 | - returns: A Kingfisher manager object with default cache, default downloader, and default prefetcher.
95 | */
96 | public convenience init() {
97 | self.init(downloader: ImageDownloader.defaultDownloader, cache: ImageCache.defaultCache)
98 | }
99 |
100 | init(downloader: ImageDownloader, cache: ImageCache) {
101 | self.downloader = downloader
102 | self.cache = cache
103 | }
104 |
105 | /**
106 | Get an image with resource.
107 | If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first.
108 | If not found, it will download the image at `resource.downloadURL` and cache it with `resource.cacheKey`.
109 | These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more.
110 |
111 | - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`.
112 | - parameter optionsInfo: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
113 | - parameter progressBlock: Called every time downloaded data changed. This could be used as a progress UI.
114 | - parameter completionHandler: Called when the whole retrieving process finished.
115 |
116 | - returns: A `RetrieveImageTask` task object. You can use this object to cancel the task.
117 | */
118 | public func retrieveImageWithResource(resource: Resource,
119 | optionsInfo: KingfisherOptionsInfo?,
120 | progressBlock: DownloadProgressBlock?,
121 | completionHandler: CompletionHandler?) -> RetrieveImageTask
122 | {
123 | let task = RetrieveImageTask()
124 |
125 | if let optionsInfo = optionsInfo where optionsInfo.forceRefresh {
126 | downloadAndCacheImageWithURL(resource.downloadURL,
127 | forKey: resource.cacheKey,
128 | retrieveImageTask: task,
129 | progressBlock: progressBlock,
130 | completionHandler: completionHandler,
131 | options: optionsInfo)
132 | } else {
133 | tryToRetrieveImageFromCacheForKey(resource.cacheKey,
134 | withURL: resource.downloadURL,
135 | retrieveImageTask: task,
136 | progressBlock: progressBlock,
137 | completionHandler: completionHandler,
138 | options: optionsInfo)
139 | }
140 |
141 | return task
142 | }
143 |
144 | /**
145 | Get an image with `URL.absoluteString` as the key.
146 | If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first.
147 | If not found, it will download the image at URL and cache it with `URL.absoluteString` value as its key.
148 |
149 | If you need to specify the key other than `URL.absoluteString`, please use resource version of this API with `resource.cacheKey` set to what you want.
150 |
151 | These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more.
152 |
153 | - parameter URL: The image URL.
154 | - parameter optionsInfo: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
155 | - parameter progressBlock: Called every time downloaded data changed. This could be used as a progress UI.
156 | - parameter completionHandler: Called when the whole retrieving process finished.
157 |
158 | - returns: A `RetrieveImageTask` task object. You can use this object to cancel the task.
159 | */
160 | public func retrieveImageWithURL(URL: NSURL,
161 | optionsInfo: KingfisherOptionsInfo?,
162 | progressBlock: DownloadProgressBlock?,
163 | completionHandler: CompletionHandler?) -> RetrieveImageTask
164 | {
165 | return retrieveImageWithResource(Resource(downloadURL: URL), optionsInfo: optionsInfo, progressBlock: progressBlock, completionHandler: completionHandler)
166 | }
167 |
168 | func downloadAndCacheImageWithURL(URL: NSURL,
169 | forKey key: String,
170 | retrieveImageTask: RetrieveImageTask,
171 | progressBlock: DownloadProgressBlock?,
172 | completionHandler: CompletionHandler?,
173 | options: KingfisherOptionsInfo?) -> RetrieveImageDownloadTask?
174 | {
175 | let downloader = options?.downloader ?? self.downloader
176 | return downloader.downloadImageWithURL(URL, retrieveImageTask: retrieveImageTask, options: options,
177 | progressBlock: { receivedSize, totalSize in
178 | progressBlock?(receivedSize: receivedSize, totalSize: totalSize)
179 | },
180 | completionHandler: { image, error, imageURL, originalData in
181 |
182 | let targetCache = options?.targetCache ?? self.cache
183 | if let error = error where error.code == KingfisherError.NotModified.rawValue {
184 | // Not modified. Try to find the image from cache.
185 | // (The image should be in cache. It should be guaranteed by the framework users.)
186 | targetCache.retrieveImageForKey(key, options: options, completionHandler: { (cacheImage, cacheType) -> () in
187 | completionHandler?(image: cacheImage, error: nil, cacheType: cacheType, imageURL: URL)
188 |
189 | })
190 | return
191 | }
192 |
193 | if let image = image, originalData = originalData {
194 | targetCache.storeImage(image, originalData: originalData, forKey: key, toDisk: !(options?.cacheMemoryOnly ?? false), completionHandler: nil)
195 | }
196 |
197 | completionHandler?(image: image, error: error, cacheType: .None, imageURL: URL)
198 |
199 | })
200 | }
201 |
202 | func tryToRetrieveImageFromCacheForKey(key: String,
203 | withURL URL: NSURL,
204 | retrieveImageTask: RetrieveImageTask,
205 | progressBlock: DownloadProgressBlock?,
206 | completionHandler: CompletionHandler?,
207 | options: KingfisherOptionsInfo?)
208 | {
209 | let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> () in
210 | // Break retain cycle created inside diskTask closure below
211 | retrieveImageTask.diskRetrieveTask = nil
212 | completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL)
213 | }
214 |
215 | let targetCache = options?.targetCache ?? cache
216 | let diskTask = targetCache.retrieveImageForKey(key, options: options,
217 | completionHandler: { image, cacheType in
218 | if image != nil {
219 | diskTaskCompletionHandler(image: image, error: nil, cacheType:cacheType, imageURL: URL)
220 | } else {
221 | self.downloadAndCacheImageWithURL(URL,
222 | forKey: key,
223 | retrieveImageTask: retrieveImageTask,
224 | progressBlock: progressBlock,
225 | completionHandler: diskTaskCompletionHandler,
226 | options: options)
227 | }
228 | })
229 | retrieveImageTask.diskRetrieveTask = diskTask
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/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) 2016 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(OSX)
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 | - TargetCache: The associated value of this member should be an ImageCache object. Kingfisher will use the specified cache object when handling related operations, including trying to retrieve the cached images and store the downloaded image to it.
44 | - Downloader: The associated value of this member should be an ImageDownloader object. Kingfisher will use this downloader to download the images.
45 | - Transition: Member for animation transition when using UIImageView. Kingfisher will use the `ImageTransition` of this enum to animate the image in if it is downloaded from web. The transition will not happen when the image is retrieved from either memory or disk cache by default. If you need to do the transition even when the image being retrieved from cache, set `ForceTransition` as well.
46 | - DownloadPriority: Associated `Float` value will be set as the priority of image download task. The value for it should be between 0.0~1.0. If this option not set, the default value (`NSURLSessionTaskPriorityDefault`) will be used.
47 | - ForceRefresh: If set, `Kingfisher` will ignore the cache and try to fire a download task for the resource.
48 | - ForceTransition: If set, setting the image to an image view will happen with transition even when retrieved from cache. See `Transition` option for more.
49 | - CacheMemoryOnly: If set, `Kingfisher` will only cache the value in memory but not in disk.
50 | - BackgroundDecode: Decode the image in background thread before using.
51 | - CallbackDispatchQueue: The associated value of this member will be used as the target queue of dispatch callbacks when retrieving images from cache. If not set, `Kingfisher` will use main quese for callbacks.
52 | - ScaleFactor: The associated value of this member will be used as the scale factor when converting retrieved data to an image.
53 | - PreloadAllGIFData: Whether all the GIF data should be preloaded. Default it false, which means following frames will be loaded on need. If true, all the GIF data will be loaded and decoded into memory. This option is mainly used for back compatibility internally. You should not set it directly. `AnimatedImageView` will not preload all data, while a normal image view (`UIImageView` or `NSImageView`) will load all data. Choose to use corresponding image view type instead of setting this option.
54 | */
55 | public enum KingfisherOptionsInfoItem {
56 | case TargetCache(ImageCache?)
57 | case Downloader(ImageDownloader?)
58 | case Transition(ImageTransition)
59 | case DownloadPriority(Float)
60 | case ForceRefresh
61 | case ForceTransition
62 | case CacheMemoryOnly
63 | case BackgroundDecode
64 | case CallbackDispatchQueue(dispatch_queue_t?)
65 | case ScaleFactor(CGFloat)
66 | case PreloadAllGIFData
67 | }
68 |
69 | infix operator <== {
70 | associativity none
71 | precedence 160
72 | }
73 |
74 | // This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values.
75 | func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool {
76 | switch (lhs, rhs) {
77 | case (.TargetCache(_), .TargetCache(_)): fallthrough
78 | case (.Downloader(_), .Downloader(_)): fallthrough
79 | case (.Transition(_), .Transition(_)): fallthrough
80 | case (.DownloadPriority(_), .DownloadPriority(_)): fallthrough
81 | case (.ForceRefresh, .ForceRefresh): fallthrough
82 | case (.ForceTransition, .ForceTransition): fallthrough
83 | case (.CacheMemoryOnly, .CacheMemoryOnly): fallthrough
84 | case (.BackgroundDecode, .BackgroundDecode): fallthrough
85 | case (.CallbackDispatchQueue(_), .CallbackDispatchQueue(_)): fallthrough
86 | case (.ScaleFactor(_), .ScaleFactor(_)): fallthrough
87 | case (.PreloadAllGIFData, .PreloadAllGIFData): return true
88 |
89 | default: return false
90 | }
91 | }
92 |
93 | extension CollectionType where Generator.Element == KingfisherOptionsInfoItem {
94 | func kf_firstMatchIgnoringAssociatedValue(target: Generator.Element) -> Generator.Element? {
95 | return indexOf { $0 <== target }.flatMap { self[$0] }
96 | }
97 |
98 | func kf_removeAllMatchesIgnoringAssociatedValue(target: Generator.Element) -> [Generator.Element] {
99 | return self.filter { !($0 <== target) }
100 | }
101 | }
102 |
103 | extension CollectionType where Generator.Element == KingfisherOptionsInfoItem {
104 | var targetCache: ImageCache? {
105 | if let item = kf_firstMatchIgnoringAssociatedValue(.TargetCache(nil)),
106 | case .TargetCache(let cache) = item
107 | {
108 | return cache
109 | }
110 | return nil
111 | }
112 |
113 | var downloader: ImageDownloader? {
114 | if let item = kf_firstMatchIgnoringAssociatedValue(.Downloader(nil)),
115 | case .Downloader(let downloader) = item
116 | {
117 | return downloader
118 | }
119 | return nil
120 | }
121 |
122 | var transition: ImageTransition {
123 | if let item = kf_firstMatchIgnoringAssociatedValue(.Transition(.None)),
124 | case .Transition(let transition) = item
125 | {
126 | return transition
127 | }
128 | return ImageTransition.None
129 | }
130 |
131 | var downloadPriority: Float {
132 | if let item = kf_firstMatchIgnoringAssociatedValue(.DownloadPriority(0)),
133 | case .DownloadPriority(let priority) = item
134 | {
135 | return priority
136 | }
137 | return NSURLSessionTaskPriorityDefault
138 | }
139 |
140 | var forceRefresh: Bool {
141 | return contains{ $0 <== .ForceRefresh }
142 | }
143 |
144 | var forceTransition: Bool {
145 | return contains{ $0 <== .ForceTransition }
146 | }
147 |
148 | var cacheMemoryOnly: Bool {
149 | return contains{ $0 <== .CacheMemoryOnly }
150 | }
151 |
152 | var backgroundDecode: Bool {
153 | return contains{ $0 <== .BackgroundDecode }
154 | }
155 |
156 | var preloadAllGIFData: Bool {
157 | return contains { $0 <== .PreloadAllGIFData }
158 | }
159 |
160 | var callbackDispatchQueue: dispatch_queue_t {
161 | if let item = kf_firstMatchIgnoringAssociatedValue(.CallbackDispatchQueue(nil)),
162 | case .CallbackDispatchQueue(let queue) = item
163 | {
164 | return queue ?? dispatch_get_main_queue()
165 | }
166 | return dispatch_get_main_queue()
167 | }
168 |
169 | var scaleFactor: CGFloat {
170 | if let item = kf_firstMatchIgnoringAssociatedValue(.ScaleFactor(0)),
171 | case .ScaleFactor(let scale) = item
172 | {
173 | return scale
174 | }
175 | return 1.0
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/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) 2016 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 is a simple combination of `downloadURL` and `cacheKey`.
31 |
32 | When passed to image view set methods, Kingfisher will try to download the target
33 | image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache.
34 | */
35 | public struct Resource {
36 | /// The key used in cache.
37 | public let cacheKey: String
38 |
39 | /// The target image URL.
40 | public let downloadURL: NSURL
41 |
42 | /**
43 | Create a resource.
44 |
45 | - parameter downloadURL: The target image URL.
46 | - parameter cacheKey: The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key.
47 |
48 | - returns: A resource.
49 | */
50 | public init(downloadURL: NSURL, cacheKey: String? = nil) {
51 | self.downloadURL = downloadURL
52 | self.cacheKey = cacheKey ?? downloadURL.absoluteString
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/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 | extension String {
25 | var kf_MD5: String {
26 | if let data = dataUsingEncoding(NSUTF8StringEncoding) {
27 | let MD5Calculator = MD5(Array(UnsafeBufferPointer(start: UnsafePointer(data.bytes), count: data.length)))
28 | let MD5Data = MD5Calculator.calculate()
29 |
30 | let MD5String = NSMutableString()
31 | for c in MD5Data {
32 | MD5String.appendFormat("%02x", c)
33 | }
34 | return MD5String as String
35 |
36 | } else {
37 | return self
38 | }
39 | }
40 | }
41 |
42 | /** array of bytes, little-endian representation */
43 | func arrayOfBytes(value: T, length: Int? = nil) -> [UInt8] {
44 | let totalBytes = length ?? (sizeofValue(value) * 8)
45 |
46 | let valuePointer = UnsafeMutablePointer.alloc(1)
47 | valuePointer.memory = value
48 |
49 | let bytesPointer = UnsafeMutablePointer(valuePointer)
50 | var bytes = [UInt8](count: totalBytes, repeatedValue: 0)
51 | for j in 0.. [UInt8] {
64 | return arrayOfBytes(self, length: totalBytes)
65 | }
66 |
67 | }
68 |
69 | extension NSMutableData {
70 |
71 | /** Convenient way to append bytes */
72 | func appendBytes(arrayOfBytes: [UInt8]) {
73 | appendBytes(arrayOfBytes, length: arrayOfBytes.count)
74 | }
75 |
76 | }
77 |
78 | protocol HashProtocol {
79 | var message: Array { get }
80 |
81 | /** Common part for hash calculation. Prepare header data. */
82 | func prepare(len: Int) -> Array
83 | }
84 |
85 | extension HashProtocol {
86 |
87 | func prepare(len: Int) -> Array {
88 | var tmpMessage = message
89 |
90 | // Step 1. Append Padding Bits
91 | tmpMessage.append(0x80) // append one bit (UInt8 with one bit) to message
92 |
93 | // append "0" bit until message length in bits ≡ 448 (mod 512)
94 | var msgLength = tmpMessage.count
95 | var counter = 0
96 |
97 | while msgLength % len != (len - 8) {
98 | counter += 1
99 | msgLength += 1
100 | }
101 |
102 | tmpMessage += Array(count: counter, repeatedValue: 0)
103 | return tmpMessage
104 | }
105 | }
106 |
107 | func toUInt32Array(slice: ArraySlice) -> Array {
108 | var result = Array()
109 | result.reserveCapacity(16)
110 |
111 | for idx in slice.startIndex.stride(to: slice.endIndex, by: sizeof(UInt32)) {
112 | let d0 = UInt32(slice[idx.advancedBy(3)]) << 24
113 | let d1 = UInt32(slice[idx.advancedBy(2)]) << 16
114 | let d2 = UInt32(slice[idx.advancedBy(1)]) << 8
115 | let d3 = UInt32(slice[idx])
116 | let val: UInt32 = d0 | d1 | d2 | d3
117 |
118 | result.append(val)
119 | }
120 | return result
121 | }
122 |
123 | struct BytesGenerator: GeneratorType {
124 |
125 | let chunkSize: Int
126 | let data: [UInt8]
127 |
128 | init(chunkSize: Int, data: [UInt8]) {
129 | self.chunkSize = chunkSize
130 | self.data = data
131 | }
132 |
133 | var offset = 0
134 |
135 | mutating func next() -> ArraySlice? {
136 | let end = min(chunkSize, data.count - offset)
137 | let result = data[offset.. 0 ? result : nil
140 | }
141 | }
142 |
143 | struct BytesSequence: SequenceType {
144 | let chunkSize: Int
145 | let data: [UInt8]
146 |
147 | func generate() -> BytesGenerator {
148 | return BytesGenerator(chunkSize: chunkSize, data: data)
149 | }
150 | }
151 |
152 | func rotateLeft(value: UInt32, bits: UInt32) -> UInt32 {
153 | return ((value << bits) & 0xFFFFFFFF) | (value >> (32 - bits))
154 | }
155 |
156 | class MD5: HashProtocol {
157 |
158 | static let size = 16 // 128 / 8
159 | let message: [UInt8]
160 |
161 | init (_ message: [UInt8]) {
162 | self.message = message
163 | }
164 |
165 | /** specifies the per-round shift amounts */
166 | private let shifts: [UInt32] = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
167 | 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
168 | 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
169 | 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]
170 |
171 | /** binary integer part of the sines of integers (Radians) */
172 | private let sines: [UInt32] = [0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
173 | 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
174 | 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
175 | 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
176 | 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
177 | 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
178 | 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
179 | 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
180 | 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
181 | 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
182 | 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
183 | 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
184 | 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
185 | 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
186 | 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
187 | 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391]
188 |
189 | private let hashes: [UInt32] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]
190 |
191 | func calculate() -> [UInt8] {
192 | var tmpMessage = prepare(64)
193 | tmpMessage.reserveCapacity(tmpMessage.count + 4)
194 |
195 | // hash values
196 | var hh = hashes
197 |
198 | // Step 2. Append Length a 64-bit representation of lengthInBits
199 | let lengthInBits = (message.count * 8)
200 | let lengthBytes = lengthInBits.bytes(64 / 8)
201 | tmpMessage += lengthBytes.reverse()
202 |
203 | // Process the message in successive 512-bit chunks:
204 | let chunkSizeBytes = 512 / 8 // 64
205 |
206 | for chunk in BytesSequence(chunkSize: chunkSizeBytes, data: tmpMessage) {
207 | // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15
208 | var M = toUInt32Array(chunk)
209 | assert(M.count == 16, "Invalid array")
210 |
211 | // Initialize hash value for this chunk:
212 | var A: UInt32 = hh[0]
213 | var B: UInt32 = hh[1]
214 | var C: UInt32 = hh[2]
215 | var D: UInt32 = hh[3]
216 |
217 | var dTemp: UInt32 = 0
218 |
219 | // Main loop
220 | for j in 0 ..< sines.count {
221 | var g = 0
222 | var F: UInt32 = 0
223 |
224 | switch j {
225 | case 0...15:
226 | F = (B & C) | ((~B) & D)
227 | g = j
228 | break
229 | case 16...31:
230 | F = (D & B) | (~D & C)
231 | g = (5 * j + 1) % 16
232 | break
233 | case 32...47:
234 | F = B ^ C ^ D
235 | g = (3 * j + 5) % 16
236 | break
237 | case 48...63:
238 | F = C ^ (B | (~D))
239 | g = (7 * j) % 16
240 | break
241 | default:
242 | break
243 | }
244 | dTemp = D
245 | D = C
246 | C = B
247 | B = B &+ rotateLeft((A &+ F &+ sines[j] &+ M[g]), bits: shifts[j])
248 | A = dTemp
249 | }
250 |
251 | hh[0] = hh[0] &+ A
252 | hh[1] = hh[1] &+ B
253 | hh[2] = hh[2] &+ C
254 | hh[3] = hh[3] &+ D
255 | }
256 |
257 | var result = [UInt8]()
258 | result.reserveCapacity(hh.count / 4)
259 |
260 | hh.forEach {
261 | let itemLE = $0.littleEndian
262 | result += [UInt8(itemLE & 0xff), UInt8((itemLE >> 8) & 0xff), UInt8((itemLE >> 16) & 0xff), UInt8((itemLE >> 24) & 0xff)]
263 | }
264 | return result
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/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) 2016 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 | func dispatch_async_safely_to_main_queue(block: ()->()) {
30 | dispatch_async_safely_to_queue(dispatch_get_main_queue(), block)
31 | }
32 |
33 | // This method will dispatch the `block` to a specified `queue`.
34 | // If the `queue` is the main queue, and current thread is main thread, the block
35 | // will be invoked immediately instead of being dispatched.
36 | func dispatch_async_safely_to_queue(queue: dispatch_queue_t, _ block: ()->()) {
37 | if queue === dispatch_get_main_queue() && NSThread.isMainThread() {
38 | block()
39 | } else {
40 | dispatch_async(queue) {
41 | block()
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/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) 2016 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 | /**
30 | * Set image to use from web for a specified state.
31 | */
32 | extension UIButton {
33 | /**
34 | Set an image to use for a specified state with a URL, a placeholder image, options, progress handler and completion handler.
35 |
36 | - parameter URL: The URL of image for specified state.
37 | - parameter state: The state that uses the specified image.
38 | - parameter placeholderImage: A placeholder image when retrieving the image at URL.
39 | - parameter optionsInfo: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
40 | - parameter progressBlock: Called when the image downloading progress gets updated.
41 | - parameter completionHandler: Called when the image retrieved and set.
42 |
43 | - returns: A task represents the retrieving process.
44 |
45 | - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
46 | The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
47 | */
48 | public func kf_setImageWithURL(URL: NSURL?,
49 | forState state: UIControlState,
50 | placeholderImage: UIImage? = nil,
51 | optionsInfo: KingfisherOptionsInfo? = nil,
52 | progressBlock: DownloadProgressBlock? = nil,
53 | completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
54 | {
55 | let resource = URL.map { Resource(downloadURL: $0) }
56 | return kf_setImageWithResource(resource,
57 | forState: state,
58 | placeholderImage: placeholderImage,
59 | optionsInfo: optionsInfo,
60 | progressBlock: progressBlock,
61 | completionHandler: completionHandler)
62 | }
63 |
64 |
65 | /**
66 | Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and completion handler.
67 |
68 | - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`.
69 | - parameter state: The state that uses the specified image.
70 | - parameter placeholderImage: A placeholder image when retrieving the image at URL.
71 | - parameter optionsInfo: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
72 | - parameter progressBlock: Called when the image downloading progress gets updated.
73 | - parameter completionHandler: Called when the image retrieved and set.
74 |
75 | - returns: A task represents the retrieving process.
76 |
77 | - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
78 | The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
79 | */
80 | public func kf_setImageWithResource(resource: Resource?,
81 | forState state: UIControlState,
82 | placeholderImage: UIImage? = nil,
83 | optionsInfo: KingfisherOptionsInfo? = nil,
84 | progressBlock: DownloadProgressBlock? = nil,
85 | completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
86 | {
87 | setImage(placeholderImage, forState: state)
88 |
89 | guard let resource = resource else {
90 | completionHandler?(image: nil, error: nil, cacheType: .None, imageURL: nil)
91 | return RetrieveImageTask.emptyTask
92 | }
93 |
94 | kf_setWebURL(resource.downloadURL, forState: state)
95 | let task = KingfisherManager.sharedManager.retrieveImageWithResource(resource, optionsInfo: optionsInfo,
96 | progressBlock: { receivedSize, totalSize in
97 | if let progressBlock = progressBlock {
98 | progressBlock(receivedSize: receivedSize, totalSize: totalSize)
99 | }
100 | },
101 | completionHandler: {[weak self] image, error, cacheType, imageURL in
102 | dispatch_async_safely_to_main_queue {
103 | guard let sSelf = self where imageURL == sSelf.kf_webURLForState(state) else {
104 | return
105 | }
106 |
107 | sSelf.kf_setImageTask(nil)
108 |
109 | if image != nil {
110 | sSelf.setImage(image, forState: state)
111 | }
112 |
113 | completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL)
114 | }
115 | })
116 |
117 | kf_setImageTask(task)
118 | return task
119 | }
120 | }
121 |
122 | private var lastURLKey: Void?
123 | private var imageTaskKey: Void?
124 |
125 | // MARK: - Runtime for UIButton image
126 | extension UIButton {
127 | /**
128 | Get the image URL binded to this button for a specified state.
129 |
130 | - parameter state: The state that uses the specified image.
131 |
132 | - returns: Current URL for image.
133 | */
134 | public func kf_webURLForState(state: UIControlState) -> NSURL? {
135 | return kf_webURLs[NSNumber(unsignedLong:state.rawValue)] as? NSURL
136 | }
137 |
138 | private func kf_setWebURL(URL: NSURL, forState state: UIControlState) {
139 | kf_webURLs[NSNumber(unsignedLong:state.rawValue)] = URL
140 | }
141 |
142 | private var kf_webURLs: NSMutableDictionary {
143 | var dictionary = objc_getAssociatedObject(self, &lastURLKey) as? NSMutableDictionary
144 | if dictionary == nil {
145 | dictionary = NSMutableDictionary()
146 | kf_setWebURLs(dictionary!)
147 | }
148 | return dictionary!
149 | }
150 |
151 | private func kf_setWebURLs(URLs: NSMutableDictionary) {
152 | objc_setAssociatedObject(self, &lastURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
153 | }
154 |
155 | private var kf_imageTask: RetrieveImageTask? {
156 | return objc_getAssociatedObject(self, &imageTaskKey) as? RetrieveImageTask
157 | }
158 |
159 | private func kf_setImageTask(task: RetrieveImageTask?) {
160 | objc_setAssociatedObject(self, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
161 | }
162 | }
163 |
164 | /**
165 | * Set background image to use from web for a specified state.
166 | */
167 | extension UIButton {
168 | /**
169 | Set the background image to use for a specified state with a URL,
170 | a placeholder image, options progress handler and completion handler.
171 |
172 | - parameter URL: The URL of image for specified state.
173 | - parameter state: The state that uses the specified image.
174 | - parameter placeholderImage: A placeholder image when retrieving the image at URL.
175 | - parameter optionsInfo: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
176 | - parameter progressBlock: Called when the image downloading progress gets updated.
177 | - parameter completionHandler: Called when the image retrieved and set.
178 |
179 | - returns: A task represents the retrieving process.
180 |
181 | - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
182 | The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
183 | */
184 | public func kf_setBackgroundImageWithURL(URL: NSURL?,
185 | forState state: UIControlState,
186 | placeholderImage: UIImage? = nil,
187 | optionsInfo: KingfisherOptionsInfo? = nil,
188 | progressBlock: DownloadProgressBlock? = nil,
189 | completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
190 | {
191 | let resource = URL.map { Resource(downloadURL: $0) }
192 | return kf_setBackgroundImageWithResource(resource,
193 | forState: state,
194 | placeholderImage: placeholderImage,
195 | optionsInfo: optionsInfo,
196 | progressBlock: progressBlock,
197 | completionHandler: completionHandler)
198 | }
199 |
200 |
201 | /**
202 | Set the background image to use for a specified state with a resource,
203 | a placeholder image, options progress handler and completion handler.
204 |
205 | - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`.
206 | - parameter state: The state that uses the specified image.
207 | - parameter placeholderImage: A placeholder image when retrieving the image at URL.
208 | - parameter optionsInfo: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
209 | - parameter progressBlock: Called when the image downloading progress gets updated.
210 | - parameter completionHandler: Called when the image retrieved and set.
211 |
212 | - returns: A task represents the retrieving process.
213 |
214 | - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
215 | The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
216 | */
217 | public func kf_setBackgroundImageWithResource(resource: Resource?,
218 | forState state: UIControlState,
219 | placeholderImage: UIImage? = nil,
220 | optionsInfo: KingfisherOptionsInfo? = nil,
221 | progressBlock: DownloadProgressBlock? = nil,
222 | completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
223 | {
224 | setBackgroundImage(placeholderImage, forState: state)
225 |
226 | guard let resource = resource else {
227 | completionHandler?(image: nil, error: nil, cacheType: .None, imageURL: nil)
228 | return RetrieveImageTask.emptyTask
229 | }
230 |
231 | kf_setBackgroundWebURL(resource.downloadURL, forState: state)
232 | let task = KingfisherManager.sharedManager.retrieveImageWithResource(resource, optionsInfo: optionsInfo,
233 | progressBlock: { receivedSize, totalSize in
234 | if let progressBlock = progressBlock {
235 | progressBlock(receivedSize: receivedSize, totalSize: totalSize)
236 | }
237 | },
238 | completionHandler: { [weak self] image, error, cacheType, imageURL in
239 | dispatch_async_safely_to_main_queue {
240 | guard let sSelf = self where imageURL == sSelf.kf_backgroundWebURLForState(state) else {
241 | return
242 | }
243 |
244 | sSelf.kf_setBackgroundImageTask(nil)
245 |
246 | if image != nil {
247 | sSelf.setBackgroundImage(image, forState: state)
248 | }
249 | completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL)
250 | }
251 | })
252 |
253 | kf_setBackgroundImageTask(task)
254 | return task
255 | }
256 | }
257 |
258 | private var lastBackgroundURLKey: Void?
259 | private var backgroundImageTaskKey: Void?
260 |
261 | // MARK: - Runtime for UIButton background image
262 | extension UIButton {
263 | /**
264 | Get the background image URL binded to this button for a specified state.
265 |
266 | - parameter state: The state that uses the specified background image.
267 |
268 | - returns: Current URL for background image.
269 | */
270 | public func kf_backgroundWebURLForState(state: UIControlState) -> NSURL? {
271 | return kf_backgroundWebURLs[NSNumber(unsignedLong:state.rawValue)] as? NSURL
272 | }
273 |
274 | private func kf_setBackgroundWebURL(URL: NSURL, forState state: UIControlState) {
275 | kf_backgroundWebURLs[NSNumber(unsignedLong:state.rawValue)] = URL
276 | }
277 |
278 | private var kf_backgroundWebURLs: NSMutableDictionary {
279 | var dictionary = objc_getAssociatedObject(self, &lastBackgroundURLKey) as? NSMutableDictionary
280 | if dictionary == nil {
281 | dictionary = NSMutableDictionary()
282 | kf_setBackgroundWebURLs(dictionary!)
283 | }
284 | return dictionary!
285 | }
286 |
287 | private func kf_setBackgroundWebURLs(URLs: NSMutableDictionary) {
288 | objc_setAssociatedObject(self, &lastBackgroundURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
289 | }
290 |
291 | private var kf_backgroundImageTask: RetrieveImageTask? {
292 | return objc_getAssociatedObject(self, &backgroundImageTaskKey) as? RetrieveImageTask
293 | }
294 |
295 | private func kf_setBackgroundImageTask(task: RetrieveImageTask?) {
296 | objc_setAssociatedObject(self, &backgroundImageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
297 | }
298 | }
299 |
300 | // MARK: - Cancel image download tasks.
301 | extension UIButton {
302 | /**
303 | Cancel the image download task bounded to the image view if it is running.
304 | Nothing will happen if the downloading has already finished.
305 | */
306 | public func kf_cancelImageDownloadTask() {
307 | kf_imageTask?.downloadTask?.cancel()
308 | }
309 |
310 | /**
311 | Cancel the background image download task bounded to the image view if it is running.
312 | Nothing will happen if the downloading has already finished.
313 | */
314 | public func kf_cancelBackgroundImageDownloadTask() {
315 | kf_backgroundImageTask?.downloadTask?.cancel()
316 | }
317 | }
318 |
--------------------------------------------------------------------------------
/Example/Pods/Local Podspecs/ParallexBanner.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ParallexBanner",
3 | "version": "0.1.0",
4 | "summary": "A short description of ParallexBanner.",
5 | "description": "TODO: Add long description of the pod here.",
6 | "homepage": "https://github.com//ParallexBanner",
7 | "license": {
8 | "type": "MIT",
9 | "file": "LICENSE"
10 | },
11 | "authors": {
12 | "leo": "leomobiledeveloper@gmail.com"
13 | },
14 | "source": {
15 | "git": "https://github.com//ParallexBanner.git",
16 | "tag": "0.1.0"
17 | },
18 | "platforms": {
19 | "ios": "8.0"
20 | },
21 | "source_files": "ParallexBanner/Classes/**/*",
22 | "frameworks": "UIKit",
23 | "dependencies": {
24 | "Kingfisher": [
25 |
26 | ]
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Example/Pods/Manifest.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Kingfisher (2.4.2)
3 | - ParallexBanner (0.1.0):
4 | - Kingfisher
5 |
6 | DEPENDENCIES:
7 | - ParallexBanner (from `../`)
8 |
9 | EXTERNAL SOURCES:
10 | ParallexBanner:
11 | :path: ../
12 |
13 | SPEC CHECKSUMS:
14 | Kingfisher: 05bf8d04408aaa70fcd2c8c81b9f1d0f1ad313dd
15 | ParallexBanner: 7b39e5272f666fe9de0787b6c88dc888a66cc676
16 |
17 | PODFILE CHECKSUM: ac66f4d9cef0a35075dffaf388befd760a1173d5
18 |
19 | COCOAPODS: 1.0.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 | 2.4.2
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 | #endif
4 |
5 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "Kingfisher.h"
4 |
5 | FOUNDATION_EXPORT double KingfisherVersionNumber;
6 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[];
7 |
8 |
--------------------------------------------------------------------------------
/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 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
10 | SKIP_INSTALL = YES
11 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/ParallexBanner/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/ParallexBanner/ParallexBanner-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_ParallexBanner : NSObject
3 | @end
4 | @implementation PodsDummy_ParallexBanner
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/ParallexBanner/ParallexBanner-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #endif
4 |
5 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/ParallexBanner/ParallexBanner-umbrella.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 |
4 | FOUNDATION_EXPORT double ParallexBannerVersionNumber;
5 | FOUNDATION_EXPORT const unsigned char ParallexBannerVersionString[];
6 |
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/ParallexBanner/ParallexBanner.modulemap:
--------------------------------------------------------------------------------
1 | framework module ParallexBanner {
2 | umbrella header "ParallexBanner-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/ParallexBanner/ParallexBanner.xcconfig:
--------------------------------------------------------------------------------
1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/ParallexBanner
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
5 | OTHER_LDFLAGS = -framework "UIKit"
6 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
7 | PODS_BUILD_DIR = $BUILD_DIR
8 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
9 | PODS_ROOT = ${SRCROOT}
10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
11 | SKIP_INSTALL = YES
12 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_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-ParallexBanner_Example/Pods-ParallexBanner_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) 2015 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 | ## ParallexBanner
31 |
32 | Copyright (c) 2016 leo
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-ParallexBanner_Example/Pods-ParallexBanner_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) 2015 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 | Title
41 | Kingfisher
42 | Type
43 | PSGroupSpecifier
44 |
45 |
46 | FooterText
47 | Copyright (c) 2016 leo <leomobiledeveloper@gmail.com>
48 |
49 | Permission is hereby granted, free of charge, to any person obtaining a copy
50 | of this software and associated documentation files (the "Software"), to deal
51 | in the Software without restriction, including without limitation the rights
52 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
53 | copies of the Software, and to permit persons to whom the Software is
54 | furnished to do so, subject to the following conditions:
55 |
56 | The above copyright notice and this permission notice shall be included in
57 | all copies or substantial portions of the Software.
58 |
59 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
60 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
61 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
62 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
63 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
64 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
65 | THE SOFTWARE.
66 |
67 | Title
68 | ParallexBanner
69 | Type
70 | PSGroupSpecifier
71 |
72 |
73 | FooterText
74 | Generated by CocoaPods - https://cocoapods.org
75 | Title
76 |
77 | Type
78 | PSGroupSpecifier
79 |
80 |
81 | StringsTable
82 | Acknowledgements
83 | Title
84 | Acknowledgements
85 |
86 |
87 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_Example/Pods-ParallexBanner_Example-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_ParallexBanner_Example : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_ParallexBanner_Example
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_Example/Pods-ParallexBanner_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 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\""
63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1"
64 | fi
65 | }
66 |
67 | # Strip invalid architectures
68 | strip_invalid_archs() {
69 | binary="$1"
70 | # Get architectures for current file
71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
72 | stripped=""
73 | for arch in $archs; do
74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then
75 | # Strip non-valid architectures in-place
76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1
77 | stripped="$stripped $arch"
78 | fi
79 | done
80 | if [[ "$stripped" ]]; then
81 | echo "Stripped $binary of architectures:$stripped"
82 | fi
83 | }
84 |
85 |
86 | if [[ "$CONFIGURATION" == "Debug" ]]; then
87 | install_framework "$BUILT_PRODUCTS_DIR/Kingfisher/Kingfisher.framework"
88 | install_framework "$BUILT_PRODUCTS_DIR/ParallexBanner/ParallexBanner.framework"
89 | fi
90 | if [[ "$CONFIGURATION" == "Release" ]]; then
91 | install_framework "$BUILT_PRODUCTS_DIR/Kingfisher/Kingfisher.framework"
92 | install_framework "$BUILT_PRODUCTS_DIR/ParallexBanner/ParallexBanner.framework"
93 | fi
94 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_Example/Pods-ParallexBanner_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 | *)
22 | TARGET_DEVICE_ARGS="--target-device mac"
23 | ;;
24 | esac
25 |
26 | realpath() {
27 | DIRECTORY="$(cd "${1%/*}" && pwd)"
28 | FILENAME="${1##*/}"
29 | echo "$DIRECTORY/$FILENAME"
30 | }
31 |
32 | install_resource()
33 | {
34 | if [[ "$1" = /* ]] ; then
35 | RESOURCE_PATH="$1"
36 | else
37 | RESOURCE_PATH="${PODS_ROOT}/$1"
38 | fi
39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
40 | cat << EOM
41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
42 | EOM
43 | exit 1
44 | fi
45 | case $RESOURCE_PATH in
46 | *.storyboard)
47 | 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}"
48 | 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}
49 | ;;
50 | *.xib)
51 | 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}"
52 | 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}"
53 | ;;
54 | *.framework)
55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
59 | ;;
60 | *.xcdatamodel)
61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\""
62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
63 | ;;
64 | *.xcdatamodeld)
65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\""
66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
67 | ;;
68 | *.xcmappingmodel)
69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\""
70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
71 | ;;
72 | *.xcassets)
73 | ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH")
74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
75 | ;;
76 | *)
77 | echo "$RESOURCE_PATH"
78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
79 | ;;
80 | esac
81 | }
82 |
83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
88 | fi
89 | rm -f "$RESOURCES_TO_COPY"
90 |
91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
92 | then
93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
95 | while read line; do
96 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then
97 | XCASSET_FILES+=("$line")
98 | fi
99 | done <<<"$OTHER_XCASSETS"
100 |
101 | 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}"
102 | fi
103 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_Example/Pods-ParallexBanner_Example-umbrella.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 |
4 | FOUNDATION_EXPORT double Pods_ParallexBanner_ExampleVersionNumber;
5 | FOUNDATION_EXPORT const unsigned char Pods_ParallexBanner_ExampleVersionString[];
6 |
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_Example/Pods-ParallexBanner_Example.debug.xcconfig:
--------------------------------------------------------------------------------
1 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" "$PODS_CONFIGURATION_BUILD_DIR/ParallexBanner"
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/ParallexBanner/ParallexBanner.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "Kingfisher" -framework "ParallexBanner"
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-ParallexBanner_Example/Pods-ParallexBanner_Example.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_ParallexBanner_Example {
2 | umbrella header "Pods-ParallexBanner_Example-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_Example/Pods-ParallexBanner_Example.release.xcconfig:
--------------------------------------------------------------------------------
1 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" "$PODS_CONFIGURATION_BUILD_DIR/ParallexBanner"
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/ParallexBanner/ParallexBanner.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "Kingfisher" -framework "ParallexBanner"
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-ParallexBanner_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 | 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-ParallexBanner_Tests/Pods-ParallexBanner_Tests-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 | Generated by CocoaPods - https://cocoapods.org
4 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_Tests/Pods-ParallexBanner_Tests-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 | Generated by CocoaPods - https://cocoapods.org
18 | Title
19 |
20 | Type
21 | PSGroupSpecifier
22 |
23 |
24 | StringsTable
25 | Acknowledgements
26 | Title
27 | Acknowledgements
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_Tests/Pods-ParallexBanner_Tests-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_ParallexBanner_Tests : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_ParallexBanner_Tests
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_Tests/Pods-ParallexBanner_Tests-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 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\""
63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1"
64 | fi
65 | }
66 |
67 | # Strip invalid architectures
68 | strip_invalid_archs() {
69 | binary="$1"
70 | # Get architectures for current file
71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
72 | stripped=""
73 | for arch in $archs; do
74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then
75 | # Strip non-valid architectures in-place
76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1
77 | stripped="$stripped $arch"
78 | fi
79 | done
80 | if [[ "$stripped" ]]; then
81 | echo "Stripped $binary of architectures:$stripped"
82 | fi
83 | }
84 |
85 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_Tests/Pods-ParallexBanner_Tests-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 | *)
22 | TARGET_DEVICE_ARGS="--target-device mac"
23 | ;;
24 | esac
25 |
26 | realpath() {
27 | DIRECTORY="$(cd "${1%/*}" && pwd)"
28 | FILENAME="${1##*/}"
29 | echo "$DIRECTORY/$FILENAME"
30 | }
31 |
32 | install_resource()
33 | {
34 | if [[ "$1" = /* ]] ; then
35 | RESOURCE_PATH="$1"
36 | else
37 | RESOURCE_PATH="${PODS_ROOT}/$1"
38 | fi
39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
40 | cat << EOM
41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
42 | EOM
43 | exit 1
44 | fi
45 | case $RESOURCE_PATH in
46 | *.storyboard)
47 | 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}"
48 | 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}
49 | ;;
50 | *.xib)
51 | 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}"
52 | 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}"
53 | ;;
54 | *.framework)
55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
59 | ;;
60 | *.xcdatamodel)
61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\""
62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
63 | ;;
64 | *.xcdatamodeld)
65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\""
66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
67 | ;;
68 | *.xcmappingmodel)
69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\""
70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
71 | ;;
72 | *.xcassets)
73 | ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH")
74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
75 | ;;
76 | *)
77 | echo "$RESOURCE_PATH"
78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
79 | ;;
80 | esac
81 | }
82 |
83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
88 | fi
89 | rm -f "$RESOURCES_TO_COPY"
90 |
91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
92 | then
93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
95 | while read line; do
96 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then
97 | XCASSET_FILES+=("$line")
98 | fi
99 | done <<<"$OTHER_XCASSETS"
100 |
101 | 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}"
102 | fi
103 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_Tests/Pods-ParallexBanner_Tests-umbrella.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 |
4 | FOUNDATION_EXPORT double Pods_ParallexBanner_TestsVersionNumber;
5 | FOUNDATION_EXPORT const unsigned char Pods_ParallexBanner_TestsVersionString[];
6 |
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_Tests/Pods-ParallexBanner_Tests.debug.xcconfig:
--------------------------------------------------------------------------------
1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" "$PODS_CONFIGURATION_BUILD_DIR/ParallexBanner"
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
4 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher/Kingfisher.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/ParallexBanner/ParallexBanner.framework/Headers"
5 | PODS_BUILD_DIR = $BUILD_DIR
6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
7 | PODS_ROOT = ${SRCROOT}/Pods
8 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_Tests/Pods-ParallexBanner_Tests.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_ParallexBanner_Tests {
2 | umbrella header "Pods-ParallexBanner_Tests-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-ParallexBanner_Tests/Pods-ParallexBanner_Tests.release.xcconfig:
--------------------------------------------------------------------------------
1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" "$PODS_CONFIGURATION_BUILD_DIR/ParallexBanner"
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
4 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher/Kingfisher.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/ParallexBanner/ParallexBanner.framework/Headers"
5 | PODS_BUILD_DIR = $BUILD_DIR
6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
7 | PODS_ROOT = ${SRCROOT}/Pods
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016 leo
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 |
--------------------------------------------------------------------------------
/ParallexBanner.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint ParallexBanner.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 = 'ParallexBanner'
11 | s.version = '0.1.0'
12 | s.summary = 'A banner to show images with parallex effect'
13 | s.description = <<-DESC
14 | This is a banner to show images with parallex effect.It support both local and Web Image.
15 | DESC
16 |
17 | s.homepage = 'https://github.com/LeoMobileDeveloper/ParallexBanner'
18 | s.license = { :type => 'MIT', :file => 'LICENSE' }
19 | s.author = { 'leo' => 'leomobiledeveloper@gmail.com' }
20 | s.source = { :git => 'https://github.com/LeoMobileDeveloper/ParallexBanner.git', :tag => s.version.to_s }
21 | s.ios.deployment_target = '8.0'
22 | s.source_files = 'ParallexBanner/Classes/**/*'
23 | s.frameworks = 'UIKit'
24 | s.dependency 'Kingfisher'
25 | end
26 |
--------------------------------------------------------------------------------
/ParallexBanner/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeoMobileDeveloper/ParallexBanner/ae2e48c8c94165ab4157ab8c7f3329e266ffd7ac/ParallexBanner/Assets/.gitkeep
--------------------------------------------------------------------------------
/ParallexBanner/Classes/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeoMobileDeveloper/ParallexBanner/ae2e48c8c94165ab4157ab8c7f3329e266ffd7ac/ParallexBanner/Classes/.gitkeep
--------------------------------------------------------------------------------
/ParallexBanner/Classes/ParallexBanner.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ParallexBanner.swift
3 | // Pods
4 | //
5 | // Created by huangwenchen on 16/7/23.
6 | //
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import Kingfisher
12 |
13 | public enum ParallexBannerTransition{
14 | case Normal
15 | case Parallex
16 | //Maybe I will add some transition in the future
17 | }
18 | @objc public protocol ParallexBannerDelegate {
19 | optional func banner(banner:ParallexBanner,didClickAtIndex index:NSInteger)
20 | optional func banner(banner:ParallexBanner,didScrollToIndex index:NSInteger)
21 | }
22 |
23 | @objc public protocol ParallexBannerDataSource{
24 | func numberOfBannersIn(bannner:ParallexBanner)->NSInteger
25 | func banner(banner:ParallexBanner,urlOrImageAtIndex index:NSInteger)->AnyObject
26 | optional func banner(banner:ParallexBanner,placeHolderForIndex index:NSInteger)->UIImage?
27 | optional func banner(banner:ParallexBanner,contentModeAtIndex index:NSInteger)->UIViewContentMode
28 | }
29 |
30 | public class ParallexBanner: UIView {
31 | // MARK: - Propertys -
32 | public weak var dataSource:ParallexBannerDataSource?{
33 | didSet{
34 | reloadData()
35 | }
36 | }
37 | public weak var delegate:ParallexBannerDelegate?
38 | /// The transitionMode when scroll,default is .Parallex
39 | public var transitionMode:ParallexBannerTransition = ParallexBannerTransition.Parallex
40 | /// Weather to enable timer based scroll
41 | public var autoScroll:Bool = true {
42 | didSet{
43 | if autoScroll{
44 | restartTimerIfNeeded()
45 | }else{
46 | stopTimerIfNecessory()
47 | }
48 | }
49 | }
50 | /// Weather to enable scroll if there is only one page
51 | public var enableScrollForSinglePage = false
52 | /// The speed of parallex scroll,it should between 0.1 to 0.8
53 | public var parllexSpeed:CGFloat = 0.4
54 | /// The duration between an auto scroll fire
55 | public var autoScrollTimeInterval:NSTimeInterval = 3.0 {
56 | didSet{
57 | stopTimerIfNecessory()
58 | restartTimerIfNeeded()
59 | }
60 | }
61 | /// The page Control
62 | public let pageControl:UIPageControl = UIPageControl()
63 | /// The current index of page
64 | public var currentIndex:Int{
65 | get{
66 | if _currentIndex == 0 {
67 | return self.dataSource!.numberOfBannersIn(self) + 2 - 3
68 | }else if(_currentIndex == self.dataSource!.numberOfBannersIn(self) + 2 - 1){
69 | return 0;
70 | }else{
71 | return _currentIndex - 1
72 | }
73 | }
74 | };
75 |
76 | private var _currentIndex = 1
77 | private var collectionView:UICollectionView!
78 | private var timer:NSTimer?
79 | private var flowLayout:UICollectionViewFlowLayout!
80 |
81 | // MARK: - Init -
82 | override public init(frame: CGRect) {
83 | super.init(frame: frame)
84 | commonInit()
85 | }
86 | public required init?(coder aDecoder: NSCoder) {
87 | super.init(coder: aDecoder)
88 | commonInit()
89 | }
90 | func commonInit(){
91 | flowLayout = UICollectionViewFlowLayout()
92 | flowLayout.scrollDirection = .Horizontal
93 | flowLayout.minimumLineSpacing = 0.0
94 | collectionView = UICollectionView(frame:CGRectZero, collectionViewLayout: flowLayout)
95 | collectionView.dataSource = self
96 | collectionView.delegate = self
97 | collectionView.showsHorizontalScrollIndicator = false
98 | collectionView.showsVerticalScrollIndicator = false
99 | collectionView.pagingEnabled = true
100 | collectionView.backgroundColor = UIColor(white: 0.0, alpha: 0.1)
101 | pageControl.pageIndicatorTintColor = UIColor.whiteColor()
102 | pageControl.currentPageIndicatorTintColor = UIColor.darkGrayColor()
103 | pageControl.hidesForSinglePage = true
104 | pageControl.userInteractionEnabled = false
105 | addSubview(collectionView)
106 | addSubview(pageControl)
107 | collectionView.registerClass(BannerCell.self, forCellWithReuseIdentifier: "cell")
108 | }
109 | // MARK: - Layout -
110 | public override func layoutSubviews() {
111 | super.layoutSubviews()
112 | flowLayout.itemSize = self.frame.size
113 | if let ds = self.dataSource{
114 | pageControl.numberOfPages = ds.numberOfBannersIn(self)
115 | }else{
116 | pageControl.numberOfPages = 0
117 | }
118 | pageControl.sizeToFit()
119 | collectionView.frame = self.bounds
120 | pageControl.center = CGPointMake(CGRectGetWidth(self.bounds)/2, CGRectGetHeight(self.bounds) - CGRectGetHeight(pageControl.bounds)/2)
121 | let originIndexPath = NSIndexPath(forItem: _currentIndex, inSection: 0)
122 | if let ds = dataSource{
123 | let targetCount = ds.numberOfBannersIn(self);
124 | let needAdjust = targetCount > 1 || (targetCount == 1 && enableScrollForSinglePage)
125 | if needAdjust{
126 | collectionView.scrollToItemAtIndexPath(originIndexPath, atScrollPosition: .None, animated: false)
127 | }
128 | }
129 | }
130 | // MARK: - API -
131 | public func reloadData(){
132 | _currentIndex = 1;
133 | collectionView.reloadData()
134 | stopTimerIfNecessory()
135 | if let ds = self.dataSource{
136 | let targetCount = ds.numberOfBannersIn(self);
137 | pageControl.numberOfPages = targetCount
138 | pageControl.currentPage = 0
139 | restartTimerIfNeeded()
140 | }else{
141 | pageControl.numberOfPages = 0
142 | }
143 | setNeedsLayout()
144 | }
145 | // MARK: - Life Circle -
146 | public override func willMoveToSuperview(newSuperview: UIView?) {
147 | if newSuperview == nil{
148 | stopTimerIfNecessory()
149 | }
150 | super.willMoveToSuperview(newSuperview)
151 | }
152 | deinit{
153 | stopTimerIfNecessory()
154 | }
155 | // MARK: - Private -
156 | private func restartTimerIfNeeded(){
157 | stopTimerIfNecessory()
158 | if autoScroll == false {
159 | return
160 | }
161 | let count = self.dataSource!.numberOfBannersIn(self)
162 | guard count != 0 else{
163 | return
164 | }
165 | if count == 1 && enableScrollForSinglePage == false{
166 | return
167 | }
168 | self.timer = NSTimer(timeInterval: self.autoScrollTimeInterval, target: self, selector: #selector(ParallexBanner.scrollToNext), userInfo: nil, repeats: true)
169 | NSRunLoop.mainRunLoop().addTimer(self.timer!, forMode: NSRunLoopCommonModes)
170 | }
171 | private func stopTimerIfNecessory(){
172 | if self.timer != nil{
173 | self.timer?.invalidate()
174 | self.timer = nil
175 | }
176 | }
177 | @objc private func scrollToNext(){
178 | _currentIndex = (_currentIndex + 1) % (self.dataSource!.numberOfBannersIn(self) + 2);
179 | let nextIndx = NSIndexPath(forItem: _currentIndex, inSection: 0)
180 | collectionView.scrollToItemAtIndexPath(nextIndx, atScrollPosition: UICollectionViewScrollPosition.None, animated: true)
181 | }
182 | }
183 |
184 | //Handle dataSource/Delegate and scrollViewScroll
185 | extension ParallexBanner:UICollectionViewDataSource,UICollectionViewDelegate{
186 | public func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
187 | return 1
188 | }
189 | public func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
190 | guard self.dataSource != nil else{
191 | return 0
192 | }
193 | let count = self.dataSource!.numberOfBannersIn(self);
194 | guard count != 0 else{
195 | return 0
196 | }
197 | if count == 1 && enableScrollForSinglePage == false{
198 | return count;
199 | }
200 | return count + 2;
201 | }
202 | public func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
203 | assert(dataSource != nil)
204 | let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! BannerCell
205 | let index = indexPath.item;
206 | var adjustIndex = index
207 | if index == 0{
208 | adjustIndex = self.dataSource!.numberOfBannersIn(self) + 2 - 3
209 | }else if index == self.dataSource!.numberOfBannersIn(self) + 2 - 1 {
210 | adjustIndex = 0;
211 | }else{
212 | adjustIndex = index - 1
213 | }
214 | let imageOrURL = self.dataSource!.banner(self, urlOrImageAtIndex: adjustIndex)
215 | let placeHolder = self.dataSource!.banner?(self, placeHolderForIndex: adjustIndex)
216 | let contentMode = self.dataSource!.banner?(self, contentModeAtIndex: adjustIndex)
217 | if let mode = contentMode{
218 | cell.imageView.contentMode = mode
219 | }
220 | if let url = imageOrURL as? String{
221 | let url = NSURL(string: url)
222 | cell.imageView.kf_setImageWithURL(url, placeholderImage: placeHolder, optionsInfo: nil, progressBlock: nil, completionHandler: nil)
223 | }
224 | if let image = imageOrURL as? UIImage{
225 | cell.imageView.image = image
226 | }
227 | handleEffect(cell)
228 | return cell
229 | }
230 | public func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
231 | collectionView.deselectItemAtIndexPath(indexPath, animated: true)
232 | self.delegate?.banner?(self, didClickAtIndex: currentIndex)
233 | }
234 | public func scrollViewWillBeginDragging(scrollView: UIScrollView) {
235 | guard autoScroll else{
236 | return;
237 | }
238 | stopTimerIfNecessory()
239 | }
240 | public func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
241 | guard autoScroll else{
242 | return;
243 | }
244 | restartTimerIfNeeded()
245 | }
246 | public func scrollViewDidScroll(scrollView: UIScrollView) {
247 | var offSetX = scrollView.contentOffset.x
248 | let width = CGRectGetWidth(scrollView.bounds)
249 | guard width != 0 else{
250 | return
251 | }
252 | if offSetX >= width * CGFloat(self.dataSource!.numberOfBannersIn(self) + 2 - 1){
253 | offSetX = width;
254 | scrollView.contentOffset = CGPointMake(offSetX,0);
255 | }else if(offSetX < 0 ){
256 | offSetX = width * CGFloat(self.dataSource!.numberOfBannersIn(self) + 2 - 2);
257 | scrollView.contentOffset = CGPointMake(offSetX,0);
258 | }
259 | _currentIndex = Int((offSetX + width / 2.0) / width);
260 | if pageControl.currentPage != currentIndex{
261 | pageControl.currentPage = currentIndex
262 | self.delegate?.banner?(self, didScrollToIndex: currentIndex)
263 |
264 | }
265 | //Calculate offset for visiable cell
266 | collectionView.visibleCells().forEach { (cell) in
267 | if let bannerCell = cell as? BannerCell{
268 | handleEffect(bannerCell)
269 | }
270 | }
271 | }
272 | private func handleEffect(cell:BannerCell){
273 | switch transitionMode {
274 | case .Parallex:
275 | let minusX = self.collectionView.contentOffset.x - cell.frame.origin.x
276 | let imageOffsetX = -minusX * parllexSpeed;
277 | cell.scrollView.contentOffset = CGPointMake(imageOffsetX, 0)
278 | default:
279 | break
280 | }
281 | }
282 | }
283 |
284 | public class BannerCell:UICollectionViewCell{
285 | let imageView = UIImageView()
286 | let scrollView = UIScrollView()
287 | override init(frame: CGRect) {
288 | super.init(frame: frame)
289 | commonInit()
290 | }
291 | private func commonInit(){
292 | contentView.addSubview(scrollView)
293 | scrollView.scrollEnabled = false
294 | scrollView.userInteractionEnabled = false
295 | scrollView.addSubview(imageView)
296 | imageView.contentMode = UIViewContentMode.ScaleAspectFill;
297 | }
298 | required public init?(coder aDecoder: NSCoder) {
299 | fatalError("init(coder:) has not been implemented")
300 | }
301 | override public func layoutSubviews() {
302 | super.layoutSubviews()
303 | scrollView.contentSize = self.bounds.size;
304 | scrollView.frame = self.bounds
305 | imageView.frame = scrollView.bounds
306 | }
307 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ParallexBanner
2 |
3 | [](http://cocoapods.org/pods/ParallexBanner) [](https://developer.apple.com/iphone/index.action)
5 | [](https://developer.apple.com/swift)
7 | [](http://mit-license.org)
9 |
10 | ParallexBanner is a banner with parallex scroll effect written by pure Swift.
11 |
12 |
13 |
14 | ## Features
15 |
16 | - [x] Storyboard and pure code layout
17 | - [x] Auto scroll
18 | - [x] Infinite scroll
19 | - [x] WebImage and local image (It use Kingfisher to load web image)
20 | - [x] Placeholder image
21 |
22 | ## Requirements
23 |
24 | - iOS 8
25 | - ARC
26 |
27 | ## Install
28 |
29 | Using CocoaPod
30 |
31 | ```
32 | pod "ParallexBanner"
33 | ```
34 |
35 | ## Getting start
36 |
37 | ParallexBanner use dataSource and delegate as main interface
38 |
39 | Data Source
40 |
41 | ```
42 | //Number of banners
43 | func numberOfBannersIn(_ bannner:ParallexBanner)->NSInteger
44 | //You can return a ImageURL(String) or a UIImage object here
45 | func banner(_ banner:ParallexBanner,urlOrImageAtIndex index:NSInteger)->AnyObject
46 | //Return the place holder image here
47 | optional func banner(_ banner:ParallexBanner,placeHolderForIndex index:NSInteger)->UIImage?
48 | //Return the image contentMode here
49 | optional func banner(_ banner:ParallexBanner,contentModeAtIndex index:NSInteger)->UIViewContentMode
50 |
51 | ```
52 |
53 | Delegate
54 |
55 | ```
56 | //Click at index
57 | optional func banner(_ banner:ParallexBanner,didClickAtIndex index:NSInteger)
58 | //Scroll to index
59 | optional func banner(_ banner:ParallexBanner,didScrollToIndex index:NSInteger)
60 | ```
61 |
62 | !!! Do not forget to call `banner.reloadData()` when model changed.
63 |
64 | ##Propertys
65 |
66 | - `autoScroll` enable timer based scroll
67 | - `autoScrollTimeInterval` scroll interval
68 | - `enableScrollForSinglePage` enable scroll if there is only single page
69 | - `parllexSpeed` the speed of parallex scroll.Better to between 0.1 and 0.8
70 | - `currentIndex` current page index.Readonly
71 | - `pageControl` the pageControl object
72 | - `transitionMode` set it to .Normal if you do not want parallex scroll.
73 |
74 |
75 | ## Author
76 |
77 | leo, leomobiledeveloper@gmail.com
78 |
79 | ## License
80 |
81 | ParallexBanner is available under the MIT license. See the LICENSE file for more info.
82 |
--------------------------------------------------------------------------------
/ScreenShots/gif.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeoMobileDeveloper/ParallexBanner/ae2e48c8c94165ab4157ab8c7f3329e266ffd7ac/ScreenShots/gif.gif
--------------------------------------------------------------------------------
/_Pods.xcodeproj:
--------------------------------------------------------------------------------
1 | Example/Pods/Pods.xcodeproj
--------------------------------------------------------------------------------