diff --git a/Kingfisher-Demo/ViewController.swift b/Kingfisher-Demo/ViewController.swift index e591070b7..8c25bf7fd 100644 --- a/Kingfisher-Demo/ViewController.swift +++ b/Kingfisher-Demo/ViewController.swift @@ -58,7 +58,9 @@ extension ViewController: UICollectionViewDataSource { override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCellWithReuseIdentifier("collectionViewCell", forIndexPath: indexPath) as! CollectionViewCell cell.cellImageView.kf_showIndicatorWhenLoading = true - cell.cellImageView.kf_setImageWithURL(NSURL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/kingfisher-\(indexPath.row + 1).jpg")!, placeholderImage: nil, optionsInfo: nil, progressBlock: { (receivedSize, totalSize) -> () in + + let URL = NSURL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/kingfisher-\(indexPath.row + 1).jpg")! + cell.cellImageView.kf_setImageWithResource(Resource(downloadURL: URL), placeholderImage: nil, optionsInfo: nil, progressBlock: { (receivedSize, totalSize) -> () in println("\(indexPath.row + 1): \(receivedSize)/\(totalSize)") }) { (image, error, cacheType, imageURL) -> () in println("\(indexPath.row + 1): Finished") diff --git a/Kingfisher.xcodeproj/project.pbxproj b/Kingfisher.xcodeproj/project.pbxproj index 8b1f09871..1eaed4bd3 100644 --- a/Kingfisher.xcodeproj/project.pbxproj +++ b/Kingfisher.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 0D9C68098E20AB4F19D7C313 /* libPods-KingfisherTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A9E621E297FEFAD35D39C34E /* libPods-KingfisherTests.a */; }; + 4B2C4DF81B7D7ACD000912CA /* Resource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2C4DF71B7D7ACD000912CA /* Resource.swift */; }; 4B3E714F1B02005900F5AAED /* WatchKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B3E714D1B01FEB200F5AAED /* WatchKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 4B412CA51AE8A2F9008D530A /* KingfisherOptionsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B412CA41AE8A2F9008D530A /* KingfisherOptionsInfo.swift */; }; 4B6D4F651AE0A46D0084D15B /* UIImageViewExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6D4F641AE0A46D0084D15B /* UIImageViewExtensionTests.swift */; }; @@ -107,6 +108,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 4B2C4DF71B7D7ACD000912CA /* Resource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Resource.swift; sourceTree = ""; }; 4B3E714D1B01FEB200F5AAED /* WatchKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WatchKit.framework; path = System/Library/Frameworks/WatchKit.framework; sourceTree = SDKROOT; }; 4B412CA41AE8A2F9008D530A /* KingfisherOptionsInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KingfisherOptionsInfo.swift; sourceTree = ""; }; 4B6D4F641AE0A46D0084D15B /* UIImageViewExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageViewExtensionTests.swift; sourceTree = ""; }; @@ -295,6 +297,7 @@ D1ED2D591AD2D0F900CFC3EB /* KingfisherManager.swift */, D1ED2D5A1AD2D0F900CFC3EB /* KingfisherOptions.swift */, 4B412CA41AE8A2F9008D530A /* KingfisherOptionsInfo.swift */, + 4B2C4DF71B7D7ACD000912CA /* Resource.swift */, D1ED2D371AD2D09F00CFC3EB /* Supporting Files */, ); path = Kingfisher; @@ -605,6 +608,7 @@ 4BD352F31AF36A0700B18A40 /* WKInterfaceImage+Kingfisher.swift in Sources */, D1ED2D5E1AD2D0F900CFC3EB /* ImageDownloader.swift in Sources */, 4B412CA51AE8A2F9008D530A /* KingfisherOptionsInfo.swift in Sources */, + 4B2C4DF81B7D7ACD000912CA /* Resource.swift in Sources */, D1ED2D601AD2D0F900CFC3EB /* KingfisherOptions.swift in Sources */, D151E72B1AD3C48D004FD4AE /* UIImage+Decode.swift in Sources */, ); diff --git a/Kingfisher/KingfisherManager.swift b/Kingfisher/KingfisherManager.swift index 961353de3..9ca89d325 100644 --- a/Kingfisher/KingfisherManager.swift +++ b/Kingfisher/KingfisherManager.swift @@ -95,24 +95,24 @@ public class KingfisherManager { cache = ImageCache.defaultCache downloader = ImageDownloader.defaultDownloader } - + /** - Get an image with URL as the key. + Get an image with resource. If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first. - If not found, it will download the image at URL and cache it. + If not found, it will download the image at `resource.downloadURL` and cache it with `resource.cacheKey`. These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more. - :param: URL The image URL. + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. :param: progressBlock Called every time downloaded data changed. This could be used as a progress UI. :param: completionHandler Called when the whole retriving process finished. :returns: A `RetrieveImageTask` task object. You can use this object to cancel the task. */ - public func retrieveImageWithURL(URL: NSURL, - optionsInfo: KingfisherOptionsInfo?, - progressBlock: DownloadProgressBlock?, - completionHandler: CompletionHandler?) -> RetrieveImageTask + public func retrieveImageWithResource(resource: Resource, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask { func parseOptionsInfo(optionsInfo: KingfisherOptionsInfo?) -> (Options, ImageCache, ImageDownloader) { let options: Options @@ -142,42 +142,64 @@ public class KingfisherManager { let parsedOptions = parseOptionsInfo(optionsInfo) let (options, targetCache, downloader) = (parsedOptions.0, parsedOptions.1, parsedOptions.2) - if let key = URL.absoluteString { - if options.forceRefresh { - downloadAndCacheImageWithURL(URL, - forKey: key, - retrieveImageTask: task, - progressBlock: progressBlock, - completionHandler: completionHandler, - options: options, - targetCache: targetCache, - downloader: downloader) - } else { - let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> () in - // Break retain cycle created inside diskTask closure below - task.diskRetrieveTask = nil - completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL) - } - let diskTask = targetCache.retrieveImageForKey(key, options: options, completionHandler: { (image, cacheType) -> () in - if image != nil { - diskTaskCompletionHandler(image: image, error: nil, cacheType:cacheType, imageURL: URL) - } else { - self.downloadAndCacheImageWithURL(URL, - forKey: key, - retrieveImageTask: task, - progressBlock: progressBlock, - completionHandler: diskTaskCompletionHandler, - options: options, - targetCache: targetCache, - downloader: downloader) - } - }) - task.diskRetrieveTask = diskTask + if options.forceRefresh { + downloadAndCacheImageWithURL(resource.downloadURL, + forKey: resource.cacheKey, + retrieveImageTask: task, + progressBlock: progressBlock, + completionHandler: completionHandler, + options: options, + targetCache: targetCache, + downloader: downloader) + } else { + let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> () in + // Break retain cycle created inside diskTask closure below + task.diskRetrieveTask = nil + completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL) } + let diskTask = targetCache.retrieveImageForKey(resource.cacheKey, options: options, completionHandler: { (image, cacheType) -> () in + if image != nil { + diskTaskCompletionHandler(image: image, error: nil, cacheType:cacheType, imageURL: resource.downloadURL) + } else { + self.downloadAndCacheImageWithURL(resource.downloadURL, + forKey: resource.cacheKey, + retrieveImageTask: task, + progressBlock: progressBlock, + completionHandler: diskTaskCompletionHandler, + options: options, + targetCache: targetCache, + downloader: downloader) + } + }) + task.diskRetrieveTask = diskTask } return task } + + /** + Get an image with `URL.absoluteString` as the key. + If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first. + If not found, it will download the image at URL and cache it with `URL.absoluteString` value as its key. + + 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. + + These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more. + + :param: URL The image URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + :param: progressBlock Called every time downloaded data changed. This could be used as a progress UI. + :param: completionHandler Called when the whole retriving process finished. + + :returns: A `RetrieveImageTask` task object. You can use this object to cancel the task. + */ + public func retrieveImageWithURL(URL: NSURL, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + return retrieveImageWithResource(Resource(downloadURL: URL), optionsInfo: optionsInfo, progressBlock: progressBlock, completionHandler: completionHandler) + } func downloadAndCacheImageWithURL(URL: NSURL, forKey key: String, diff --git a/Kingfisher/Resource.swift b/Kingfisher/Resource.swift new file mode 100644 index 000000000..f39b8e09c --- /dev/null +++ b/Kingfisher/Resource.swift @@ -0,0 +1,37 @@ +// +// Resource.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/6. +// +// Copyright (c) 2015 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +public struct Resource { + public let cacheKey: String + public let downloadURL: NSURL + + public init(downloadURL: NSURL, cacheKey: String? = nil) { + self.downloadURL = downloadURL + self.cacheKey = cacheKey ?? downloadURL.absoluteString! + } +} \ No newline at end of file diff --git a/Kingfisher/UIButton+Kingfisher.swift b/Kingfisher/UIButton+Kingfisher.swift index 057279551..27ca5ec5b 100644 --- a/Kingfisher/UIButton+Kingfisher.swift +++ b/Kingfisher/UIButton+Kingfisher.swift @@ -30,10 +30,29 @@ import Foundation * Set image to use from web for a specified state. */ public extension UIButton { + + /** + Set an image to use for a specified state with a resource. + It will ask for Kingfisher's manager to get the image for the `cacheKey` property in `resource` and then set it for a button state. + The memory and disk will be searched first. If the manager does not find it, it will try to download the image at the `resource.downloadURL` and store it with `resource.cacheKey` for next use. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource, + forState state: UIControlState) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, forState: state, placeholderImage: nil, optionsInfo: nil, progressBlock: nil, completionHandler: nil) + } + /** Set an image to use for a specified state with a URL. It will ask for Kingfisher's manager to get the image for the URL and then set it for a button state. - The memory and disk will be searched first. If the manager does not find it, it will try to download the image at this URL and store it for next use. + The memory and disk will be searched first with `URL.absoluteString` as the cache key. If the manager does not find it, it will try to download the image at this URL and store the image with `URL.absoluteString` as cache key for next use. + + If you need to specify the key other than `URL.absoluteString`, please use resource version of these APIs with `resource.cacheKey` set to what you want. :param: URL The URL of image for specified state. :param: state The state that uses the specified image. @@ -45,6 +64,22 @@ public extension UIButton { { return kf_setImageWithURL(URL, forState: state, placeholderImage: nil, optionsInfo: nil, progressBlock: nil, completionHandler: nil) } + + /** + Set an image to use for a specified state with a resource and a placeholder image. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, forState: state, placeholderImage: placeholderImage, optionsInfo: nil, progressBlock: nil, completionHandler: nil) + } /** Set an image to use for a specified state with a URL and a placeholder image. @@ -61,6 +96,24 @@ public extension UIButton { { return kf_setImageWithURL(URL, forState: state, placeholderImage: placeholderImage, optionsInfo: nil, progressBlock: nil, completionHandler: nil) } + + /** + Set an image to use for a specified state with a resource, a placeholder image and options. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: nil) + } /** Set an image to use for a specified state with a URL, a placeholder image and options. @@ -79,6 +132,26 @@ public extension UIButton { { return kf_setImageWithURL(URL, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: nil) } + + /** + Set an image to use for a specified state with a resource, a placeholder image, options and completion handler. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + :param: completionHandler Called when the image retrieved and set. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: completionHandler) + } /** Set an image to use for a specified state with a URL, a placeholder image, options and completion handler. @@ -99,11 +172,12 @@ public extension UIButton { { return kf_setImageWithURL(URL, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: completionHandler) } + /** - Set an image to use for a specified state with a URL, a placeholder image, options, progress handler and completion handler. + Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and completion handler. - :param: URL The URL of image for specified state. + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. :param: state The state that uses the specified image. :param: placeholderImage A placeholder image when retrieving the image at URL. :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. @@ -112,32 +186,59 @@ public extension UIButton { :returns: A task represents the retriving process. */ - public func kf_setImageWithURL(URL: NSURL, - forState state: UIControlState, - placeholderImage: UIImage?, - optionsInfo: KingfisherOptionsInfo?, - progressBlock: DownloadProgressBlock?, - completionHandler: CompletionHandler?) -> RetrieveImageTask + public func kf_setImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask { setImage(placeholderImage, forState: state) - kf_setWebURL(URL, forState: state) - let task = KingfisherManager.sharedManager.retrieveImageWithURL(URL, optionsInfo: optionsInfo, progressBlock: { (receivedSize, totalSize) -> () in + kf_setWebURL(resource.downloadURL, forState: state) + let task = KingfisherManager.sharedManager.retrieveImageWithResource(resource, optionsInfo: optionsInfo, progressBlock: { (receivedSize, totalSize) -> () in if let progressBlock = progressBlock { dispatch_async(dispatch_get_main_queue(), { () -> Void in progressBlock(receivedSize: receivedSize, totalSize: totalSize) }) } - }) { (image, error, cacheType, imageURL) -> () in - dispatch_async(dispatch_get_main_queue(), { () -> Void in - if (imageURL == self.kf_webURLForState(state) && image != nil) { - self.setImage(image, forState: state) - } - completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL) - }) + }) { (image, error, cacheType, imageURL) -> () in + dispatch_async(dispatch_get_main_queue(), { () -> Void in + if (imageURL == self.kf_webURLForState(state) && image != nil) { + self.setImage(image, forState: state) + } + completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL) + }) } return task } + + /** + Set an image to use for a specified state with a URL, a placeholder image, options, progress handler and completion handler. + + :param: URL The URL of image for specified state. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + :param: progressBlock Called when the image downloading progress gets updated. + :param: completionHandler Called when the image retrieved and set. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithURL(URL: NSURL, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + return kf_setImageWithResource(Resource(downloadURL: URL), + forState: state, + placeholderImage: placeholderImage, + optionsInfo: optionsInfo, + progressBlock: progressBlock, + completionHandler: completionHandler) + } } private var lastURLKey: Void? @@ -177,10 +278,28 @@ public extension UIButton { * Set background image to use from web for a specified state. */ public extension UIButton { + /** + Set the background image to use for a specified state with a resource. + It will ask for Kingfisher's manager to get the image for the `cacheKey` property in `resource` and then set it for a button state. + The memory and disk will be searched first. If the manager does not find it, it will try to download the image at the `resource.downloadURL` and store it with `resource.cacheKey` for next use. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + + :returns: A task represents the retriving process. + */ + public func kf_setBackgroundImageWithResource(resource: Resource, + forState state: UIControlState) -> RetrieveImageTask + { + return kf_setBackgroundImageWithResource(resource, forState: state, placeholderImage: nil, optionsInfo: nil, progressBlock: nil, completionHandler: nil) + } + /** Set the background image to use for a specified state with a URL. It will ask for Kingfisher's manager to get the image for the URL and then set it for a button state. - The memory and disk will be searched first. If the manager does not find it, it will try to download the image at this URL and store it for next use. + The memory and disk will be searched first with `URL.absoluteString` as the cache key. If the manager does not find it, it will try to download the image at this URL and store the image with `URL.absoluteString` as cache key for next use. + + If you need to specify the key other than `URL.absoluteString`, please use resource version of these APIs with `resource.cacheKey` set to what you want. :param: URL The URL of image for specified state. :param: state The state that uses the specified image. @@ -192,6 +311,22 @@ public extension UIButton { { return kf_setBackgroundImageWithURL(URL, forState: state, placeholderImage: nil, optionsInfo: nil, progressBlock: nil, completionHandler: nil) } + + /** + Set the background image to use for a specified state with a resource and a placeholder image. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + + :returns: A task represents the retriving process. + */ + public func kf_setBackgroundImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?) -> RetrieveImageTask + { + return kf_setBackgroundImageWithResource(resource, forState: state, placeholderImage: placeholderImage, optionsInfo: nil, progressBlock: nil, completionHandler: nil) + } /** Set the background image to use for a specified state with a URL and a placeholder image. @@ -208,6 +343,24 @@ public extension UIButton { { return kf_setBackgroundImageWithURL(URL, forState: state, placeholderImage: placeholderImage, optionsInfo: nil, progressBlock: nil, completionHandler: nil) } + + /** + Set the background image to use for a specified state with a resource, a placeholder image and options. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + + :returns: A task represents the retriving process. + */ + public func kf_setBackgroundImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?) -> RetrieveImageTask + { + return kf_setBackgroundImageWithResource(resource, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: nil) + } /** Set the background image to use for a specified state with a URL, a placeholder image and options. @@ -226,6 +379,26 @@ public extension UIButton { { return kf_setBackgroundImageWithURL(URL, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: nil) } + + /** + Set the background image to use for a specified state with a resource, a placeholder image, options and completion handler. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + :param: completionHandler Called when the image retrieved and set. + + :returns: A task represents the retriving process. + */ + public func kf_setBackgroundImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + return kf_setBackgroundImageWithResource(resource, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: completionHandler) + } /** Set the background image to use for a specified state with a URL, a placeholder image, options and completion handler. @@ -246,12 +419,12 @@ public extension UIButton { { return kf_setBackgroundImageWithURL(URL, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: completionHandler) } - + /** - Set the background image to use for a specified state with a URL, + Set the background image to use for a specified state with a resource, a placeholder image, options progress handler and completion handler. - :param: URL The URL of image for specified state. + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. :param: state The state that uses the specified image. :param: placeholderImage A placeholder image when retrieving the image at URL. :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. @@ -260,16 +433,16 @@ public extension UIButton { :returns: A task represents the retriving process. */ - public func kf_setBackgroundImageWithURL(URL: NSURL, - forState state: UIControlState, - placeholderImage: UIImage?, - optionsInfo: KingfisherOptionsInfo?, - progressBlock: DownloadProgressBlock?, - completionHandler: CompletionHandler?) -> RetrieveImageTask + public func kf_setBackgroundImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask { setBackgroundImage(placeholderImage, forState: state) - kf_setBackgroundWebURL(URL, forState: state) - let task = KingfisherManager.sharedManager.retrieveImageWithURL(URL, optionsInfo: optionsInfo, progressBlock: { (receivedSize, totalSize) -> () in + kf_setBackgroundWebURL(resource.downloadURL, forState: state) + let task = KingfisherManager.sharedManager.retrieveImageWithResource(resource, optionsInfo: optionsInfo, progressBlock: { (receivedSize, totalSize) -> () in if let progressBlock = progressBlock { dispatch_async(dispatch_get_main_queue(), { () -> Void in progressBlock(receivedSize: receivedSize, totalSize: totalSize) @@ -286,6 +459,34 @@ public extension UIButton { return task } + + /** + Set the background image to use for a specified state with a URL, + a placeholder image, options progress handler and completion handler. + + :param: URL The URL of image for specified state. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + :param: progressBlock Called when the image downloading progress gets updated. + :param: completionHandler Called when the image retrieved and set. + + :returns: A task represents the retriving process. + */ + public func kf_setBackgroundImageWithURL(URL: NSURL, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + return kf_setBackgroundImageWithResource(Resource(downloadURL: URL), + forState: state, + placeholderImage: placeholderImage, + optionsInfo: optionsInfo, + progressBlock: progressBlock, + completionHandler: completionHandler) + } } private var lastBackgroundURLKey: Void? diff --git a/Kingfisher/UIImageView+Kingfisher.swift b/Kingfisher/UIImageView+Kingfisher.swift index 2ae26466e..19b8ac509 100644 --- a/Kingfisher/UIImageView+Kingfisher.swift +++ b/Kingfisher/UIImageView+Kingfisher.swift @@ -33,10 +33,26 @@ import Foundation public extension UIImageView { + /** + Set an image with a resource. + It will ask for Kingfisher's manager to get the image for the `cacheKey` property in `resource`. + The memory and disk will be searched first. If the manager does not find it, it will try to download the image at the `resource.downloadURL` and store it with `resource.cacheKey` for next use. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, placeholderImage: nil, optionsInfo: nil, progressBlock: nil, completionHandler: nil) + } + /** Set an image with a URL. It will ask for Kingfisher's manager to get the image for the URL. - The memory and disk will be searched first. If the manager does not find it, it will try to download the image at this URL and store it for next use. + The memory and disk will be searched first with `URL.absoluteString` as the cache key. If the manager does not find it, it will try to download the image at this URL and store the image with `URL.absoluteString` as cache key for next use. + + If you need to specify the key other than `URL.absoluteString`, please use resource version of these APIs with `resource.cacheKey` set to what you want. :param: URL The URL of image. @@ -46,6 +62,20 @@ public extension UIImageView { { return kf_setImageWithURL(URL, placeholderImage: nil, optionsInfo: nil, progressBlock: nil, completionHandler: nil) } + + /** + Set an image with a resource and a placeholder image. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: placeholderImage A placeholder image when retrieving the image at URL. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource, + placeholderImage: UIImage?) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, placeholderImage: placeholderImage, optionsInfo: nil, progressBlock: nil, completionHandler: nil) + } /** Set an image with a URL and a placeholder image. @@ -61,6 +91,22 @@ public extension UIImageView { return kf_setImageWithURL(URL, placeholderImage: placeholderImage, optionsInfo: nil, progressBlock: nil, completionHandler: nil) } + /** + Set an image with a resource, a placaholder image and options. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: nil) + } + /** Set an image with a URL, a placaholder image and options. @@ -77,6 +123,24 @@ public extension UIImageView { return kf_setImageWithURL(URL, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: nil) } + /** + Set an image with a resource, a placeholder image, options and completion handler. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + :param: completionHandler Called when the image retrieved and set. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: completionHandler) + } + /** Set an image with a URL, a placeholder image, options and completion handler. @@ -98,7 +162,7 @@ public extension UIImageView { /** Set an image with a URL, a placeholder image, options, progress handler and completion handler. - :param: URL The URL of image. + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. :param: placeholderImage A placeholder image when retrieving the image at URL. :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. :param: progressBlock Called when the image downloading progress gets updated. @@ -106,12 +170,11 @@ public extension UIImageView { :returns: A task represents the retriving process. */ - - public func kf_setImageWithURL(URL: NSURL, - placeholderImage: UIImage?, - optionsInfo: KingfisherOptionsInfo?, - progressBlock: DownloadProgressBlock?, - completionHandler: CompletionHandler?) -> RetrieveImageTask + public func kf_setImageWithResource(resource: Resource, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask { let showIndicatorWhenLoading = kf_showIndicatorWhenLoading var indicator: UIActivityIndicatorView? = nil @@ -123,8 +186,8 @@ public extension UIImageView { image = placeholderImage - kf_setWebURL(URL) - let task = KingfisherManager.sharedManager.retrieveImageWithURL(URL, optionsInfo: optionsInfo, progressBlock: { (receivedSize, totalSize) -> () in + kf_setWebURL(resource.downloadURL) + let task = KingfisherManager.sharedManager.retrieveImageWithResource(resource, optionsInfo: optionsInfo, progressBlock: { (receivedSize, totalSize) -> () in if let progressBlock = progressBlock { dispatch_async(dispatch_get_main_queue(), { () -> Void in progressBlock(receivedSize: receivedSize, totalSize: totalSize) @@ -137,13 +200,38 @@ public extension UIImageView { sSelf.image = image; indicator?.stopAnimating() } - + completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL) }) }) return task } + + /** + Set an image with a URL, a placeholder image, options, progress handler and completion handler. + + :param: URL The URL of image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + :param: progressBlock Called when the image downloading progress gets updated. + :param: completionHandler Called when the image retrieved and set. + + :returns: A task represents the retriving process. + */ + + public func kf_setImageWithURL(URL: NSURL, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + return kf_setImageWithResource(Resource(downloadURL: URL), + placeholderImage: placeholderImage, + optionsInfo: optionsInfo, + progressBlock: progressBlock, + completionHandler: completionHandler) + } } // MARK: - Associated Object diff --git a/KingfisherTests/UIImageViewExtensionTests.swift b/KingfisherTests/UIImageViewExtensionTests.swift index a75425415..970ac3bdb 100644 --- a/KingfisherTests/UIImageViewExtensionTests.swift +++ b/KingfisherTests/UIImageViewExtensionTests.swift @@ -85,6 +85,33 @@ class UIImageViewExtensionTests: XCTestCase { waitForExpectationsWithTimeout(5, handler: nil) } + func testImageDownloadWithResourceForImageView() { + let expectation = expectationWithDescription("wait for downloading image") + + let URLString = testKeys[0] + stubRequest("GET", URLString).andReturn(200).withBody(testImageData) + let URL = NSURL(string: URLString)! + let resource = Resource(downloadURL: URL) + + var progressBlockIsCalled = false + + imageView.kf_setImageWithResource(resource, placeholderImage: nil, optionsInfo: nil, progressBlock: { (receivedSize, totalSize) -> () in + progressBlockIsCalled = true + }) { (image, error, cacheType, imageURL) -> () in + expectation.fulfill() + + XCTAssert(progressBlockIsCalled, "progressBlock should be called at least once.") + XCTAssert(image != nil, "Downloaded image should exist.") + XCTAssert(image! == testImage, "Downloaded image should be the same as test image.") + XCTAssert(self.imageView.image! == testImage, "Downloaded image should be already set to the image property.") + XCTAssert(self.imageView.kf_webURL == imageURL, "Web URL should equal to the downloaded url.") + + XCTAssert(cacheType == .None, "The cache type should be none here. This image was just downloaded.") + } + + waitForExpectationsWithTimeout(5, handler: nil) + } + func testImageDownloadCancelForImageView() { let expectation = expectationWithDescription("wait for downloading image")