added iOS source code
[wl-app.git] / iOS / Pods / Kingfisher / Sources / UIButton+Kingfisher.swift
1 //
2 //  UIButton+Kingfisher.swift
3 //  Kingfisher
4 //
5 //  Created by Wei Wang on 15/4/13.
6 //
7 //  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
8 //
9 //  Permission is hereby granted, free of charge, to any person obtaining a copy
10 //  of this software and associated documentation files (the "Software"), to deal
11 //  in the Software without restriction, including without limitation the rights
12 //  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 //  copies of the Software, and to permit persons to whom the Software is
14 //  furnished to do so, subject to the following conditions:
15 //
16 //  The above copyright notice and this permission notice shall be included in
17 //  all copies or substantial portions of the Software.
18 //
19 //  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 //  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 //  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 //  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 //  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 //  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 //  THE SOFTWARE.
26
27 import UIKit
28
29 // MARK: - Set Images
30 /**
31  *      Set image to use in button from web for a specified state.
32  */
33 extension Kingfisher where Base: UIButton {
34     /**
35      Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and
36      completion handler.
37      
38      - parameter resource:          Resource object contains information such as `cacheKey` and `downloadURL`.
39      - parameter state:             The state that uses the specified image.
40      - parameter placeholder:       A placeholder image when retrieving the image at URL.
41      - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
42      - parameter progressBlock:     Called when the image downloading progress gets updated.
43      - parameter completionHandler: Called when the image retrieved and set.
44      
45      - returns: A task represents the retrieving process.
46      
47      - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
48      The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
49      
50      If `resource` is `nil`, the `placeholder` image will be set and
51      `completionHandler` will be called with both `error` and `image` being `nil`.
52      */
53     @discardableResult
54     public func setImage(with resource: Resource?,
55                          for state: UIControlState,
56                          placeholder: UIImage? = nil,
57                          options: KingfisherOptionsInfo? = nil,
58                          progressBlock: DownloadProgressBlock? = nil,
59                          completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
60     {
61         guard let resource = resource else {
62             base.setImage(placeholder, for: state)
63             setWebURL(nil, for: state)
64             completionHandler?(nil, nil, .none, nil)
65             return .empty
66         }
67         
68         let options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
69         if !options.keepCurrentImageWhileLoading {
70             base.setImage(placeholder, for: state)
71         }
72         
73         setWebURL(resource.downloadURL, for: state)
74         let task = KingfisherManager.shared.retrieveImage(
75             with: resource,
76             options: options,
77             progressBlock: { receivedSize, totalSize in
78                 guard resource.downloadURL == self.webURL(for: state) else {
79                     return
80                 }
81                 if let progressBlock = progressBlock {
82                     progressBlock(receivedSize, totalSize)
83                 }
84             },
85             completionHandler: {[weak base] image, error, cacheType, imageURL in
86                 DispatchQueue.main.safeAsync {
87                     guard let strongBase = base, imageURL == self.webURL(for: state) else {
88                         completionHandler?(image, error, cacheType, imageURL)
89                         return
90                     }
91                     self.setImageTask(nil)
92                     if image != nil {
93                         strongBase.setImage(image, for: state)
94                     }
95
96                     completionHandler?(image, error, cacheType, imageURL)
97                 }
98             })
99         
100         setImageTask(task)
101         return task
102     }
103     
104     /**
105      Cancel the image download task bounded to the image view if it is running.
106      Nothing will happen if the downloading has already finished.
107      */
108     public func cancelImageDownloadTask() {
109         imageTask?.cancel()
110     }
111     
112     /**
113      Set the background image to use for a specified state with a resource,
114      a placeholder image, options progress handler and completion handler.
115      
116      - parameter resource:          Resource object contains information such as `cacheKey` and `downloadURL`.
117      - parameter state:             The state that uses the specified image.
118      - parameter placeholder:       A placeholder image when retrieving the image at URL.
119      - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
120      - parameter progressBlock:     Called when the image downloading progress gets updated.
121      - parameter completionHandler: Called when the image retrieved and set.
122      
123      - returns: A task represents the retrieving process.
124      
125      - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
126      The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
127      
128      If `resource` is `nil`, the `placeholder` image will be set and
129      `completionHandler` will be called with both `error` and `image` being `nil`.
130      */
131     @discardableResult
132     public func setBackgroundImage(with resource: Resource?,
133                                    for state: UIControlState,
134                                    placeholder: UIImage? = nil,
135                                    options: KingfisherOptionsInfo? = nil,
136                                    progressBlock: DownloadProgressBlock? = nil,
137                                    completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
138     {
139         guard let resource = resource else {
140             base.setBackgroundImage(placeholder, for: state)
141             setBackgroundWebURL(nil, for: state)
142             completionHandler?(nil, nil, .none, nil)
143             return .empty
144         }
145         
146         let options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
147         if !options.keepCurrentImageWhileLoading {
148             base.setBackgroundImage(placeholder, for: state)
149         }
150         
151         setBackgroundWebURL(resource.downloadURL, for: state)
152         let task = KingfisherManager.shared.retrieveImage(
153             with: resource,
154             options: options,
155             progressBlock: { receivedSize, totalSize in
156                 guard resource.downloadURL == self.backgroundWebURL(for: state) else {
157                     return
158                 }
159                 if let progressBlock = progressBlock {
160                     progressBlock(receivedSize, totalSize)
161                 }
162             },
163             completionHandler: { [weak base] image, error, cacheType, imageURL in
164                 DispatchQueue.main.safeAsync {
165                     guard let strongBase = base, imageURL == self.backgroundWebURL(for: state) else {
166                         completionHandler?(image, error, cacheType, imageURL)
167                         return
168                     }
169                     self.setBackgroundImageTask(nil)
170                     if image != nil {
171                         strongBase.setBackgroundImage(image, for: state)
172                     }
173                     completionHandler?(image, error, cacheType, imageURL)
174                 }
175             })
176         
177         setBackgroundImageTask(task)
178         return task
179     }
180     
181     /**
182      Cancel the background image download task bounded to the image view if it is running.
183      Nothing will happen if the downloading has already finished.
184      */
185     public func cancelBackgroundImageDownloadTask() {
186         backgroundImageTask?.cancel()
187     }
188
189 }
190
191 // MARK: - Associated Object
192 private var lastURLKey: Void?
193 private var imageTaskKey: Void?
194
195 extension Kingfisher where Base: UIButton {
196     /**
197      Get the image URL binded to this button for a specified state.
198      
199      - parameter state: The state that uses the specified image.
200      
201      - returns: Current URL for image.
202      */
203     public func webURL(for state: UIControlState) -> URL? {
204         return webURLs[NSNumber(value:state.rawValue)] as? URL
205     }
206     
207     fileprivate func setWebURL(_ url: URL?, for state: UIControlState) {
208         webURLs[NSNumber(value:state.rawValue)] = url
209     }
210     
211     fileprivate var webURLs: NSMutableDictionary {
212         var dictionary = objc_getAssociatedObject(base, &lastURLKey) as? NSMutableDictionary
213         if dictionary == nil {
214             dictionary = NSMutableDictionary()
215             setWebURLs(dictionary!)
216         }
217         return dictionary!
218     }
219     
220     fileprivate func setWebURLs(_ URLs: NSMutableDictionary) {
221         objc_setAssociatedObject(base, &lastURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
222     }
223     
224     fileprivate var imageTask: RetrieveImageTask? {
225         return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask
226     }
227     
228     fileprivate func setImageTask(_ task: RetrieveImageTask?) {
229         objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
230     }
231 }
232
233
234 private var lastBackgroundURLKey: Void?
235 private var backgroundImageTaskKey: Void?
236
237
238 extension Kingfisher where Base: UIButton {
239     /**
240      Get the background image URL binded to this button for a specified state.
241      
242      - parameter state: The state that uses the specified background image.
243      
244      - returns: Current URL for background image.
245      */
246     public func backgroundWebURL(for state: UIControlState) -> URL? {
247         return backgroundWebURLs[NSNumber(value:state.rawValue)] as? URL
248     }
249     
250     fileprivate func setBackgroundWebURL(_ url: URL?, for state: UIControlState) {
251         backgroundWebURLs[NSNumber(value:state.rawValue)] = url
252     }
253     
254     fileprivate var backgroundWebURLs: NSMutableDictionary {
255         var dictionary = objc_getAssociatedObject(base, &lastBackgroundURLKey) as? NSMutableDictionary
256         if dictionary == nil {
257             dictionary = NSMutableDictionary()
258             setBackgroundWebURLs(dictionary!)
259         }
260         return dictionary!
261     }
262     
263     fileprivate func setBackgroundWebURLs(_ URLs: NSMutableDictionary) {
264         objc_setAssociatedObject(base, &lastBackgroundURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
265     }
266     
267     fileprivate var backgroundImageTask: RetrieveImageTask? {
268         return objc_getAssociatedObject(base, &backgroundImageTaskKey) as? RetrieveImageTask
269     }
270     
271     fileprivate func setBackgroundImageTask(_ task: RetrieveImageTask?) {
272         objc_setAssociatedObject(base, &backgroundImageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
273     }
274 }