2 // UIButton+Kingfisher.swift
5 // Created by Wei Wang on 15/4/13.
7 // Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
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:
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
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
31 * Set image to use in button from web for a specified state.
33 extension Kingfisher where Base: UIButton {
35 Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and
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.
45 - returns: A task represents the retrieving process.
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.
50 If `resource` is `nil`, the `placeholder` image will be set and
51 `completionHandler` will be called with both `error` and `image` being `nil`.
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
61 guard let resource = resource else {
62 base.setImage(placeholder, for: state)
63 setWebURL(nil, for: state)
64 completionHandler?(nil, nil, .none, nil)
68 let options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
69 if !options.keepCurrentImageWhileLoading {
70 base.setImage(placeholder, for: state)
73 setWebURL(resource.downloadURL, for: state)
74 let task = KingfisherManager.shared.retrieveImage(
77 progressBlock: { receivedSize, totalSize in
78 guard resource.downloadURL == self.webURL(for: state) else {
81 if let progressBlock = progressBlock {
82 progressBlock(receivedSize, totalSize)
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)
91 self.setImageTask(nil)
93 strongBase.setImage(image, for: state)
96 completionHandler?(image, error, cacheType, imageURL)
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.
108 public func cancelImageDownloadTask() {
113 Set the background image to use for a specified state with a resource,
114 a placeholder image, options progress handler and completion handler.
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.
123 - returns: A task represents the retrieving process.
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.
128 If `resource` is `nil`, the `placeholder` image will be set and
129 `completionHandler` will be called with both `error` and `image` being `nil`.
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
139 guard let resource = resource else {
140 base.setBackgroundImage(placeholder, for: state)
141 setBackgroundWebURL(nil, for: state)
142 completionHandler?(nil, nil, .none, nil)
146 let options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
147 if !options.keepCurrentImageWhileLoading {
148 base.setBackgroundImage(placeholder, for: state)
151 setBackgroundWebURL(resource.downloadURL, for: state)
152 let task = KingfisherManager.shared.retrieveImage(
155 progressBlock: { receivedSize, totalSize in
156 guard resource.downloadURL == self.backgroundWebURL(for: state) else {
159 if let progressBlock = progressBlock {
160 progressBlock(receivedSize, totalSize)
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)
169 self.setBackgroundImageTask(nil)
171 strongBase.setBackgroundImage(image, for: state)
173 completionHandler?(image, error, cacheType, imageURL)
177 setBackgroundImageTask(task)
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.
185 public func cancelBackgroundImageDownloadTask() {
186 backgroundImageTask?.cancel()
191 // MARK: - Associated Object
192 private var lastURLKey: Void?
193 private var imageTaskKey: Void?
195 extension Kingfisher where Base: UIButton {
197 Get the image URL binded to this button for a specified state.
199 - parameter state: The state that uses the specified image.
201 - returns: Current URL for image.
203 public func webURL(for state: UIControlState) -> URL? {
204 return webURLs[NSNumber(value:state.rawValue)] as? URL
207 fileprivate func setWebURL(_ url: URL?, for state: UIControlState) {
208 webURLs[NSNumber(value:state.rawValue)] = url
211 fileprivate var webURLs: NSMutableDictionary {
212 var dictionary = objc_getAssociatedObject(base, &lastURLKey) as? NSMutableDictionary
213 if dictionary == nil {
214 dictionary = NSMutableDictionary()
215 setWebURLs(dictionary!)
220 fileprivate func setWebURLs(_ URLs: NSMutableDictionary) {
221 objc_setAssociatedObject(base, &lastURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
224 fileprivate var imageTask: RetrieveImageTask? {
225 return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask
228 fileprivate func setImageTask(_ task: RetrieveImageTask?) {
229 objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
234 private var lastBackgroundURLKey: Void?
235 private var backgroundImageTaskKey: Void?
238 extension Kingfisher where Base: UIButton {
240 Get the background image URL binded to this button for a specified state.
242 - parameter state: The state that uses the specified background image.
244 - returns: Current URL for background image.
246 public func backgroundWebURL(for state: UIControlState) -> URL? {
247 return backgroundWebURLs[NSNumber(value:state.rawValue)] as? URL
250 fileprivate func setBackgroundWebURL(_ url: URL?, for state: UIControlState) {
251 backgroundWebURLs[NSNumber(value:state.rawValue)] = url
254 fileprivate var backgroundWebURLs: NSMutableDictionary {
255 var dictionary = objc_getAssociatedObject(base, &lastBackgroundURLKey) as? NSMutableDictionary
256 if dictionary == nil {
257 dictionary = NSMutableDictionary()
258 setBackgroundWebURLs(dictionary!)
263 fileprivate func setBackgroundWebURLs(_ URLs: NSMutableDictionary) {
264 objc_setAssociatedObject(base, &lastBackgroundURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
267 fileprivate var backgroundImageTask: RetrieveImageTask? {
268 return objc_getAssociatedObject(base, &backgroundImageTaskKey) as? RetrieveImageTask
271 fileprivate func setBackgroundImageTask(_ task: RetrieveImageTask?) {
272 objc_setAssociatedObject(base, &backgroundImageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)