diff --git a/Demo/Kingfisher-Demo/ViewController.swift b/Demo/Kingfisher-Demo/ViewController.swift index 0e5e98d4c..2d37ca946 100755 --- a/Demo/Kingfisher-Demo/ViewController.swift +++ b/Demo/Kingfisher-Demo/ViewController.swift @@ -57,16 +57,16 @@ extension ViewController { override func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { // This will cancel all unfinished downloading task when the cell disappearing. - (cell as! CollectionViewCell).cellImageView.kf_cancelDownloadTask() + (cell as! CollectionViewCell).cellImageView.kf.cancelDownloadTask() } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as! CollectionViewCell - cell.cellImageView.kf_indicatorType = .activity + cell.cellImageView.kf.indicatorType = .activity let url = URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/kingfisher-\(indexPath.row + 1).jpg")! - _ = cell.cellImageView.kf_setImage(with: url, + _ = cell.cellImageView.kf.setImage(with: url, placeholder: nil, options: [.transition(ImageTransition.fade(1))], progressBlock: { receivedSize, totalSize in diff --git a/Demo/Kingfisher-macOS-Demo/ViewController.swift b/Demo/Kingfisher-macOS-Demo/ViewController.swift index 7f756ff6d..9bef1b6f8 100755 --- a/Demo/Kingfisher-macOS-Demo/ViewController.swift +++ b/Demo/Kingfisher-macOS-Demo/ViewController.swift @@ -57,8 +57,8 @@ extension ViewController: NSCollectionViewDataSource { let url = URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/kingfisher-\(indexPath.item + 1).jpg")! - item.imageView?.kf_indicatorType = .activity - item.imageView?.kf_setImage(with: url, placeholder: nil, options: nil, + item.imageView?.kf.indicatorType = .activity + item.imageView?.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { receivedSize, totalSize in print("\(indexPath.item + 1): \(receivedSize)/\(totalSize)") }, diff --git a/Kingfisher.swift b/Kingfisher.swift new file mode 100644 index 000000000..c93327189 --- /dev/null +++ b/Kingfisher.swift @@ -0,0 +1,54 @@ +// +// Kingfisher.swift +// Kingfisher +// +// Created by WANG WEI on 2016/09/14. +// Copyright © 2016年 Wei Wang. All rights reserved. +// + +import Foundation +import ImageIO + +#if os(macOS) + import AppKit + public typealias Image = NSImage + public typealias Color = NSColor + public typealias ImageView = NSImageView + typealias Button = NSButton +#else + import UIKit + public typealias Image = UIImage + public typealias Color = UIColor + #if !os(watchOS) + public typealias ImageView = UIImageView + typealias Button = UIButton + #endif +#endif + +public struct Kingfisher { + public let base: Base + public init(_ base: Base) { + self.base = base + } +} + +/** + A type that has Kingfisher extensions. + */ +public protocol KingfisherCompatible { + associatedtype CompatibleType + var kf: Kingfisher { get set } +} + +public extension KingfisherCompatible { + public var kf: Kingfisher { + get { return Kingfisher(self) } + set { } + } +} + +extension Image: KingfisherCompatible { } +#if !os(watchOS) +extension ImageView: KingfisherCompatible { } +extension Button: KingfisherCompatible { } +#endif diff --git a/Kingfisher.xcodeproj/project.pbxproj b/Kingfisher.xcodeproj/project.pbxproj index 6c96b876f..5a3b8586a 100644 --- a/Kingfisher.xcodeproj/project.pbxproj +++ b/Kingfisher.xcodeproj/project.pbxproj @@ -33,6 +33,10 @@ 4B7742481D87E42E0077024E /* loader.gif in Resources */ = {isa = PBXBuildFile; fileRef = 4B7742461D87E42E0077024E /* loader.gif */; }; 4B98674F1CD1CF42003ADAC7 /* AnimatedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B98674E1CD1CF42003ADAC7 /* AnimatedImageView.swift */; }; 4B9867501CD1CF42003ADAC7 /* AnimatedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B98674E1CD1CF42003ADAC7 /* AnimatedImageView.swift */; }; + 4BA6E2101D891C1D000ED91A /* Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA6E20F1D891C1D000ED91A /* Kingfisher.swift */; }; + 4BA6E2111D892AB0000ED91A /* Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA6E20F1D891C1D000ED91A /* Kingfisher.swift */; }; + 4BA6E2121D892AB1000ED91A /* Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA6E20F1D891C1D000ED91A /* Kingfisher.swift */; }; + 4BA6E2131D892AB2000ED91A /* Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA6E20F1D891C1D000ED91A /* Kingfisher.swift */; }; 4BB24C3D1D79215A00CD5F9C /* CacheSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB24C3C1D79215A00CD5F9C /* CacheSerializer.swift */; }; 4BB24C3E1D79215A00CD5F9C /* CacheSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB24C3C1D79215A00CD5F9C /* CacheSerializer.swift */; }; 4BB24C3F1D79215A00CD5F9C /* CacheSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB24C3C1D79215A00CD5F9C /* CacheSerializer.swift */; }; @@ -488,6 +492,7 @@ 4B7742421D87E2AA0077024E /* Box.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Box.swift; path = Sources/Box.swift; sourceTree = ""; }; 4B7742461D87E42E0077024E /* loader.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = loader.gif; sourceTree = ""; }; 4B98674E1CD1CF42003ADAC7 /* AnimatedImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AnimatedImageView.swift; path = Sources/AnimatedImageView.swift; sourceTree = ""; }; + 4BA6E20F1D891C1D000ED91A /* Kingfisher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Kingfisher.swift; sourceTree = ""; }; 4BB24C3C1D79215A00CD5F9C /* CacheSerializer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CacheSerializer.swift; path = Sources/CacheSerializer.swift; sourceTree = ""; }; 4BCCF3361D5B02F8003387C2 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 4BCCF3371D5B02F8003387C2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -895,6 +900,7 @@ D10945F21C526B6C001408EB /* KingfisherOptionsInfo.swift */, 4B6313F31D766BEF0078E017 /* Filter.swift */, D10945F31C526B6C001408EB /* Resource.swift */, + 4BA6E20F1D891C1D000ED91A /* Kingfisher.swift */, ); name = Core; sourceTree = ""; @@ -2070,6 +2076,7 @@ D10946211C526C61001408EB /* Resource.swift in Sources */, 4BB24C3F1D79215A00CD5F9C /* CacheSerializer.swift in Sources */, D9638BA21C7DBA660046523D /* ImagePrefetcher.swift in Sources */, + 4BA6E2121D892AB1000ED91A /* Kingfisher.swift in Sources */, D10946221C526C61001408EB /* String+MD5.swift in Sources */, D10946231C526C61001408EB /* ThreadHelper.swift in Sources */, ); @@ -2139,6 +2146,7 @@ 4B7742441D87E2AA0077024E /* Box.swift in Sources */, 4B6313F51D766BEF0078E017 /* Filter.swift in Sources */, 4B9867501CD1CF42003ADAC7 /* AnimatedImageView.swift in Sources */, + 4BA6E2111D892AB0000ED91A /* Kingfisher.swift in Sources */, D109460F1C526C0D001408EB /* ImageCache.swift in Sources */, D10946101C526C0D001408EB /* ImageDownloader.swift in Sources */, 4B2B8E4B1D70140F00FC4749 /* ImageProcessor.swift in Sources */, @@ -2169,6 +2177,7 @@ 4BFBEE801D7D0C3600699FD3 /* RequrstModifier.swift in Sources */, D10946271C526CE8001408EB /* ImageDownloader.swift in Sources */, D10946281C526CE8001408EB /* KingfisherManager.swift in Sources */, + 4BA6E2131D892AB2000ED91A /* Kingfisher.swift in Sources */, D10946291C526CE8001408EB /* KingfisherOptionsInfo.swift in Sources */, D109462A1C526CE8001408EB /* Resource.swift in Sources */, D109462B1C526CE8001408EB /* String+MD5.swift in Sources */, @@ -2204,6 +2213,7 @@ 4B7742431D87E2AA0077024E /* Box.swift in Sources */, 4B6313F41D766BEF0078E017 /* Filter.swift in Sources */, 4B98674F1CD1CF42003ADAC7 /* AnimatedImageView.swift in Sources */, + 4BA6E2101D891C1D000ED91A /* Kingfisher.swift in Sources */, D10945F81C526B86001408EB /* ImageCache.swift in Sources */, D10945F91C526B86001408EB /* ImageDownloader.swift in Sources */, 4B2B8E4A1D70128200FC4749 /* ImageProcessor.swift in Sources */, diff --git a/Sources/AnimatedImageView.swift b/Sources/AnimatedImageView.swift index d05fc2ba7..0368f1ba3 100755 --- a/Sources/AnimatedImageView.swift +++ b/Sources/AnimatedImageView.swift @@ -159,7 +159,7 @@ public class AnimatedImageView: UIImageView { /// Reset the animator. private func reset() { animator = nil - if let imageSource = image?.kf_imageSource?.imageRef { + if let imageSource = image?.kf.imageSource?.imageRef { animator = Animator(imageSource: imageSource, contentMode: contentMode, size: bounds.size, framePreloadCount: framePreloadCount) animator?.needsPrescaling = needsPrescaling animator?.prepareFrames() @@ -258,7 +258,7 @@ class Animator { return AnimatedFrame.null } - let frameDuration = imageSource.kf_GIFProperties(at: index).flatMap { + let frameDuration = imageSource.kf.GIFProperties(at: index).flatMap { gifInfo -> Double? in let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as Double? @@ -281,7 +281,7 @@ class Animator { let scaledImage: Image? if needsPrescaling { - scaledImage = image.kf_resize(to: size, for: contentMode) + scaledImage = image.kf.resize(to: size, for: contentMode) } else { scaledImage = image } @@ -312,9 +312,10 @@ class Animator { } } -extension CGImageSource { - func kf_GIFProperties(at index: Int) -> [String: Double]? { - let properties = CGImageSourceCopyPropertiesAtIndex(self, index, nil) as Dictionary? +extension CGImageSource: KingfisherCompatible { } +extension Kingfisher where Base: CGImageSource { + func GIFProperties(at index: Int) -> [String: Double]? { + let properties = CGImageSourceCopyPropertiesAtIndex(base, index, nil) as Dictionary? return properties?[kCGImagePropertyGIFDictionary] as? [String: Double] } } diff --git a/Sources/CacheSerializer.swift b/Sources/CacheSerializer.swift index 02809ce38..df3c5cef3 100644 --- a/Sources/CacheSerializer.swift +++ b/Sources/CacheSerializer.swift @@ -63,14 +63,14 @@ public struct DefaultCacheSerializer: CacheSerializer { private init() {} public func data(with image: Image, original: Data?) -> Data? { - let imageFormat = original?.kf_imageFormat ?? .unknown + let imageFormat = original?.kf.imageFormat ?? .unknown let data: Data? switch imageFormat { - case .PNG: data = image.pngRepresentation() - case .JPEG: data = image.jpegRepresentation(compressionQuality: 1.0) - case .GIF: data = image.gifRepresentation() - case .unknown: data = original ?? image.kf_normalized().pngRepresentation() + case .PNG: data = image.kf.pngRepresentation() + case .JPEG: data = image.kf.jpegRepresentation(compressionQuality: 1.0) + case .GIF: data = image.kf.gifRepresentation() + case .unknown: data = original ?? image.kf.normalized.kf.pngRepresentation() } return data @@ -80,6 +80,6 @@ public struct DefaultCacheSerializer: CacheSerializer { let scale = (options ?? KingfisherEmptyOptionsInfo).scaleFactor let preloadAllGIFData = (options ?? KingfisherEmptyOptionsInfo).preloadAllGIFData - return Image.kf_image(data: data, scale: scale, preloadAllGIFData: preloadAllGIFData) + return Kingfisher.image(data: data, scale: scale, preloadAllGIFData: preloadAllGIFData) } } diff --git a/Sources/Filter.swift b/Sources/Filter.swift index 6a0bf1669..06138bada 100644 --- a/Sources/Filter.swift +++ b/Sources/Filter.swift @@ -44,9 +44,9 @@ extension CIImageProcessor { public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): - return image.kf_apply(filter) + return image.kf.apply(filter) case .data(let data): - return Image.kf_image(data: data, scale: options.scaleFactor, preloadAllGIFData: options.preloadAllGIFData) + return Kingfisher.image(data: data, scale: options.scaleFactor, preloadAllGIFData: options.preloadAllGIFData) } } } @@ -93,8 +93,7 @@ public struct Filter { } } -public extension Image { - +extension Kingfisher where Base: Image { /// Apply a `Filter` containing `CIImage` transformer to `self`. /// /// - parameter filter: The filter used to transform `self`. @@ -102,27 +101,45 @@ public extension Image { /// - returns: A transformed image by input `Filter`. /// /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned. - public func kf_apply(_ filter: Filter) -> Image { + public func apply(_ filter: Filter) -> Image { guard let cgImage = cgImage else { assertionFailure("[Kingfisher] Tint image only works for CG-based image.") - return self + return base } let inputImage = CIImage(cgImage: cgImage) guard let outputImage = filter.transform(inputImage) else { - return self + return base } guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else { assertionFailure("[Kingfisher] Can not make an tint image within context.") - return self + return base } #if os(macOS) - return kf_fixedForRetinaPixel(cgImage: result, to: kf_size) + return fixedForRetinaPixel(cgImage: result, to: size) #else return Image(cgImage: result) #endif } + +} + +public extension Image { + + /// Apply a `Filter` containing `CIImage` transformer to `self`. + /// + /// - parameter filter: The filter used to transform `self`. + /// + /// - returns: A transformed image by input `Filter`. + /// + /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned. + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.apply` instead.", + renamed: "kf.apply") + public func kf_apply(_ filter: Filter) -> Image { + return kf.apply(filter) + } } diff --git a/Sources/Image.swift b/Sources/Image.swift index f532ccf57..761836d4c 100755 --- a/Sources/Image.swift +++ b/Sources/Image.swift @@ -27,17 +27,11 @@ #if os(macOS) import AppKit -public typealias Image = NSImage -public typealias Color = NSColor - private var imagesKey: Void? private var durationKey: Void? #else import UIKit import MobileCoreServices -public typealias Image = UIImage -public typealias Color = UIColor - private var imageSourceKey: Void? private var animatedImageDataKey: Void? #endif @@ -51,103 +45,107 @@ import CoreImage #endif // MARK: - Image Properties -extension Image { -#if os(macOS) +extension Kingfisher where Base: Image { + #if os(macOS) var cgImage: CGImage? { - return cgImage(forProposedRect: nil, context: nil, hints: nil) + return base.cgImage(forProposedRect: nil, context: nil, hints: nil) } - var kf_scale: CGFloat { + var scale: CGFloat { return 1.0 } - fileprivate(set) var kf_images: [Image]? { + fileprivate(set) var images: [Image]? { get { - return objc_getAssociatedObject(self, &imagesKey) as? [Image] + return objc_getAssociatedObject(base, &imagesKey) as? [Image] } set { - objc_setAssociatedObject(self, &imagesKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(base, &imagesKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } - fileprivate(set) var kf_duration: TimeInterval { + fileprivate(set) var duration: TimeInterval { get { - return objc_getAssociatedObject(self, &durationKey) as? TimeInterval ?? 0.0 + return objc_getAssociatedObject(base, &durationKey) as? TimeInterval ?? 0.0 } set { - objc_setAssociatedObject(self, &durationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(base, &durationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } - var kf_size: CGSize { - return representations.reduce(CGSize.zero, { size, rep in + var size: CGSize { + return base.representations.reduce(CGSize.zero, { size, rep in return CGSize(width: max(size.width, CGFloat(rep.pixelsWide)), height: max(size.height, CGFloat(rep.pixelsHigh))) }) } -#else - var kf_scale: CGFloat { - return scale + #else + var cgImage: CGImage? { + return base.cgImage } - var kf_images: [Image]? { - return images + var scale: CGFloat { + return base.scale } - var kf_duration: TimeInterval { - return duration + var images: [Image]? { + return base.images } - fileprivate(set) var kf_imageSource: ImageSource? { - get { - return objc_getAssociatedObject(self, &imageSourceKey) as? ImageSource - } - set { - objc_setAssociatedObject(self, &imageSourceKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } + var duration: TimeInterval { + return base.duration } - - fileprivate(set) var kf_animatedImageData: Data? { + + fileprivate(set) var imageSource: ImageSource? { get { - return objc_getAssociatedObject(self, &animatedImageDataKey) as? Data + return objc_getAssociatedObject(base, &imageSourceKey) as? ImageSource } set { - objc_setAssociatedObject(self, &animatedImageDataKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(base, &imageSourceKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } - var kf_size: CGSize { - return size + fileprivate(set) var animatedImageData: Data? { + get { + return objc_getAssociatedObject(base, &animatedImageDataKey) as? Data } -#endif + set { + objc_setAssociatedObject(base, &animatedImageDataKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + var size: CGSize { + return base.size + } + #endif } // MARK: - Image Conversion -extension Image { -#if os(macOS) - static func kf_image(cgImage: CGImage, scale: CGFloat, refImage: Image?) -> Image { +extension Kingfisher where Base: Image { + #if os(macOS) + static func image(cgImage: CGImage, scale: CGFloat, refImage: Image?) -> Image { return Image(cgImage: cgImage, size: CGSize.zero) } /** - Normalize the image. This method does nothing in OS X. - - - returns: The image itself. - */ - public func kf_normalized() -> Image { - return self + Normalize the image. This method does nothing in OS X. + + - returns: The image itself. + */ + public var normalized: Image { + return base } - static func kf_animated(with images: [Image], forDuration forDurationduration: TimeInterval) -> Image? { + static func animated(with images: [Image], forDuration forDurationduration: TimeInterval) -> Image? { return nil } -#else - static func kf_image(cgImage: CGImage, scale: CGFloat, refImage: Image?) -> Image { - if let refImage = refImage { - return Image(cgImage: cgImage, scale: scale, orientation: refImage.imageOrientation) - } else { - return Image(cgImage: cgImage, scale: scale, orientation: .up) - } + #else + static func image(cgImage: CGImage, scale: CGFloat, refImage: Image?) -> Image { + if let refImage = refImage { + return Image(cgImage: cgImage, scale: scale, orientation: refImage.imageOrientation) + } else { + return Image(cgImage: cgImage, scale: scale, orientation: .up) + } } /** @@ -155,25 +153,25 @@ extension Image { - returns: The normalized image with orientation set to up and correct scale. */ - public func kf_normalized() -> Image { + public var normalized: Image { // prevent animated image (GIF) lose it's images - guard images == nil else { return self } + guard images == nil else { return base } // No need to do anything if already up - guard imageOrientation != .up else { return self } + guard base.imageOrientation != .up else { return base } return draw(cgImage: nil, to: size) { - draw(in: CGRect(origin: CGPoint.zero, size: size)) + base.draw(in: CGRect(origin: CGPoint.zero, size: size)) } } - static func kf_animated(with images: [Image], forDuration duration: TimeInterval) -> Image? { + static func animated(with images: [Image], forDuration duration: TimeInterval) -> Image? { return .animatedImage(with: images, duration: duration) } -#endif + #endif } // MARK: - Image Representation -extension Image { +extension Kingfisher where Base: Image { // MARK: - PNG func pngRepresentation() -> Data? { #if os(macOS) @@ -183,7 +181,7 @@ extension Image { let rep = NSBitmapImageRep(cgImage: cgimage) return rep.representation(using: .PNG, properties: [:]) #else - return UIImagePNGRepresentation(self) + return UIImagePNGRepresentation(base) #endif } @@ -196,7 +194,7 @@ extension Image { let rep = NSBitmapImageRep(cgImage: cgImage) return rep.representation(using:.JPEG, properties: [NSImageCompressionFactor: compressionQuality]) #else - return UIImageJPEGRepresentation(self, compressionQuality) + return UIImageJPEGRepresentation(base, compressionQuality) #endif } @@ -205,18 +203,18 @@ extension Image { #if os(macOS) return gifRepresentation(duration: 0.0, repeatCount: 0) #else - return kf_animatedImageData + return animatedImageData #endif } #if os(macOS) func gifRepresentation(duration: TimeInterval, repeatCount: Int) -> Data? { - guard let images = kf_images else { + guard let images = images else { return nil } let frameCount = images.count - let gifDuration = duration <= 0.0 ? kf_duration / Double(frameCount) : duration / Double(frameCount) + let gifDuration = duration <= 0.0 ? duration / Double(frameCount) : duration / Double(frameCount) let frameProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: gifDuration]] let imageProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: repeatCount]] @@ -229,7 +227,7 @@ extension Image { CGImageDestinationSetProperties(destination, imageProperties as CFDictionary) for image in images { - CGImageDestinationAddImage(destination, image.cgImage!, frameProperties as CFDictionary) + CGImageDestinationAddImage(destination, image.kf.cgImage!, frameProperties as CFDictionary) } return CGImageDestinationFinalize(destination) ? data.copy() as? Data : nil @@ -238,12 +236,11 @@ extension Image { } // MARK: - Create images from data -extension Image { - - static func kf_animated(with data: Data, scale: CGFloat = 1.0, duration: TimeInterval = 0.0, preloadAll: Bool) -> Image? { +extension Kingfisher where Base: Image { + static func animated(with data: Data, scale: CGFloat = 1.0, duration: TimeInterval = 0.0, preloadAll: Bool) -> Image? { func decode(from imageSource: CGImageSource, for options: NSDictionary) -> ([Image], TimeInterval)? { - + //Calculates frame duration for a gif frame out of the kCGImagePropertyGIFDictionary dictionary func frameDuration(from gifInfo: NSDictionary) -> Double { let gifDefaultFrameDuration = 0.100 @@ -272,66 +269,67 @@ extension Image { } else { // Animated GIF guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil), - let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary else + let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary else { return nil } gifDuration += frameDuration(from: gifInfo) } - images.append(Image.kf_image(cgImage: imageRef, scale: scale, refImage: nil)) + images.append(Kingfisher.image(cgImage: imageRef, scale: scale, refImage: nil)) } return (images, gifDuration) } - // Start of kf_animatedImageWithGIFData + // Start of kf.animatedImageWithGIFData let options: NSDictionary = [kCGImageSourceShouldCache as String: true, kCGImageSourceTypeIdentifierHint as String: kUTTypeGIF] guard let imageSource = CGImageSourceCreateWithData(data as CFData, options) else { return nil } -#if os(macOS) - guard let (images, gifDuration) = decode(from: imageSource, for: options) else { - return nil - } - let image = Image(data: data) - image?.kf_images = images - image?.kf_duration = gifDuration - - return image -#else - - if preloadAll { + #if os(macOS) guard let (images, gifDuration) = decode(from: imageSource, for: options) else { return nil } - let image = Image.kf_animated(with: images, forDuration: duration <= 0.0 ? gifDuration : duration) - image?.kf_animatedImageData = data - return image - } else { - let image = Image(data: data) - image?.kf_animatedImageData = data - image?.kf_imageSource = ImageSource(ref: imageSource) + var image = Image(data: data) + image?.kf.images = images + image?.kf.duration = gifDuration + return image - } -#endif + #else + + if preloadAll { + guard let (images, gifDuration) = decode(from: imageSource, for: options) else { + return nil + } + var image = Kingfisher.animated(with: images, forDuration: duration <= 0.0 ? gifDuration : duration) + image?.kf.animatedImageData = data + return image + } else { + var image = Image(data: data) + image?.kf.animatedImageData = data + image?.kf.imageSource = ImageSource(ref: imageSource) + return image + } + #endif } - static func kf_image(data: Data, scale: CGFloat, preloadAllGIFData: Bool) -> Image? { + static func image(data: Data, scale: CGFloat, preloadAllGIFData: Bool) -> Image? { var image: Image? + #if os(macOS) - switch data.kf_imageFormat { + switch data.kf.imageFormat { case .JPEG: image = Image(data: data) case .PNG: image = Image(data: data) - case .GIF: image = Image.kf_animated(with: data, scale: scale, duration: 0.0, preloadAll: preloadAllGIFData) + case .GIF: image = Kingfisher.animated(with: data, scale: scale, duration: 0.0, preloadAll: preloadAllGIFData) case .unknown: image = Image(data: data) } #else - switch data.kf_imageFormat { + switch data.kf.imageFormat { case .JPEG: image = Image(data: data, scale: scale) case .PNG: image = Image(data: data, scale: scale) - case .GIF: image = Image.kf_animated(with: data, scale: scale, duration: 0.0, preloadAll: preloadAllGIFData) + case .GIF: image = Kingfisher.animated(with: data, scale: scale, duration: 0.0, preloadAll: preloadAllGIFData) case .unknown: image = Image(data: data, scale: scale) } #endif @@ -341,9 +339,8 @@ extension Image { } // MARK: - Image Transforming -extension Image { +extension Kingfisher where Base: Image { // MARK: - Round Corner - /// Create a round corner image based on `self`. /// /// - parameter radius: The round corner radius of creating image. @@ -353,44 +350,44 @@ extension Image { /// - returns: An image with round corner of `self`. /// /// - Note: This method only works for CG-based image. - public func kf_image(withRoundRadius radius: CGFloat, fit size: CGSize, scale: CGFloat) -> Image { + public func image(withRoundRadius radius: CGFloat, fit size: CGSize, scale: CGFloat) -> Image { guard let cgImage = cgImage else { assertionFailure("[Kingfisher] Round corder image only works for CG-based image.") - return self + return base } let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size) return draw(cgImage: cgImage, to: size) { #if os(macOS) - let path = NSBezierPath(roundedRect: rect, xRadius: radius, yRadius: radius) - path.windingRule = .evenOddWindingRule - path.addClip() - draw(in: rect) + let path = NSBezierPath(roundedRect: rect, xRadius: radius, yRadius: radius) + path.windingRule = .evenOddWindingRule + path.addClip() + base.draw(in: rect) #else - guard let context = UIGraphicsGetCurrentContext() else { - assertionFailure("[Kingfisher] Failed to create CG context for image.") - return - } - let path = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: radius, height: radius)).cgPath - context.addPath(path) - context.clip() - draw(in: rect) + guard let context = UIGraphicsGetCurrentContext() else { + assertionFailure("[Kingfisher] Failed to create CG context for image.") + return + } + let path = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: radius, height: radius)).cgPath + context.addPath(path) + context.clip() + base.draw(in: rect) #endif } } #if os(iOS) || os(tvOS) - func kf_resize(to size: CGSize, for contentMode: UIViewContentMode) -> Image { + func resize(to size: CGSize, for contentMode: UIViewContentMode) -> Image { switch contentMode { case .scaleAspectFit: - let newSize = self.size.kf_constrained(size) - return kf_resize(to: newSize) + let newSize = self.size.kf.constrained(size) + return resize(to: newSize) case .scaleAspectFill: - let newSize = self.size.kf_filling(size) - return kf_resize(to: newSize) + let newSize = self.size.kf.filling(size) + return resize(to: newSize) default: - return kf_resize(to: size) + return resize(to: size) } } #endif @@ -404,19 +401,19 @@ extension Image { /// - returns: An image with new size. /// /// - Note: This method only works for CG-based image. - public func kf_resize(to size: CGSize) -> Image { + public func resize(to size: CGSize) -> Image { guard let cgImage = cgImage?.fixed else { assertionFailure("[Kingfisher] Resize only works for CG-based image.") - return self + return base } let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size) return draw(cgImage: cgImage, to: size) { #if os(macOS) - draw(in: rect, from: NSRect.zero, operation: .copy, fraction: 1.0) + base.draw(in: rect, from: NSRect.zero, operation: .copy, fraction: 1.0) #else - draw(in: rect) + base.draw(in: rect) #endif } } @@ -430,13 +427,13 @@ extension Image { /// - returns: An image with blur effect applied. /// /// - Note: This method only works for CG-based image. - public func kf_blurred(withRadius radius: CGFloat) -> Image { + public func blurred(withRadius radius: CGFloat) -> Image { #if os(watchOS) - return self + return base #else guard let cgImage = cgImage else { assertionFailure("[Kingfisher] Blur only works for CG-based image.") - return self + return base } // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement @@ -459,16 +456,16 @@ extension Image { iterations = 3 } - let w = Int(kf_size.width) - let h = Int(kf_size.height) + let w = Int(size.width) + let h = Int(size.height) let rowBytes = Int(CGFloat(cgImage.bytesPerRow)) - + let inDataPointer = UnsafeMutablePointer.allocate(capacity: rowBytes * Int(h)) inDataPointer.initialize(to: 0) defer { inDataPointer.deinitialize() inDataPointer.deallocate(capacity: rowBytes * Int(h)) - } + } let bitmapInfo = cgImage.bitmapInfo.fixed guard let context = CGContext(data: inDataPointer, @@ -480,7 +477,7 @@ extension Image { bitmapInfo: bitmapInfo.rawValue) else { assertionFailure("[Kingfisher] Failed to create CG context for blurring image.") - return self + return base } context.draw(cgImage, in: CGRect(x: 0, y: 0, width: w, height: h)) @@ -493,7 +490,7 @@ extension Image { defer { outDataPointer.deinitialize() outDataPointer.deallocate(capacity: rowBytes * Int(h)) - } + } var outBuffer = vImage_Buffer(data: outDataPointer, height: vImagePixelCount(h), width: vImagePixelCount(w), rowBytes: rowBytes) @@ -511,17 +508,17 @@ extension Image { bitmapInfo: bitmapInfo.rawValue) else { assertionFailure("[Kingfisher] Failed to create CG context for blurring image.") - return self + return base } #if os(macOS) - let result = outContext.makeImage().flatMap { kf_fixedForRetinaPixel(cgImage: $0, to: kf_size) } + let result = outContext.makeImage().flatMap { fixedForRetinaPixel(cgImage: $0, to: size) } #else let result = outContext.makeImage().flatMap { Image(cgImage: $0) } #endif guard let blurredImage = result else { assertionFailure("[Kingfisher] Can not make an blurred image within this context.") - return self + return base } return blurredImage @@ -538,29 +535,29 @@ extension Image { /// - returns: An image with a color overlay applied. /// /// - Note: This method only works for CG-based image. - public func kf_overlaying(with color: Color, fraction: CGFloat) -> Image { - + public func overlaying(with color: Color, fraction: CGFloat) -> Image { + guard let cgImage = cgImage?.fixed else { assertionFailure("[Kingfisher] Overlaying only works for CG-based image.") - return self + return base } - let rect = CGRect(x: 0, y: 0, width: kf_size.width, height: kf_size.height) + let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) return draw(cgImage: cgImage, to: rect.size) { #if os(macOS) - draw(in: rect) - if fraction > 0 { - color.withAlphaComponent(1 - fraction).set() - NSRectFillUsingOperation(rect, .sourceAtop) - } + base.draw(in: rect) + if fraction > 0 { + color.withAlphaComponent(1 - fraction).set() + NSRectFillUsingOperation(rect, .sourceAtop) + } #else - color.set() - UIRectFill(rect) - draw(in: rect, blendMode: .destinationIn, alpha: 1.0) - - if fraction > 0 { - draw(in: rect, blendMode: .sourceAtop, alpha: fraction) - } + color.set() + UIRectFill(rect) + base.draw(in: rect, blendMode: .destinationIn, alpha: 1.0) + + if fraction > 0 { + base.draw(in: rect, blendMode: .sourceAtop, alpha: fraction) + } #endif } } @@ -572,11 +569,11 @@ extension Image { /// - parameter color: The color should be used to tint `self` /// /// - returns: An image with a color tint applied. - public func kf_tinted(with color: Color) -> Image { + public func tinted(with color: Color) -> Image { #if os(watchOS) - return self + return base #else - return kf_apply(.tint(color)) + return apply(.tint(color)) #endif } @@ -590,45 +587,45 @@ extension Image { /// - parameter inputEV: InputEV changing to image. /// /// - returns: An image with color control applied. - public func kf_adjusted(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) -> Image { + public func adjusted(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) -> Image { #if os(watchOS) - return self + return base #else - return kf_apply(.colorControl(brightness, contrast, saturation, inputEV)) + return apply(.colorControl(brightness, contrast, saturation, inputEV)) #endif } } // MARK: - Decode -extension Image { - func kf_decoded() -> Image? { - return self.kf_decoded(scale: kf_scale) +extension Kingfisher where Base: Image { + var decoded: Image? { + return decoded(scale: scale) } - func kf_decoded(scale: CGFloat) -> Image { + func decoded(scale: CGFloat) -> Image { // prevent animated image (GIF) lose it's images -#if os(iOS) - if kf_imageSource != nil { return self } -#else - if kf_images != nil { return self } -#endif + #if os(iOS) + if imageSource != nil { return base } + #else + if images != nil { return base } + #endif guard let imageRef = self.cgImage else { assertionFailure("[Kingfisher] Decoding only works for CG-based image.") - return self + return base } let colorSpace = CGColorSpaceCreateDeviceRGB() let bitmapInfo = imageRef.bitmapInfo.fixed guard let context = CGContext(data: nil, width: imageRef.width, height: imageRef.height, bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) else { assertionFailure("[Kingfisher] Decoding fails to create a valid context.") - return self + return base } let rect = CGRect(x: 0, y: 0, width: imageRef.width, height: imageRef.height) context.draw(imageRef, in: rect) let decompressedImageRef = context.makeImage() - return Image.kf_image(cgImage: decompressedImageRef!, scale: scale, refImage: self) + return Kingfisher.image(cgImage: decompressedImageRef!, scale: scale, refImage: base) } } @@ -654,10 +651,20 @@ enum ImageFormat { // MARK: - Misc Helpers -extension Data { - var kf_imageFormat: ImageFormat { +protocol DataType { + func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) +} +extension Data: DataType { + func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { + (self as NSData).getBytes(buffer, length: length) + } +} + +extension Data: KingfisherCompatible { } +extension Kingfisher where Base: DataType { + var imageFormat: ImageFormat { var buffer = [UInt8](repeating: 0, count: 8) - (self as NSData).getBytes(&buffer, length: 8) + base.getBytes(&buffer, length: 8) if buffer == ImageHeaderData.PNG { return .PNG } else if buffer[0] == ImageHeaderData.JPEG_SOI[0] && @@ -676,23 +683,30 @@ extension Data { } } -extension CGSize { - func kf_constrained(_ size: CGSize) -> CGSize { - let aspectWidth = round(kf_aspectRatio * size.height) - let aspectHeight = round(size.width / kf_aspectRatio) +protocol SizeType { + var width: CGFloat { get } + var height: CGFloat { get } +} +extension CGSize: SizeType { } + +extension CGSize: KingfisherCompatible { } +extension Kingfisher where Base: SizeType { + func constrained(_ size: CGSize) -> CGSize { + let aspectWidth = round(aspectRatio * size.height) + let aspectHeight = round(size.width / aspectRatio) return aspectWidth > size.width ? CGSize(width: size.width, height: aspectHeight) : CGSize(width: aspectWidth, height: size.height) } - func kf_filling(_ size: CGSize) -> CGSize { - let aspectWidth = round(kf_aspectRatio * size.height) - let aspectHeight = round(size.width / kf_aspectRatio) + func filling(_ size: CGSize) -> CGSize { + let aspectWidth = round(aspectRatio * size.height) + let aspectHeight = round(size.width / aspectRatio) return aspectWidth < size.width ? CGSize(width: size.width, height: aspectHeight) : CGSize(width: aspectWidth, height: size.height) } - private var kf_aspectRatio: CGFloat { - return height == 0.0 ? 1.0 : width / height + private var aspectRatio: CGFloat { + return base.height == 0.0 ? 1.0 : base.width / base.height } } @@ -734,7 +748,7 @@ extension CGBitmapInfo { } -extension Image { +extension Kingfisher where Base: Image { func draw(cgImage: CGImage?, to size: CGSize, draw: ()->()) -> Image { #if os(macOS) @@ -751,7 +765,7 @@ extension Image { bitsPerPixel: 0) else { assertionFailure("[Kingfisher] Image representation cannot be created.") - return self + return base } rep.size = size @@ -770,18 +784,18 @@ extension Image { UIGraphicsBeginImageContextWithOptions(size, false, scale) defer { UIGraphicsEndImageContext() } draw() - return UIGraphicsGetImageFromCurrentImageContext() ?? self + return UIGraphicsGetImageFromCurrentImageContext() ?? base #endif } #if os(macOS) - func kf_fixedForRetinaPixel(cgImage: CGImage, to size: CGSize) -> Image { + func fixedForRetinaPixel(cgImage: CGImage, to size: CGSize) -> Image { - let image = Image(cgImage: cgImage, size: self.size) + let image = Image(cgImage: cgImage, size: base.size) let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size) - return draw(cgImage: cgImage, to: kf_size) { + return draw(cgImage: cgImage, to: self.size) { image.draw(in: rect, from: NSRect.zero, operation: .copy, fraction: 1.0) } } @@ -824,4 +838,112 @@ extension Double { } } - +// MARK: - Deprecated. Only for back compatibility. +extension Image { + /** + Normalize the image. This method does nothing in OS X. + + - returns: The image itself. + */ + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.normalized` instead.", + renamed: "kf.normalized") + public func kf_normalized() -> Image { + return kf.normalized + } + + // MARK: - Round Corner + + /// Create a round corner image based on `self`. + /// + /// - parameter radius: The round corner radius of creating image. + /// - parameter size: The target size of creating image. + /// - parameter scale: The image scale of creating image. + /// + /// - returns: An image with round corner of `self`. + /// + /// - Note: This method only works for CG-based image. + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.image(withRoundRadius:fit:scale:)` instead.", + renamed: "kf.image") + public func kf_image(withRoundRadius radius: CGFloat, fit size: CGSize, scale: CGFloat) -> Image { + return kf.image(withRoundRadius: radius, fit: size, scale: scale) + } + + // MARK: - Resize + /// Resize `self` to an image of new size. + /// + /// - parameter size: The target size. + /// + /// - returns: An image with new size. + /// + /// - Note: This method only works for CG-based image. + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.resize(to:)` instead.", + renamed: "kf.resize") + public func kf_resize(to size: CGSize) -> Image { + return kf.resize(to: size) + } + + // MARK: - Blur + /// Create an image with blur effect based on `self`. + /// + /// - parameter radius: The blur radius should be used when creating blue. + /// + /// - returns: An image with blur effect applied. + /// + /// - Note: This method only works for CG-based image. + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.blurred(withRadius:)` instead.", + renamed: "kf.blurred") + public func kf_blurred(withRadius radius: CGFloat) -> Image { + return kf.blurred(withRadius: radius) + } + + // MARK: - Overlay + /// Create an image from `self` with a color overlay layer. + /// + /// - parameter color: The color should be use to overlay. + /// - parameter fraction: Fraction of input color. From 0.0 to 1.0. 0.0 means solid color, 1.0 means transparent overlay. + /// + /// - returns: An image with a color overlay applied. + /// + /// - Note: This method only works for CG-based image. + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.overlaying(with:fraction:)` instead.", + renamed: "kf.overlaying") + public func kf_overlaying(with color: Color, fraction: CGFloat) -> Image { + return kf.overlaying(with: color, fraction: fraction) + } + + // MARK: - Tint + + /// Create an image from `self` with a color tint. + /// + /// - parameter color: The color should be used to tint `self` + /// + /// - returns: An image with a color tint applied. + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.tinted(with:)` instead.", + renamed: "kf.tinted") + public func kf_tinted(with color: Color) -> Image { + return kf.tinted(with: color) + } + + // MARK: - Color Control + + /// Create an image from `self` with color control. + /// + /// - parameter brightness: Brightness changing to image. + /// - parameter contrast: Contrast changing to image. + /// - parameter saturation: Saturation changing to image. + /// - parameter inputEV: InputEV changing to image. + /// + /// - returns: An image with color control applied. + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.adjusted` instead.", + renamed: "kf.adjusted") + public func kf_adjusted(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) -> Image { + return kf.adjusted(brightness: brightness, contrast: contrast, saturation: saturation, inputEV: inputEV) + } +} diff --git a/Sources/ImageCache.swift b/Sources/ImageCache.swift index 582f3f71c..0f6261cb6 100755 --- a/Sources/ImageCache.swift +++ b/Sources/ImageCache.swift @@ -174,7 +174,7 @@ extension ImageCache { { let computedKey = key.computedKey(with: identifier) - memoryCache.setObject(image, forKey: computedKey as NSString, cost: image.kf_imageCost) + memoryCache.setObject(image, forKey: computedKey as NSString, cost: image.kf.imageCost) func callHandlerInMainQueue() { if let handler = completionHandler { @@ -280,7 +280,7 @@ extension ImageCache { if let image = sSelf.retrieveImageInDiskCache(forKey: key, options: options) { if options.backgroundDecode { sSelf.processQueue.async { - let result = image.kf_decoded(scale: options.scaleFactor) + let result = image.kf.decoded(scale: options.scaleFactor) sSelf.store(result, forKey: key, @@ -510,7 +510,7 @@ extension ImageCache { */ @objc public func backgroundCleanExpiredDiskCache() { // if 'sharedApplication()' is unavailable, then return - guard let sharedApplication = UIApplication.kf_shared else { return } + guard let sharedApplication = Kingfisher.shared else { return } func endBackgroundTask(_ task: inout UIBackgroundTaskIdentifier) { sharedApplication.endBackgroundTask(task) @@ -636,15 +636,15 @@ extension ImageCache { } func cacheFileName(forComputedKey key: String) -> String { - return key.kf_MD5 + return key.kf.md5 } } -extension Image { - var kf_imageCost: Int { - return kf_images == nil ? - Int(size.height * size.width * kf_scale * kf_scale) : - Int(size.height * size.width * kf_scale * kf_scale) * kf_images!.count +extension Kingfisher where Base: Image { + var imageCost: Int { + return images == nil ? + Int(size.height * size.width * scale * scale) : + Int(size.height * size.width * scale * scale) * images!.count } } @@ -656,11 +656,12 @@ extension Dictionary { #if !os(macOS) && !os(watchOS) // MARK: - For App Extensions -extension UIApplication { - public static var kf_shared: UIApplication? { +extension UIApplication: KingfisherCompatible { } +extension Kingfisher where Base: UIApplication { + public static var shared: UIApplication? { let selector = NSSelectorFromString("sharedApplication") - guard responds(to: selector) else { return nil } - return perform(selector).takeUnretainedValue() as? UIApplication + guard Base.responds(to: selector) else { return nil } + return Base.perform(selector).takeUnretainedValue() as? UIApplication } } #endif diff --git a/Sources/ImageDownloader.swift b/Sources/ImageDownloader.swift index 97cc106e4..922002ed2 100755 --- a/Sources/ImageDownloader.swift +++ b/Sources/ImageDownloader.swift @@ -487,7 +487,7 @@ class ImageDownloaderSessionHandler: NSObject, URLSessionDataDelegate, Authentic downloader.delegate?.imageDownloader(downloader, didDownload: image, for: url, with: task.response) if options.backgroundDecode { - self.callback(with: image.kf_decoded(scale: options.scaleFactor), error: nil, url: url, originalData: data) + self.callback(with: image.kf.decoded(scale: options.scaleFactor), error: nil, url: url, originalData: data) } else { self.callback(with: image, error: nil, url: url, originalData: data) } diff --git a/Sources/ImagePrefetcher.swift b/Sources/ImagePrefetcher.swift index f7adac83b..f699700c6 100755 --- a/Sources/ImagePrefetcher.swift +++ b/Sources/ImagePrefetcher.swift @@ -128,7 +128,7 @@ public class ImagePrefetcher { pendingResources = ArraySlice(resources) // We want all callbacks from main queue, so we ignore the call back queue in options - let optionsInfoWithoutQueue = options?.kf_removeAllMatchesIgnoringAssociatedValue(.callbackDispatchQueue(nil)) + let optionsInfoWithoutQueue = options?.removeAllMatchesIgnoringAssociatedValue(.callbackDispatchQueue(nil)) self.optionsInfo = optionsInfoWithoutQueue ?? KingfisherEmptyOptionsInfo let cache = self.optionsInfo.targetCache diff --git a/Sources/ImageProcessor.swift b/Sources/ImageProcessor.swift index 1e2d404ff..de93a1c9a 100644 --- a/Sources/ImageProcessor.swift +++ b/Sources/ImageProcessor.swift @@ -116,7 +116,7 @@ public struct DefaultImageProcessor: ImageProcessor { case .image(let image): return image case .data(let data): - return Image.kf_image(data: data, scale: options.scaleFactor, preloadAllGIFData: options.preloadAllGIFData) + return Kingfisher.image(data: data, scale: options.scaleFactor, preloadAllGIFData: options.preloadAllGIFData) } } } @@ -153,8 +153,8 @@ public struct RoundCornerImageProcessor: ImageProcessor { public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): - let size = targetSize ?? image.kf_size - return image.kf_image(withRoundRadius: cornerRadius, fit: size, scale: options.scaleFactor) + let size = targetSize ?? image.kf.size + return image.kf.image(withRoundRadius: cornerRadius, fit: size, scale: options.scaleFactor) case .data(_): return (DefaultImageProcessor() >> self).process(item: item, options: options) } @@ -181,7 +181,7 @@ public struct ResizingImageProcessor: ImageProcessor { public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): - return image.kf_resize(to: targetSize) + return image.kf.resize(to: targetSize) case .data(_): return (DefaultImageProcessor() >> self).process(item: item, options: options) } @@ -210,7 +210,7 @@ public struct BlurImageProcessor: ImageProcessor { switch item { case .image(let image): let radius = blurRadius * options.scaleFactor - return image.kf_blurred(withRadius: radius) + return image.kf.blurred(withRadius: radius) case .data(_): return (DefaultImageProcessor() >> self).process(item: item, options: options) } @@ -244,7 +244,7 @@ public struct OverlayImageProcessor: ImageProcessor { public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): - return image.kf_overlaying(with: overlay, fraction: fraction) + return image.kf.overlaying(with: overlay, fraction: fraction) case .data(_): return (DefaultImageProcessor() >> self).process(item: item, options: options) } @@ -272,7 +272,7 @@ public struct TintImageProcessor: ImageProcessor { public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): - return image.kf_tinted(with: tint) + return image.kf.tinted(with: tint) case .data(_): return (DefaultImageProcessor() >> self).process(item: item, options: options) } @@ -316,7 +316,7 @@ public struct ColorControlsProcessor: ImageProcessor { public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): - return image.kf_adjusted(brightness: brightness, contrast: contrast, saturation: saturation, inputEV: inputEV) + return image.kf.adjusted(brightness: brightness, contrast: contrast, saturation: saturation, inputEV: inputEV) case .data(_): return (DefaultImageProcessor() >> self).process(item: item, options: options) } diff --git a/Sources/ImageView+Kingfisher.swift b/Sources/ImageView+Kingfisher.swift index 7b54e68ac..d5b53a6b7 100755 --- a/Sources/ImageView+Kingfisher.swift +++ b/Sources/ImageView+Kingfisher.swift @@ -27,141 +27,116 @@ #if os(macOS) import AppKit -typealias ImageView = NSImageView #else import UIKit -typealias ImageView = UIImageView #endif -// MARK: - Set Images +// MARK: - Extension methods. /** -* Set image to use from web. -*/ -extension ImageView { - + * Set image to use from web. + */ +extension Kingfisher where Base: ImageView { /** - Set an image with a resource, a placeholder image, options, progress handler and completion handler. - - - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. - - parameter placeholder: A placeholder image when retrieving the image at URL. - - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. - - parameter progressBlock: Called when the image downloading progress gets updated. - - parameter completionHandler: Called when the image retrieved and set. - - - returns: A task represents the retrieving process. + Set an image with a resource, a placeholder image, options, progress handler and completion handler. - - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. - */ + */ @discardableResult - public func kf_setImage(with resource: Resource?, - placeholder: Image? = nil, - options: KingfisherOptionsInfo? = nil, - progressBlock: DownloadProgressBlock? = nil, - completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + public func setImage(with resource: Resource?, + placeholder: Image? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask { - image = placeholder + base.image = placeholder guard let resource = resource else { completionHandler?(nil, nil, .none, nil) return .empty } - let maybeIndicator = kf_indicator + let maybeIndicator = indicator maybeIndicator?.startAnimatingView() - kf_setWebURL(resource.downloadURL) + setWebURL(resource.downloadURL) var options = options ?? KingfisherEmptyOptionsInfo if shouldPreloadAllGIF() { options.append(.preloadAllGIFData) } - - let task = KingfisherManager.shared.retrieveImage(with: resource, options: options, + + let task = KingfisherManager.shared.retrieveImage( + with: resource, + options: options, progressBlock: { receivedSize, totalSize in if let progressBlock = progressBlock { progressBlock(receivedSize, totalSize) } }, - completionHandler: {[weak self] image, error, cacheType, imageURL in - + completionHandler: {[weak base] image, error, cacheType, imageURL in DispatchQueue.main.safeAsync { - guard let sSelf = self, imageURL == sSelf.kf_webURL else { + guard let strongBase = base, imageURL == self.webURL else { return } - - sSelf.kf_setImageTask(nil) - + self.setImageTask(nil) guard let image = image else { maybeIndicator?.stopAnimatingView() completionHandler?(nil, error, cacheType, imageURL) return } - guard let transitionItem = options.kf_firstMatchIgnoringAssociatedValue(.transition(.none)), + guard let transitionItem = options.firstMatchIgnoringAssociatedValue(.transition(.none)), case .transition(let transition) = transitionItem, ( options.forceTransition || cacheType == .none) else { maybeIndicator?.stopAnimatingView() - sSelf.image = image + strongBase.image = image completionHandler?(image, error, cacheType, imageURL) return } #if !os(macOS) - UIView.transition(with: sSelf, duration: 0.0, options: [], - animations: { maybeIndicator?.stopAnimatingView() }, - completion: { _ in - UIView.transition(with: sSelf, duration: transition.duration, - options: [transition.animationOptions, .allowUserInteraction], - animations: { - // Set image property in the animation. - transition.animations?(sSelf, image) - }, - completion: { finished in - transition.completion?(finished) - completionHandler?(image, error, cacheType, imageURL) - } - ) - }) + UIView.transition(with: strongBase, duration: 0.0, options: [], + animations: { maybeIndicator?.stopAnimatingView() }, + completion: { _ in + UIView.transition(with: strongBase, duration: transition.duration, + options: [transition.animationOptions, .allowUserInteraction], + animations: { + // Set image property in the animation. + transition.animations?(strongBase, image) + }, + completion: { finished in + transition.completion?(finished) + completionHandler?(image, error, cacheType, imageURL) + }) + }) #endif } }) - kf_setImageTask(task) + setImageTask(task) return task } -} - -extension ImageView { - func shouldPreloadAllGIF() -> Bool { - return true - } -} - -extension ImageView { + /** Cancel the image download task bounded to the image view if it is running. Nothing will happen if the downloading has already finished. */ - public func kf_cancelDownloadTask() { - kf_imageTask?.downloadTask?.cancel() + public func cancelDownloadTask() { + imageTask?.downloadTask?.cancel() } -} - -/** - Enum for the types of indicators that the user can choose from. - */ -extension ImageView { - public enum IndicatorType { - /// No indicator. - case none - /// Use system activity indicator. - case activity - /// Use an image as indicator. GIF is supported. - case image(imageData: Data) - /// Use a custom indicator, which conforms to the `Indicator` protocol. - case custom(indicator: Indicator) + + func shouldPreloadAllGIF() -> Bool { + return true } } @@ -171,72 +146,147 @@ private var indicatorKey: Void? private var indicatorTypeKey: Void? private var imageTaskKey: Void? -extension ImageView { +extension Kingfisher where Base: ImageView { /// Get the image URL binded to this image view. - public var kf_webURL: URL? { - return objc_getAssociatedObject(self, &lastURLKey) as? URL + public var webURL: URL? { + return objc_getAssociatedObject(base, &lastURLKey) as? URL } - fileprivate func kf_setWebURL(_ url: URL) { - objc_setAssociatedObject(self, &lastURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + fileprivate func setWebURL(_ url: URL) { + objc_setAssociatedObject(base, &lastURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } /// Holds which indicator type is going to be used. /// Default is .none, means no indicator will be shown. - public var kf_indicatorType: IndicatorType { + public var indicatorType: IndicatorType { get { - let indicator = (objc_getAssociatedObject(self, &indicatorTypeKey) as? Box)?.value + let indicator = (objc_getAssociatedObject(base, &indicatorTypeKey) as? Box)?.value return indicator ?? .none } set { switch newValue { case .none: - kf_indicator = nil + indicator = nil case .activity: - kf_indicator = ActivityIndicator() + indicator = ActivityIndicator() case .image(let data): - kf_indicator = ImageIndicator(imageData: data) - case .custom(let indicator): - kf_indicator = indicator + indicator = ImageIndicator(imageData: data) + case .custom(let anIndicator): + indicator = anIndicator } - objc_setAssociatedObject(self, &indicatorTypeKey, Box(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(base, &indicatorTypeKey, Box(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } /// Holds any type that conforms to the protocol `Indicator`. /// The protocol `Indicator` has a `view` property that will be shown when loading an image. - /// It will be `nil` if `kf_indicatorType` is `.none`. - public private(set) var kf_indicator: Indicator? { + /// It will be `nil` if `indicatorType` is `.none`. + public fileprivate(set) var indicator: Indicator? { get { - return (objc_getAssociatedObject(self, &indicatorKey) as? Box)?.value + return (objc_getAssociatedObject(base, &indicatorKey) as? Box)?.value } set { // Remove previous - if let previousIndicator = kf_indicator { + if let previousIndicator = indicator { previousIndicator.view.removeFromSuperview() } // Add new if var newIndicator = newValue { - newIndicator.view.frame = frame - newIndicator.viewCenter = CGPoint(x: bounds.midX, y: bounds.midY) + newIndicator.view.frame = base.frame + newIndicator.viewCenter = CGPoint(x: base.bounds.midX, y: base.bounds.midY) newIndicator.view.isHidden = true - self.addSubview(newIndicator.view) + base.addSubview(newIndicator.view) } // Save in associated object - objc_setAssociatedObject(self, &indicatorKey, Box(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(base, &indicatorKey, Box(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } - fileprivate var kf_imageTask: RetrieveImageTask? { - return objc_getAssociatedObject(self, &imageTaskKey) as? RetrieveImageTask + fileprivate var imageTask: RetrieveImageTask? { + return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask + } + + fileprivate func setImageTask(_ task: RetrieveImageTask?) { + objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } +} + + +// MARK: - Deprecated. Only for back compatibility. +/** +* Set image to use from web. Deprecated. Use `kf` namespacing instead. +*/ +extension ImageView { + /** + Set an image with a resource, a placeholder image, options, progress handler and completion handler. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. + */ + @available(*, deprecated, message: "Extensions directly on image views are deprecated. Use `imageView.kf.setImage` instead.", renamed: "kf.setImage") + @discardableResult + public func kf_setImage(with resource: Resource?, + placeholder: Image? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + { + return kf.setImage(with: resource, placeholder: placeholder, options: options, progressBlock: progressBlock, completionHandler: completionHandler) + } + + /** + Cancel the image download task bounded to the image view if it is running. + Nothing will happen if the downloading has already finished. + */ + @available(*, deprecated, message: "Extensions directly on image views are deprecated. Use `imageView.kf.cancelDownloadTask` instead.", renamed: "kf.cancelDownloadTask") + public func kf_cancelDownloadTask() { kf.cancelDownloadTask() } + + /// Get the image URL binded to this image view. + @available(*, deprecated, message: "Extensions directly on image views are deprecated. Use `imageView.kf.webURL` instead.", renamed: "kf.webURL") + public var kf_webURL: URL? { return kf.webURL } + + /// Holds which indicator type is going to be used. + /// Default is .none, means no indicator will be shown. + @available(*, deprecated, message: "Extensions directly on image views are deprecated. Use `imageView.kf.indicatorType` instead.", renamed: "kf.indicatorType") + public var kf_indicatorType: IndicatorType { + get { return kf.indicatorType } + set { + var holder = kf + holder.indicatorType = newValue + } } - fileprivate func kf_setImageTask(_ task: RetrieveImageTask?) { - objc_setAssociatedObject(self, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + @available(*, deprecated, message: "Extensions directly on image views are deprecated. Use `imageView.kf.indicator` instead.", renamed: "kf.indicator") + /// Holds any type that conforms to the protocol `Indicator`. + /// The protocol `Indicator` has a `view` property that will be shown when loading an image. + /// It will be `nil` if `kf_indicatorType` is `.none`. + public private(set) var kf_indicator: Indicator? { + get { return kf.indicator } + set { + var holder = kf + holder.indicator = newValue + } } + + @available(*, deprecated, message: "Extensions directly on image views are deprecated.", renamed: "kf.imageTask") + fileprivate var kf_imageTask: RetrieveImageTask? { return kf.imageTask } + @available(*, deprecated, message: "Extensions directly on image views are deprecated.", renamed: "kf.setImageTask") + fileprivate func kf_setImageTask(_ task: RetrieveImageTask?) { kf.setImageTask(task) } + @available(*, deprecated, message: "Extensions directly on image views are deprecated.", renamed: "kf.setWebURL") + fileprivate func kf_setWebURL(_ url: URL) { kf.setWebURL(url) } + @available(*, deprecated, message: "Extensions directly on image views are deprecated.", renamed: "kf.shouldPreloadAllGIF") + func shouldPreloadAllGIF() -> Bool { return kf.shouldPreloadAllGIF() } } diff --git a/Sources/Indicator.swift b/Sources/Indicator.swift index 4dfc30f3a..89908b10b 100644 --- a/Sources/Indicator.swift +++ b/Sources/Indicator.swift @@ -36,6 +36,17 @@ public typealias IndicatorView = UIView #endif +public enum IndicatorType { + /// No indicator. + case none + /// Use system activity indicator. + case activity + /// Use an image as indicator. GIF is supported. + case image(imageData: Data) + /// Use a custom indicator, which conforms to the `Indicator` protocol. + case custom(indicator: Indicator) +} + // MARK: - Indicator Protocol public protocol Indicator { func startAnimatingView() diff --git a/Sources/KingfisherOptionsInfo.swift b/Sources/KingfisherOptionsInfo.swift index 41237bffb..cb57f5ed0 100755 --- a/Sources/KingfisherOptionsInfo.swift +++ b/Sources/KingfisherOptionsInfo.swift @@ -138,18 +138,18 @@ func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Boo } extension Collection where Iterator.Element == KingfisherOptionsInfoItem { - func kf_firstMatchIgnoringAssociatedValue(_ target: Iterator.Element) -> Iterator.Element? { + func firstMatchIgnoringAssociatedValue(_ target: Iterator.Element) -> Iterator.Element? { return index { $0 <== target }.flatMap { self[$0] } } - func kf_removeAllMatchesIgnoringAssociatedValue(_ target: Iterator.Element) -> [Iterator.Element] { + func removeAllMatchesIgnoringAssociatedValue(_ target: Iterator.Element) -> [Iterator.Element] { return self.filter { !($0 <== target) } } } extension Collection where Iterator.Element == KingfisherOptionsInfoItem { var targetCache: ImageCache { - if let item = kf_firstMatchIgnoringAssociatedValue(.targetCache(.default)), + if let item = firstMatchIgnoringAssociatedValue(.targetCache(.default)), case .targetCache(let cache) = item { return cache @@ -158,7 +158,7 @@ extension Collection where Iterator.Element == KingfisherOptionsInfoItem { } var downloader: ImageDownloader { - if let item = kf_firstMatchIgnoringAssociatedValue(.downloader(.default)), + if let item = firstMatchIgnoringAssociatedValue(.downloader(.default)), case .downloader(let downloader) = item { return downloader @@ -167,7 +167,7 @@ extension Collection where Iterator.Element == KingfisherOptionsInfoItem { } var transition: ImageTransition { - if let item = kf_firstMatchIgnoringAssociatedValue(.transition(.none)), + if let item = firstMatchIgnoringAssociatedValue(.transition(.none)), case .transition(let transition) = item { return transition @@ -176,7 +176,7 @@ extension Collection where Iterator.Element == KingfisherOptionsInfoItem { } var downloadPriority: Float { - if let item = kf_firstMatchIgnoringAssociatedValue(.downloadPriority(0)), + if let item = firstMatchIgnoringAssociatedValue(.downloadPriority(0)), case .downloadPriority(let priority) = item { return priority @@ -209,7 +209,7 @@ extension Collection where Iterator.Element == KingfisherOptionsInfoItem { } var callbackDispatchQueue: DispatchQueue { - if let item = kf_firstMatchIgnoringAssociatedValue(.callbackDispatchQueue(nil)), + if let item = firstMatchIgnoringAssociatedValue(.callbackDispatchQueue(nil)), case .callbackDispatchQueue(let queue) = item { return queue ?? DispatchQueue.main @@ -218,7 +218,7 @@ extension Collection where Iterator.Element == KingfisherOptionsInfoItem { } var scaleFactor: CGFloat { - if let item = kf_firstMatchIgnoringAssociatedValue(.scaleFactor(0)), + if let item = firstMatchIgnoringAssociatedValue(.scaleFactor(0)), case .scaleFactor(let scale) = item { return scale @@ -227,7 +227,7 @@ extension Collection where Iterator.Element == KingfisherOptionsInfoItem { } var modifier: ImageDownloadRequestModifier { - if let item = kf_firstMatchIgnoringAssociatedValue(.requestModifier(NoModifier.default)), + if let item = firstMatchIgnoringAssociatedValue(.requestModifier(NoModifier.default)), case .requestModifier(let modifier) = item { return modifier @@ -236,7 +236,7 @@ extension Collection where Iterator.Element == KingfisherOptionsInfoItem { } var processor: ImageProcessor { - if let item = kf_firstMatchIgnoringAssociatedValue(.processor(DefaultImageProcessor.default)), + if let item = firstMatchIgnoringAssociatedValue(.processor(DefaultImageProcessor.default)), case .processor(let processor) = item { return processor @@ -245,7 +245,7 @@ extension Collection where Iterator.Element == KingfisherOptionsInfoItem { } var cacheSerializer: CacheSerializer { - if let item = kf_firstMatchIgnoringAssociatedValue(.cacheSerializer(DefaultCacheSerializer.default)), + if let item = firstMatchIgnoringAssociatedValue(.cacheSerializer(DefaultCacheSerializer.default)), case .cacheSerializer(let cacheSerializer) = item { return cacheSerializer diff --git a/Sources/NSButton+Kingfisher.swift b/Sources/NSButton+Kingfisher.swift index 957cdb4f3..dd8a95e40 100644 --- a/Sources/NSButton+Kingfisher.swift +++ b/Sources/NSButton+Kingfisher.swift @@ -31,190 +31,286 @@ import AppKit /** * Set image to use from web. */ -extension NSButton { - +extension Kingfisher where Base: NSButton { /** Set an image with a resource, a placeholder image, options, progress handler and completion handler. - + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. - parameter placeholder: A placeholder image when retrieving the image at URL. - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. - parameter progressBlock: Called when the image downloading progress gets updated. - parameter completionHandler: Called when the image retrieved and set. - + - returns: A task represents the retrieving process. - + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. */ @discardableResult - public func kf_setImage(with resource: Resource?, - placeholder: Image? = nil, - options: KingfisherOptionsInfo? = nil, - progressBlock: DownloadProgressBlock? = nil, - completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + public func setImage(with resource: Resource?, + placeholder: Image? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask { - image = placeholder + base.image = placeholder guard let resource = resource else { completionHandler?(nil, nil, .none, nil) return .empty } - kf_setWebURL(resource.downloadURL) - let task = KingfisherManager.shared.retrieveImage(with: resource, options: options, - progressBlock: { receivedSize, totalSize in + setWebURL(resource.downloadURL) + let task = KingfisherManager.shared.retrieveImage( + with: resource, + options: options, + progressBlock: { receivedSize, totalSize in if let progressBlock = progressBlock { progressBlock(receivedSize, totalSize) } }, - completionHandler: {[weak self] image, error, cacheType, imageURL in + completionHandler: {[weak base] image, error, cacheType, imageURL in DispatchQueue.main.safeAsync { - guard let sSelf = self, imageURL == sSelf.kf_webURL else { + guard let strongBase = base, imageURL == self.webURL else { return } - - sSelf.kf_setImageTask(nil) - + self.setImageTask(nil) if image != nil { - sSelf.image = image + strongBase.image = image } - completionHandler?(image, error, cacheType, imageURL) } }) - - kf_setImageTask(task) + + setImageTask(task) return task } - -} - - -// MARK: - Associated Object -private var lastURLKey: Void? -private var imageTaskKey: Void? - -extension NSButton { - /// Get the image URL binded to this image view. - public var kf_webURL: URL? { - return objc_getAssociatedObject(self, &lastURLKey) as? URL - } - - fileprivate func kf_setWebURL(_ url: URL) { - objc_setAssociatedObject(self, &lastURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - - fileprivate var kf_imageTask: RetrieveImageTask? { - return objc_getAssociatedObject(self, &imageTaskKey) as? RetrieveImageTask - } - fileprivate func kf_setImageTask(_ task: RetrieveImageTask?) { - objc_setAssociatedObject(self, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + /** + Cancel the image download task bounded to the image view if it is running. + Nothing will happen if the downloading has already finished. + */ + public func cancelImageDownloadTask() { + imageTask?.downloadTask?.cancel() } -} - -/** - * Set alternate image to use from web. - */ -extension NSButton { - + /** Set an alternateImage with a resource, a placeholder image, options, progress handler and completion handler. - + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. - parameter placeholder: A placeholder image when retrieving the image at URL. - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. - parameter progressBlock: Called when the image downloading progress gets updated. - parameter completionHandler: Called when the image retrieved and set. - + - returns: A task represents the retrieving process. - + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. */ @discardableResult - public func kf_setAlternateImage(with resource: Resource?, - placeholder: Image? = nil, - options: KingfisherOptionsInfo? = nil, - progressBlock: DownloadProgressBlock? = nil, - completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + public func setAlternateImage(with resource: Resource?, + placeholder: Image? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask { - alternateImage = placeholder + base.alternateImage = placeholder guard let resource = resource else { completionHandler?(nil, nil, .none, nil) return .empty } - kf_setAlternateWebURL(resource.downloadURL) - let task = KingfisherManager.shared.retrieveImage(with: resource, options: options, - progressBlock: { receivedSize, totalSize in + setAlternateWebURL(resource.downloadURL) + let task = KingfisherManager.shared.retrieveImage( + with: resource, + options: options, + progressBlock: { receivedSize, totalSize in if let progressBlock = progressBlock { progressBlock(receivedSize, totalSize) } }, - completionHandler: {[weak self] image, error, cacheType, imageURL in + completionHandler: {[weak base] image, error, cacheType, imageURL in DispatchQueue.main.safeAsync { - guard let sSelf = self, imageURL == sSelf.kf_alternateWebURL else { + guard let strongBase = base, imageURL == self.alternateWebURL else { return } - - sSelf.kf_setAlternateImageTask(nil) + self.setAlternateImageTask(nil) guard let image = image else { completionHandler?(nil, error, cacheType, imageURL) return } - sSelf.alternateImage = image + strongBase.alternateImage = image completionHandler?(image, error, cacheType, imageURL) } }) - kf_setImageTask(task) + setAlternateImageTask(task) return task } + + + /// Cancel the alternate image download task bounded to the image view if it is running. + /// Nothing will happen if the downloading has already finished. + public func cancelAlternateImageDownloadTask() { + alternateImageTask?.downloadTask?.cancel() + } } + +// MARK: - Associated Object +private var lastURLKey: Void? +private var imageTaskKey: Void? + private var lastAlternateURLKey: Void? private var alternateImageTaskKey: Void? -// MARK: - Runtime for NSButton alternateImage -extension NSButton { - /** - Get the alternate image URL binded to this button. - */ - - public var kf_alternateWebURL: URL? { - return objc_getAssociatedObject(self, &lastAlternateURLKey) as? URL +extension Kingfisher where Base: NSButton { + /// Get the image URL binded to this image view. + public var webURL: URL? { + return objc_getAssociatedObject(base, &lastURLKey) as? URL } - - fileprivate func kf_setAlternateWebURL(_ url: URL) { - objc_setAssociatedObject(self, &lastAlternateURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + + fileprivate func setWebURL(_ url: URL) { + objc_setAssociatedObject(base, &lastURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } - - fileprivate var kf_alternateImageTask: RetrieveImageTask? { - return objc_getAssociatedObject(self, &alternateImageTaskKey) as? RetrieveImageTask + + fileprivate var imageTask: RetrieveImageTask? { + return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask } - - fileprivate func kf_setAlternateImageTask(_ task: RetrieveImageTask?) { - objc_setAssociatedObject(self, &alternateImageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + + fileprivate func setImageTask(_ task: RetrieveImageTask?) { + objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + + /// Get the alternate image URL binded to this button. + public var alternateWebURL: URL? { + return objc_getAssociatedObject(base, &lastAlternateURLKey) as? URL + } + + fileprivate func setAlternateWebURL(_ url: URL) { + objc_setAssociatedObject(base, &lastAlternateURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + + fileprivate var alternateImageTask: RetrieveImageTask? { + return objc_getAssociatedObject(base, &alternateImageTaskKey) as? RetrieveImageTask + } + + fileprivate func setAlternateImageTask(_ task: RetrieveImageTask?) { + objc_setAssociatedObject(base, &alternateImageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } -// MARK: - Cancel image download tasks. +// MARK: - Deprecated. Only for back compatibility. +/** + * Set image to use from web. Deprecated. Use `kf` namespacing instead. + */ extension NSButton { + /** + Set an image with a resource, a placeholder image, options, progress handler and completion handler. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. + */ + @discardableResult + @available(*, deprecated, + message: "Extensions directly on NSButton are deprecated. Use `button.kf.setImage` instead.", + renamed: "kf.setImage") + public func kf_setImage(with resource: Resource?, + placeholder: Image? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + { + return kf.setImage(with: resource, placeholder: placeholder, options: options, + progressBlock: progressBlock, completionHandler: completionHandler) + } + /** Cancel the image download task bounded to the image view if it is running. Nothing will happen if the downloading has already finished. */ - public func kf_cancelImageDownloadTask() { - kf_imageTask?.downloadTask?.cancel() - } - - public func kf_cancelAlternateImageDownloadTask() { - kf_imageTask?.downloadTask?.cancel() + @available(*, deprecated, + message: "Extensions directly on NSButton are deprecated. Use `button.kf.cancelImageDownloadTask` instead.", + renamed: "kf.cancelImageDownloadTask") + public func kf_cancelImageDownloadTask() { kf.cancelImageDownloadTask() } + + /** + Set an alternateImage with a resource, a placeholder image, options, progress handler and completion handler. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. + */ + @discardableResult + @available(*, deprecated, + message: "Extensions directly on NSButton are deprecated. Use `button.kf.setAlternateImage` instead.", + renamed: "kf.setAlternateImage") + public func kf_setAlternateImage(with resource: Resource?, + placeholder: Image? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + { + return kf.setAlternateImage(with: resource, placeholder: placeholder, options: options, + progressBlock: progressBlock, completionHandler: completionHandler) } + + /// Cancel the alternate image download task bounded to the image view if it is running. + /// Nothing will happen if the downloading has already finished. + @available(*, deprecated, + message: "Extensions directly on NSButton are deprecated. Use `button.kf.cancelAlternateImageDownloadTask` instead.", + renamed: "kf.cancelAlternateImageDownloadTask") + public func kf_cancelAlternateImageDownloadTask() { kf.cancelAlternateImageDownloadTask() } + + + /// Get the image URL binded to this image view. + @available(*, deprecated, + message: "Extensions directly on NSButton are deprecated. Use `button.kf.webURL` instead.", + renamed: "kf.webURL") + public var kf_webURL: URL? { return kf.webURL } + + @available(*, deprecated, message: "Extensions directly on NSButton are deprecated.",renamed: "kf.setWebURL") + fileprivate func kf_setWebURL(_ url: URL) { kf.setWebURL(url) } + + @available(*, deprecated, message: "Extensions directly on NSButton are deprecated.",renamed: "kf.imageTask") + fileprivate var kf_imageTask: RetrieveImageTask? { return kf.imageTask } + + @available(*, deprecated, message: "Extensions directly on UIButton are deprecated.",renamed: "kf.setImageTask") + fileprivate func kf_setImageTask(_ task: RetrieveImageTask?) { kf.setImageTask(task)} + + /// Get the alternate image URL binded to this button. + @available(*, deprecated, + message: "Extensions directly on NSButton are deprecated. Use `button.kf.alternateWebURL` instead.", + renamed: "kf.alternateWebURL") + public var kf_alternateWebURL: URL? { return kf.alternateWebURL } + + @available(*, deprecated, message: "Extensions directly on NSButton are deprecated.",renamed: "kf.setAlternateWebURL") + fileprivate func kf_setAlternateWebURL(_ url: URL) { kf.setAlternateWebURL(url) } + + @available(*, deprecated, message: "Extensions directly on NSButton are deprecated.",renamed: "kf.alternateImageTask") + fileprivate var kf_alternateImageTask: RetrieveImageTask? { return kf.alternateImageTask } + + @available(*, deprecated, message: "Extensions directly on UIButton are deprecated.",renamed: "kf.setAlternateImageTask") + fileprivate func kf_setAlternateImageTask(_ task: RetrieveImageTask?) { kf.setAlternateImageTask(task) } } + diff --git a/Sources/String+MD5.swift b/Sources/String+MD5.swift index 8d601b2f3..e2616fca8 100755 --- a/Sources/String+MD5.swift +++ b/Sources/String+MD5.swift @@ -21,9 +21,15 @@ Permission is granted to anyone to use this software for any purpose,including c import Foundation -extension String { - var kf_MD5: String { - if let data = data(using: .utf8) { +protocol StringType { + func data(using encoding: String.Encoding, allowLossyConversion: Bool) -> Data? +} +extension String: StringType { } +extension String: KingfisherCompatible {} + +extension Kingfisher where Base: StringType { + var md5: Base { + if let data = base.data(using: .utf8, allowLossyConversion: true) { let message = data.withUnsafeBytes { bytes -> [UInt8] in return Array(UnsafeBufferPointer(start: bytes, count: data.count)) @@ -31,15 +37,15 @@ extension String { let MD5Calculator = MD5(message) let MD5Data = MD5Calculator.calculate() - + let MD5String = NSMutableString() for c in MD5Data { MD5String.appendFormat("%02x", c) } - return MD5String as String + return MD5String as! Base } else { - return self + return base } } } diff --git a/Sources/UIButton+Kingfisher.swift b/Sources/UIButton+Kingfisher.swift index f075e5ed9..0f01f818e 100755 --- a/Sources/UIButton+Kingfisher.swift +++ b/Sources/UIButton+Kingfisher.swift @@ -26,229 +26,374 @@ import UIKit +// MARK: - Set Images /** -* Set image to use from web for a specified state. -*/ -extension UIButton { + * Set image to use in button from web for a specified state. + */ +extension Kingfisher where Base: UIButton { /** - Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and completion handler. - - - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. - - parameter state: The state that uses the specified image. - - parameter placeholder: A placeholder image when retrieving the image at URL. - - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. - - parameter progressBlock: Called when the image downloading progress gets updated. - - parameter completionHandler: Called when the image retrieved and set. - - - returns: A task represents the retrieving process. + Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and + completion handler. - - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter state: The state that uses the specified image. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. - */ + */ @discardableResult - public func kf_setImage(with resource: Resource?, - for state: UIControlState, - placeholder: UIImage? = nil, - options: KingfisherOptionsInfo? = nil, - progressBlock: DownloadProgressBlock? = nil, - completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + public func setImage(with resource: Resource?, + for state: UIControlState, + placeholder: UIImage? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask { - setImage(placeholder, for: state) + base.setImage(placeholder, for: state) guard let resource = resource else { completionHandler?(nil, nil, .none, nil) return .empty } - kf_setWebURL(resource.downloadURL, for: state) - let task = KingfisherManager.shared.retrieveImage(with: resource, options: options, + setWebURL(resource.downloadURL, for: state) + let task = KingfisherManager.shared.retrieveImage( + with: resource, + options: options, progressBlock: { receivedSize, totalSize in if let progressBlock = progressBlock { progressBlock(receivedSize, totalSize) } }, - completionHandler: {[weak self] image, error, cacheType, imageURL in + completionHandler: {[weak base] image, error, cacheType, imageURL in DispatchQueue.main.safeAsync { - guard let sSelf = self, imageURL == sSelf.kf_webURL(for: state) else { + guard let strongBase = base, imageURL == self.webURL(for: state) else { return } - - sSelf.kf_setImageTask(nil) + self.setImageTask(nil) if image != nil { - sSelf.setImage(image, for: state) + strongBase.setImage(image, for: state) } - + completionHandler?(image, error, cacheType, imageURL) } }) - kf_setImageTask(task) + setImageTask(task) return task } -} - -private var lastURLKey: Void? -private var imageTaskKey: Void? - -// MARK: - Runtime for UIButton image -extension UIButton { - /** - Get the image URL binded to this button for a specified state. - - - parameter state: The state that uses the specified image. - - - returns: Current URL for image. - */ - public func kf_webURL(for state: UIControlState) -> URL? { - return kf_webURLs[NSNumber(value:state.rawValue)] as? URL - } - - fileprivate func kf_setWebURL(_ url: URL, for state: UIControlState) { - kf_webURLs[NSNumber(value:state.rawValue)] = url - } - - fileprivate var kf_webURLs: NSMutableDictionary { - var dictionary = objc_getAssociatedObject(self, &lastURLKey) as? NSMutableDictionary - if dictionary == nil { - dictionary = NSMutableDictionary() - kf_setWebURLs(dictionary!) - } - return dictionary! - } - fileprivate func kf_setWebURLs(_ URLs: NSMutableDictionary) { - objc_setAssociatedObject(self, &lastURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - - fileprivate var kf_imageTask: RetrieveImageTask? { - return objc_getAssociatedObject(self, &imageTaskKey) as? RetrieveImageTask + /** + Cancel the image download task bounded to the image view if it is running. + Nothing will happen if the downloading has already finished. + */ + public func cancelImageDownloadTask() { + imageTask?.downloadTask?.cancel() } - fileprivate func kf_setImageTask(_ task: RetrieveImageTask?) { - objc_setAssociatedObject(self, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } -} - -/** -* Set background image to use from web for a specified state. -*/ -extension UIButton { /** - Set the background image to use for a specified state with a resource, - a placeholder image, options progress handler and completion handler. - - - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. - - parameter state: The state that uses the specified image. - - parameter placeholder: A placeholder image when retrieving the image at URL. - - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. - - parameter progressBlock: Called when the image downloading progress gets updated. - - parameter completionHandler: Called when the image retrieved and set. - - - returns: A task represents the retrieving process. + Set the background image to use for a specified state with a resource, + a placeholder image, options progress handler and completion handler. - - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter state: The state that uses the specified image. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. - */ + */ @discardableResult - public func kf_setBackgroundImage(with resource: Resource?, - for state: UIControlState, - placeholder: UIImage? = nil, - options: KingfisherOptionsInfo? = nil, - progressBlock: DownloadProgressBlock? = nil, - completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + public func setBackgroundImage(with resource: Resource?, + for state: UIControlState, + placeholder: UIImage? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask { - setBackgroundImage(placeholder, for: state) + base.setBackgroundImage(placeholder, for: state) guard let resource = resource else { completionHandler?(nil, nil, .none, nil) return .empty } - kf_setBackgroundWebURL(resource.downloadURL, for: state) - let task = KingfisherManager.shared.retrieveImage(with: resource, options: options, + setBackgroundWebURL(resource.downloadURL, for: state) + let task = KingfisherManager.shared.retrieveImage( + with: resource, + options: options, progressBlock: { receivedSize, totalSize in if let progressBlock = progressBlock { progressBlock(receivedSize, totalSize) } }, - completionHandler: { [weak self] image, error, cacheType, imageURL in + completionHandler: { [weak base] image, error, cacheType, imageURL in DispatchQueue.main.safeAsync { - guard let sSelf = self, imageURL == sSelf.kf_backgroundWebURL(for: state) else { + guard let strongBase = base, imageURL == self.backgroundWebURL(for: state) else { return } - - sSelf.kf_setBackgroundImageTask(nil) - + self.setBackgroundImageTask(nil) if image != nil { - sSelf.setBackgroundImage(image, for: state) + strongBase.setBackgroundImage(image, for: state) } completionHandler?(image, error, cacheType, imageURL) } }) - kf_setBackgroundImageTask(task) + setBackgroundImageTask(task) return task } + + /** + Cancel the background image download task bounded to the image view if it is running. + Nothing will happen if the downloading has already finished. + */ + public func cancelBackgroundImageDownloadTask() { + backgroundImageTask?.downloadTask?.cancel() + } + } -private var lastBackgroundURLKey: Void? -private var backgroundImageTaskKey: Void? - -// MARK: - Runtime for UIButton background image -extension UIButton { +// MARK: - Associated Object +private var lastURLKey: Void? +private var imageTaskKey: Void? + +extension Kingfisher where Base: UIButton { /** - Get the background image URL binded to this button for a specified state. + Get the image URL binded to this button for a specified state. + + - parameter state: The state that uses the specified image. + + - returns: Current URL for image. + */ + public func webURL(for state: UIControlState) -> URL? { + return webURLs[NSNumber(value:state.rawValue)] as? URL + } - - parameter state: The state that uses the specified background image. + fileprivate func setWebURL(_ url: URL, for state: UIControlState) { + webURLs[NSNumber(value:state.rawValue)] = url + } - - returns: Current URL for background image. - */ - public func kf_backgroundWebURL(for state: UIControlState) -> URL? { - return kf_backgroundWebURLs[NSNumber(value:state.rawValue)] as? URL + fileprivate var webURLs: NSMutableDictionary { + var dictionary = objc_getAssociatedObject(base, &lastURLKey) as? NSMutableDictionary + if dictionary == nil { + dictionary = NSMutableDictionary() + setWebURLs(dictionary!) + } + return dictionary! } - fileprivate func kf_setBackgroundWebURL(_ url: URL, for state: UIControlState) { - kf_backgroundWebURLs[NSNumber(value:state.rawValue)] = url + fileprivate func setWebURLs(_ URLs: NSMutableDictionary) { + objc_setAssociatedObject(base, &lastURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + + fileprivate var imageTask: RetrieveImageTask? { + return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask + } + + fileprivate func setImageTask(_ task: RetrieveImageTask?) { + objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } +} + + +private var lastBackgroundURLKey: Void? +private var backgroundImageTaskKey: Void? + + +extension Kingfisher where Base: UIButton { + /** + Get the background image URL binded to this button for a specified state. + + - parameter state: The state that uses the specified background image. + + - returns: Current URL for background image. + */ + public func backgroundWebURL(for state: UIControlState) -> URL? { + return backgroundWebURLs[NSNumber(value:state.rawValue)] as? URL + } + + fileprivate func setBackgroundWebURL(_ url: URL, for state: UIControlState) { + backgroundWebURLs[NSNumber(value:state.rawValue)] = url } - fileprivate var kf_backgroundWebURLs: NSMutableDictionary { - var dictionary = objc_getAssociatedObject(self, &lastBackgroundURLKey) as? NSMutableDictionary + fileprivate var backgroundWebURLs: NSMutableDictionary { + var dictionary = objc_getAssociatedObject(base, &lastBackgroundURLKey) as? NSMutableDictionary if dictionary == nil { dictionary = NSMutableDictionary() - kf_setBackgroundWebURLs(dictionary!) + setBackgroundWebURLs(dictionary!) } return dictionary! } - fileprivate func kf_setBackgroundWebURLs(_ URLs: NSMutableDictionary) { - objc_setAssociatedObject(self, &lastBackgroundURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + fileprivate func setBackgroundWebURLs(_ URLs: NSMutableDictionary) { + objc_setAssociatedObject(base, &lastBackgroundURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } - fileprivate var kf_backgroundImageTask: RetrieveImageTask? { - return objc_getAssociatedObject(self, &backgroundImageTaskKey) as? RetrieveImageTask + fileprivate var backgroundImageTask: RetrieveImageTask? { + return objc_getAssociatedObject(base, &backgroundImageTaskKey) as? RetrieveImageTask } - fileprivate func kf_setBackgroundImageTask(_ task: RetrieveImageTask?) { - objc_setAssociatedObject(self, &backgroundImageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + fileprivate func setBackgroundImageTask(_ task: RetrieveImageTask?) { + objc_setAssociatedObject(base, &backgroundImageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } -// MARK: - Cancel image download tasks. +// MARK: - Deprecated. Only for back compatibility. +/** +* Set image to use from web for a specified state. Deprecated. Use `kf` namespacing instead. +*/ extension UIButton { + /** + Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and + completion handler. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter state: The state that uses the specified image. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. + */ + @discardableResult + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated. Use `button.kf.setImage` instead.", + renamed: "kf.setImage") + public func kf_setImage(with resource: Resource?, + for state: UIControlState, + placeholder: UIImage? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + { + return kf.setImage(with: resource, for: state, placeholder: placeholder, options: options, + progressBlock: progressBlock, completionHandler: completionHandler) + } + /** Cancel the image download task bounded to the image view if it is running. Nothing will happen if the downloading has already finished. */ - public func kf_cancelImageDownloadTask() { - kf_imageTask?.downloadTask?.cancel() + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated. Use `button.kf.cancelImageDownloadTask` instead.", + renamed: "kf.cancelImageDownloadTask") + public func kf_cancelImageDownloadTask() { kf.cancelImageDownloadTask() } + + /** + Set the background image to use for a specified state with a resource, + a placeholder image, options progress handler and completion handler. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter state: The state that uses the specified image. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. + */ + @discardableResult + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated. Use `button.kf.setBackgroundImage` instead.", + renamed: "kf.setBackgroundImage") + public func kf_setBackgroundImage(with resource: Resource?, + for state: UIControlState, + placeholder: UIImage? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + { + return kf.setBackgroundImage(with: resource, for: state, placeholder: placeholder, options: options, + progressBlock: progressBlock, completionHandler: completionHandler) } /** Cancel the background image download task bounded to the image view if it is running. Nothing will happen if the downloading has already finished. */ - public func kf_cancelBackgroundImageDownloadTask() { - kf_backgroundImageTask?.downloadTask?.cancel() + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated. Use `button.kf.cancelBackgroundImageDownloadTask` instead.", + renamed: "kf.cancelBackgroundImageDownloadTask") + public func kf_cancelBackgroundImageDownloadTask() { kf.cancelBackgroundImageDownloadTask() } + + /** + Get the image URL binded to this button for a specified state. + + - parameter state: The state that uses the specified image. + + - returns: Current URL for image. + */ + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated. Use `button.kf.webURL` instead.", + renamed: "kf.webURL") + public func kf_webURL(for state: UIControlState) -> URL? { return kf.webURL(for: state) } + + @available(*, deprecated, message: "Extensions directly on UIButton are deprecated.",renamed: "kf.setWebURL") + fileprivate func kf_setWebURL(_ url: URL, for state: UIControlState) { kf.setWebURL(url, for: state) } + + @available(*, deprecated, message: "Extensions directly on UIButton are deprecated.",renamed: "kf.webURLs") + fileprivate var kf_webURLs: NSMutableDictionary { return kf.webURLs } + + @available(*, deprecated, message: "Extensions directly on UIButton are deprecated.",renamed: "kf.setWebURLs") + fileprivate func kf_setWebURLs(_ URLs: NSMutableDictionary) { kf.setWebURLs(URLs) } + + @available(*, deprecated, message: "Extensions directly on UIButton are deprecated.",renamed: "kf.imageTask") + fileprivate var kf_imageTask: RetrieveImageTask? { return kf.imageTask } + + @available(*, deprecated, message: "Extensions directly on UIButton are deprecated.",renamed: "kf.setImageTask") + fileprivate func kf_setImageTask(_ task: RetrieveImageTask?) { kf.setImageTask(task) } + + /** + Get the background image URL binded to this button for a specified state. + + - parameter state: The state that uses the specified background image. + + - returns: Current URL for background image. + */ + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated. Use `button.kf.backgroundWebURL` instead.", + renamed: "kf.backgroundWebURL") + public func kf_backgroundWebURL(for state: UIControlState) -> URL? { return kf.backgroundWebURL(for: state) } + + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated.",renamed: "kf.setBackgroundWebURL") + fileprivate func kf_setBackgroundWebURL(_ url: URL, for state: UIControlState) { + kf.setBackgroundWebURL(url, for: state) } + + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated.",renamed: "kf.backgroundWebURLs") + fileprivate var kf_backgroundWebURLs: NSMutableDictionary { return kf.backgroundWebURLs } + + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated.",renamed: "kf.setBackgroundWebURLs") + fileprivate func kf_setBackgroundWebURLs(_ URLs: NSMutableDictionary) { kf.setBackgroundWebURLs(URLs) } + + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated.",renamed: "kf.backgroundImageTask") + fileprivate var kf_backgroundImageTask: RetrieveImageTask? { return kf.backgroundImageTask } + + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated.",renamed: "kf.setBackgroundImageTask") + fileprivate func kf_setBackgroundImageTask(_ task: RetrieveImageTask?) { return kf.setBackgroundImageTask(task) } + } diff --git a/Tests/KingfisherTests/ImageExtensionTests.swift b/Tests/KingfisherTests/ImageExtensionTests.swift index 48f2a3f0a..f22b44df6 100755 --- a/Tests/KingfisherTests/ImageExtensionTests.swift +++ b/Tests/KingfisherTests/ImageExtensionTests.swift @@ -42,67 +42,67 @@ class ImageExtensionTests: XCTestCase { func testImageFormat() { var format: ImageFormat - format = testImageJEPGData.kf_imageFormat + format = testImageJEPGData.kf.imageFormat XCTAssertEqual(format, ImageFormat.JPEG) - format = testImagePNGData.kf_imageFormat + format = testImagePNGData.kf.imageFormat XCTAssertEqual(format, ImageFormat.PNG) - format = testImageGIFData.kf_imageFormat + format = testImageGIFData.kf.imageFormat XCTAssertEqual(format, ImageFormat.GIF) let raw: [UInt8] = [1, 2, 3, 4, 5, 6, 7, 8] - format = Data(bytes: raw) .kf_imageFormat + format = Data(bytes: raw).kf.imageFormat XCTAssertEqual(format, ImageFormat.unknown) } func testGenerateGIFImage() { - let image = Image.kf_animated(with: testImageGIFData, preloadAll: false) + let image = Kingfisher.animated(with: testImageGIFData, preloadAll: false) XCTAssertNotNil(image, "The image should be initiated.") #if os(iOS) || os(tvOS) - let count = CGImageSourceGetCount(image!.kf_imageSource!.imageRef!) + let count = CGImageSourceGetCount(image!.kf.imageSource!.imageRef!) XCTAssertEqual(count, 8, "There should be 8 frames.") #else - XCTAssertEqual(image!.kf_images!.count, 8, "There should be 8 frames.") + XCTAssertEqual(image!.kf.images!.count, 8, "There should be 8 frames.") - XCTAssertEqualWithAccuracy(image!.kf_duration, 0.8, accuracy: 0.001, "The image duration should be 0.8s") + XCTAssertEqualWithAccuracy(image!.kf.duration, 0.8, accuracy: 0.001, "The image duration should be 0.8s") #endif } func testGIFRepresentation() { - let image = Image.kf_animated(with: testImageGIFData, preloadAll: false)! - let data = image.gifRepresentation() + let image = Kingfisher.animated(with: testImageGIFData, preloadAll: false)! + let data = image.kf.gifRepresentation() XCTAssertNotNil(data, "Data should not be nil") - XCTAssertEqual(data?.kf_imageFormat, ImageFormat.GIF) + XCTAssertEqual(data?.kf.imageFormat, ImageFormat.GIF) - let allLoadImage = Image.kf_animated(with: data!, preloadAll: true)! - let allLoadData = allLoadImage.gifRepresentation() + let allLoadImage = Kingfisher.animated(with: data!, preloadAll: true)! + let allLoadData = allLoadImage.kf.gifRepresentation() XCTAssertNotNil(allLoadData, "Data1 should not be nil") - XCTAssertEqual(allLoadData?.kf_imageFormat, ImageFormat.GIF) + XCTAssertEqual(allLoadData?.kf.imageFormat, ImageFormat.GIF) } func testGenerateSingleFrameGIFImage() { - let image = Image.kf_animated(with: testImageSingleFrameGIFData, preloadAll: false) + let image = Kingfisher.animated(with: testImageSingleFrameGIFData, preloadAll: false) XCTAssertNotNil(image, "The image should be initiated.") #if os(iOS) || os(tvOS) - let count = CGImageSourceGetCount(image!.kf_imageSource!.imageRef!) + let count = CGImageSourceGetCount(image!.kf.imageSource!.imageRef!) XCTAssertEqual(count, 1, "There should be 1 frames.") #else - XCTAssertEqual(image!.kf_images!.count, 1, "There should be 1 frames.") + XCTAssertEqual(image!.kf.images!.count, 1, "There should be 1 frames.") - XCTAssertEqual(image!.kf_duration, Double.infinity, "The image duration should be 0 since it is not animated image.") + XCTAssertEqual(image!.kf.duration, Double.infinity, "The image duration should be 0 since it is not animated image.") #endif } func testPreloadAllGIFData() { - let image = Image.kf_animated(with: testImageSingleFrameGIFData, preloadAll: true)! + let image = Kingfisher.animated(with: testImageSingleFrameGIFData, preloadAll: true)! XCTAssertNotNil(image, "The image should be initiated.") #if os(iOS) || os(tvOS) - XCTAssertNil(image.kf_imageSource, "Image source should be nil") + XCTAssertNil(image.kf.imageSource, "Image source should be nil") #endif - XCTAssertEqual(image.kf_duration, image.kf_duration) - XCTAssertEqual(image.kf_images!.count, image.kf_images!.count) + XCTAssertEqual(image.kf.duration, image.kf.duration) + XCTAssertEqual(image.kf.images!.count, image.kf.images!.count) } } diff --git a/Tests/KingfisherTests/ImageViewExtensionTests.swift b/Tests/KingfisherTests/ImageViewExtensionTests.swift index 7037d7a92..ff3e4f0c6 100755 --- a/Tests/KingfisherTests/ImageViewExtensionTests.swift +++ b/Tests/KingfisherTests/ImageViewExtensionTests.swift @@ -68,7 +68,7 @@ class ImageViewExtensionTests: XCTestCase { var progressBlockIsCalled = false - imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in progressBlockIsCalled = true XCTAssertTrue(Thread.isMainThread) }) { (image, error, cacheType, imageURL) -> () in @@ -78,7 +78,7 @@ class ImageViewExtensionTests: XCTestCase { 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(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.") XCTAssertTrue(Thread.isMainThread) @@ -95,7 +95,7 @@ class ImageViewExtensionTests: XCTestCase { let url = URL(string: URLString)! let customQueue = DispatchQueue(label: "com.kingfisher.testQueue") - imageView.kf_setImage(with: url, placeholder: nil, options: [.callbackDispatchQueue(customQueue)], progressBlock: { (receivedSize, totalSize) -> () in + imageView.kf.setImage(with: url, placeholder: nil, options: [.callbackDispatchQueue(customQueue)], progressBlock: { (receivedSize, totalSize) -> () in XCTAssertTrue(Thread.isMainThread) }) { (image, error, cacheType, imageURL) -> () in XCTAssertTrue(Thread.isMainThread, "The image extension callback should be always in main queue.") @@ -117,7 +117,7 @@ class ImageViewExtensionTests: XCTestCase { cleanDefaultCache() - _ = imageView.kf_setImage(with: resource, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + _ = imageView.kf.setImage(with: resource, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in progressBlockIsCalled = true }) { (image, error, cacheType, imageURL) -> () in expectation.fulfill() @@ -126,7 +126,7 @@ class ImageViewExtensionTests: XCTestCase { 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(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. But now is: \(cacheType)") } @@ -144,7 +144,7 @@ class ImageViewExtensionTests: XCTestCase { var progressBlockIsCalled = false var completionBlockIsCalled = false - let task = imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + let task = imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in progressBlockIsCalled = true }) { (image, error, cacheType, imageURL) -> () in completionBlockIsCalled = true @@ -173,7 +173,7 @@ class ImageViewExtensionTests: XCTestCase { cleanDefaultCache() - let task = imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + let task = imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in progressBlockIsCalled = true }) { (image, error, cacheType, imageURL) -> () in XCTAssertNotNil(error) @@ -206,20 +206,20 @@ class ImageViewExtensionTests: XCTestCase { var task2Completion = false var task3Completion = false - let task1 = imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + let task1 = imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in }) { (image, error, cacheType, imageURL) -> () in task1Completion = true } - let _ = imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + let _ = imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in }) { (image, error, cacheType, imageURL) -> () in XCTAssertNotNil(image) task2Completion = true } - let _ = imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + let _ = imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in }) { (image, error, cacheType, imageURL) -> () in XCTAssertNotNil(image) @@ -252,21 +252,21 @@ class ImageViewExtensionTests: XCTestCase { var task2Completion = false var task3Completion = false - let task1 = imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + let task1 = imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in }) { (image, error, cacheType, imageURL) -> () in XCTAssertNotNil(image) task1Completion = true } - let _ = imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + let _ = imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in }) { (image, error, cacheType, imageURL) -> () in XCTAssertNotNil(image) task2Completion = true } - let _ = imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + let _ = imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in }) { (image, error, cacheType, imageURL) -> () in XCTAssertNotNil(image) @@ -299,7 +299,7 @@ class ImageViewExtensionTests: XCTestCase { var task2Completion = false var task3Completion = false - let task1 = imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + let task1 = imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in }) { (image, error, cacheType, imageURL) -> () in XCTAssertNotNil(error) @@ -307,7 +307,7 @@ class ImageViewExtensionTests: XCTestCase { task1Completion = true } - let task2 = imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + let task2 = imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in }) { (image, error, cacheType, imageURL) -> () in XCTAssertNotNil(error) @@ -315,7 +315,7 @@ class ImageViewExtensionTests: XCTestCase { task2Completion = true } - let task3 = imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + let task3 = imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in }) { (image, error, cacheType, imageURL) -> () in XCTAssertNotNil(error) @@ -354,7 +354,7 @@ class ImageViewExtensionTests: XCTestCase { _ = stubRequest("GET", URLString).andReturn(200)?.withBody(testImageData) let url = URL(string: URLString)! - imageView.kf_setImage(with: url, placeholder: nil, options: [.targetCache(cache1)], progressBlock: { (receivedSize, totalSize) -> () in + imageView.kf.setImage(with: url, placeholder: nil, options: [.targetCache(cache1)], progressBlock: { (receivedSize, totalSize) -> () in }) { (image, error, cacheType, imageURL) -> () in @@ -362,7 +362,7 @@ class ImageViewExtensionTests: XCTestCase { XCTAssertFalse(cache2.isImageCached(forKey: URLString).cached, "This image should not be cached in cache2.") XCTAssertFalse(KingfisherManager.shared.cache.isImageCached(forKey: URLString).cached, "This image should not be cached in default cache.") - self.imageView.kf_setImage(with: url, placeholder: nil, options: [.targetCache(cache2)], progressBlock: { (receivedSize, totalSize) -> () in + self.imageView.kf.setImage(with: url, placeholder: nil, options: [.targetCache(cache2)], progressBlock: { (receivedSize, totalSize) -> () in }, completionHandler: { (image, error, cacheType, imageURL) -> () in @@ -383,17 +383,17 @@ class ImageViewExtensionTests: XCTestCase { } func testIndicatorViewExisting() { - imageView.kf_indicatorType = .activity - XCTAssertNotNil(imageView.kf_indicator, "The indicator should exist when indicatorType is different than .none") - XCTAssertTrue(imageView.kf_indicator is ActivityIndicator) + imageView.kf.indicatorType = .activity + XCTAssertNotNil(imageView.kf.indicator, "The indicator should exist when indicatorType is different than .none") + XCTAssertTrue(imageView.kf.indicator is ActivityIndicator) - imageView.kf_indicatorType = .none - XCTAssertNil(imageView.kf_indicator, "The indicator should be removed when indicatorType is .none") + imageView.kf.indicatorType = .none + XCTAssertNil(imageView.kf.indicator, "The indicator should be removed when indicatorType is .none") } func testActivityIndicatorViewAnimating() { - imageView.kf_indicatorType = .activity + imageView.kf.indicatorType = .activity let expectation = self.expectation(description: "wait for downloading image") @@ -401,14 +401,14 @@ class ImageViewExtensionTests: XCTestCase { _ = stubRequest("GET", URLString).andReturn(200)?.withBody(testImageData) let url = URL(string: URLString)! - imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in - let indicator = self.imageView.kf_indicator + let indicator = self.imageView.kf.indicator XCTAssertNotNil(indicator, "The indicator view should exist when showIndicatorWhenLoading is true") XCTAssertFalse(indicator!.view.isHidden, "The indicator should be shown and animating when loading") }) { (image, error, cacheType, imageURL) -> () in - let indicator = self.imageView.kf_indicator + let indicator = self.imageView.kf.indicator XCTAssertTrue(indicator!.view.isHidden, "The indicator should stop and hidden after loading") expectation.fulfill() } @@ -418,9 +418,9 @@ class ImageViewExtensionTests: XCTestCase { func testCanUseImageIndicatorViewAnimating() { - imageView.kf_indicatorType = .image(imageData: testImageData as! Data) - XCTAssertTrue(imageView.kf_indicator is ImageIndicator) - let image = (imageView.kf_indicator?.view as? ImageView)?.image + imageView.kf.indicatorType = .image(imageData: testImageData as! Data) + XCTAssertTrue(imageView.kf.indicator is ImageIndicator) + let image = (imageView.kf.indicator?.view as? ImageView)?.image XCTAssertNotNil(image) XCTAssertTrue(image!.renderEqual(to: testImage)) @@ -430,14 +430,14 @@ class ImageViewExtensionTests: XCTestCase { _ = stubRequest("GET", URLString).andReturn(200)?.withBody(testImageData) let url = URL(string: URLString)! - imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in - let indicator = self.imageView.kf_indicator + let indicator = self.imageView.kf.indicator XCTAssertNotNil(indicator, "The indicator view should exist when showIndicatorWhenLoading is true") XCTAssertFalse(indicator!.view.isHidden, "The indicator should be shown and animating when loading") }) { (image, error, cacheType, imageURL) -> () in - let indicator = self.imageView.kf_indicator + let indicator = self.imageView.kf.indicator XCTAssertTrue(indicator!.view.isHidden, "The indicator should stop and hidden after loading") expectation.fulfill() } @@ -452,7 +452,7 @@ class ImageViewExtensionTests: XCTestCase { let stub = stubRequest("GET", URLString).andReturn(200)?.withBody(testImageData)?.delay() let url = URL(string: URLString)! - imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in XCTFail("Progress block should not be called.") }) { (image, error, cacheType, imageURL) -> () in XCTAssertNotNil(error) @@ -462,7 +462,7 @@ class ImageViewExtensionTests: XCTestCase { } DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(Double(NSEC_PER_SEC) * 0.1)) / Double(NSEC_PER_SEC)) { () -> Void in - self.imageView.kf_cancelDownloadTask() + self.imageView.kf.cancelDownloadTask() _ = stub!.go() } @@ -480,7 +480,7 @@ class ImageViewExtensionTests: XCTestCase { var task1Complete = false var task2Complete = false - imageView.kf_setImage(with: URLs[0], placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + imageView.kf.setImage(with: URLs[0], placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in }) { (image, error, cacheType, imageURL) -> () in task1Complete = true @@ -489,7 +489,7 @@ class ImageViewExtensionTests: XCTestCase { XCTAssertNotEqual(self.imageView.image, image) } - self.imageView.kf_setImage(with: URLs[1], placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + self.imageView.kf.setImage(with: URLs[1], placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in }) { (image, error, cacheType, imageURL) -> () in task2Complete = true @@ -512,7 +512,7 @@ class ImageViewExtensionTests: XCTestCase { let expectation = self.expectation(description: "wait for downloading image") let url: URL? = nil - imageView.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in XCTFail("Progress block should not be called.") }) { (image, error, cacheType, imageURL) -> () in XCTAssertNil(image) diff --git a/Tests/KingfisherTests/KingfisherTestHelper.swift b/Tests/KingfisherTests/KingfisherTestHelper.swift index 4b93148ba..b7c4b7177 100755 --- a/Tests/KingfisherTests/KingfisherTestHelper.swift +++ b/Tests/KingfisherTests/KingfisherTestHelper.swift @@ -34,8 +34,8 @@ var testImage: Image = Image(data: testImageData! as Data)! let testImageData = NSData(base64Encoded: testImageString, options: []) -let testImagePNGData = testImage.pngRepresentation()! -let testImageJEPGData = testImage.jpegRepresentation(compressionQuality: 1.0)! +let testImagePNGData = testImage.kf.pngRepresentation()! +let testImageJEPGData = testImage.kf.jpegRepresentation(compressionQuality: 1.0)! let testImageGIFData = try! Data(contentsOf: URL(fileURLWithPath: Bundle(for: ImageExtensionTests.self).path(forResource: "dancing-banana", ofType: "gif")!)) let testImageSingleFrameGIFData = try! Data(contentsOf: URL(fileURLWithPath: Bundle(for: ImageExtensionTests.self).path(forResource: "single-frame", ofType: "gif")!)) @@ -64,11 +64,11 @@ extension Image { func renderEqual(to image: Image, withinTolerance tolerance: UInt8 = 3) -> Bool { guard size == image.size else { return false } - guard let imageData1 = pngRepresentation(), let imageData2 = image.pngRepresentation() else { return false } + guard let imageData1 = kf.pngRepresentation(), let imageData2 = image.kf.pngRepresentation() else { return false } guard let unifiedImage1 = Image(data: imageData1), let unifiedImage2 = Image(data: imageData2) else { return false } guard let rendered1 = unifiedImage1.rendered(), let rendered2 = unifiedImage2.rendered() else { return false } - guard let data1 = rendered1.cgImage?.dataProvider?.data, let data2 = rendered2.cgImage?.dataProvider?.data else { return false } + guard let data1 = rendered1.kf.cgImage?.dataProvider?.data, let data2 = rendered2.kf.cgImage?.dataProvider?.data else { return false } let length1 = CFDataGetLength(data1) let length2 = CFDataGetLength(data2) @@ -92,7 +92,7 @@ extension Image { func rendered() -> Image? { // Ignore non CG images - guard let cgImage = cgImage else { + guard let cgImage = kf.cgImage else { return nil } @@ -128,7 +128,7 @@ extension Image { context.draw(cgImage, in: CGRect(origin: CGPoint.zero, size: size)) #if os(macOS) - return context.makeImage().flatMap { Image(cgImage: $0, size: kf_size) } + return context.makeImage().flatMap { Image(cgImage: $0, size: kf.size) } #else return context.makeImage().flatMap { Image(cgImage: $0) } #endif @@ -146,7 +146,7 @@ extension Image { let path = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).last! let p = ((path) as NSString).appendingPathComponent(name) print(p) - try! pngRepresentation()?.write(to: URL(fileURLWithPath: p)) + try! kf.pngRepresentation()?.write(to: URL(fileURLWithPath: p)) return p } } diff --git a/Tests/KingfisherTests/NSButtonExtensionTests.swift b/Tests/KingfisherTests/NSButtonExtensionTests.swift index c8bdb371c..cd2f0040b 100644 --- a/Tests/KingfisherTests/NSButtonExtensionTests.swift +++ b/Tests/KingfisherTests/NSButtonExtensionTests.swift @@ -71,7 +71,7 @@ class NSButtonExtensionTests: XCTestCase { cleanDefaultCache() - button.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + button.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in progressBlockIsCalled = true }) { (image, error, cacheType, imageURL) -> () in expectation.fulfill() @@ -80,7 +80,7 @@ class NSButtonExtensionTests: XCTestCase { XCTAssert(image != nil, "Downloaded image should exist.") XCTAssert(image! == testImage, "Downloaded image should be the same as test image.") XCTAssert(self.button.image! == testImage, "Downloaded image should be already set to the image for state") - XCTAssert(self.button.kf_webURL == imageURL, "Web URL should equal to the downloaded url.") + XCTAssert(self.button.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. But now is: \(cacheType)") } waitForExpectations(timeout: 5, handler: nil) @@ -94,7 +94,7 @@ class NSButtonExtensionTests: XCTestCase { let url = URL(string: URLString)! var progressBlockIsCalled = false - button.kf_setAlternateImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + button.kf.setAlternateImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in progressBlockIsCalled = true }) { (image, error, cacheType, imageURL) -> () in expectation.fulfill() @@ -103,7 +103,7 @@ class NSButtonExtensionTests: XCTestCase { XCTAssert(image != nil, "Downloaded image should exist.") XCTAssert(image! == testImage, "Downloaded image should be the same as test image.") XCTAssert(self.button.alternateImage! == testImage, "Downloaded image should be already set to the image for state") - XCTAssert(self.button.kf_alternateWebURL == imageURL, "Web URL should equal to the downloaded url.") + XCTAssert(self.button.kf.alternateWebURL == imageURL, "Web URL should equal to the downloaded url.") XCTAssert(cacheType == .none, "cacheType should be .None since the image was just downloaded.") } @@ -117,7 +117,7 @@ class NSButtonExtensionTests: XCTestCase { let stub = stubRequest("GET", URLString).andReturn(200)?.withBody(testImageData)?.delay() let url = URL(string: URLString)! - button.kf_setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + button.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in }) { (image, error, cacheType, imageURL) -> () in XCTAssertNotNil(error) @@ -127,7 +127,7 @@ class NSButtonExtensionTests: XCTestCase { } DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { - self.button.kf_cancelImageDownloadTask() + self.button.kf.cancelImageDownloadTask() _ = stub!.go() } @@ -141,7 +141,7 @@ class NSButtonExtensionTests: XCTestCase { let stub = stubRequest("GET", URLString).andReturn(200)?.withBody(testImageData)?.delay() let url = URL(string: URLString)! - _ = button.kf_setAlternateImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + _ = button.kf.setAlternateImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in XCTFail("Progress block should not be called.") }) { (image, error, cacheType, imageURL) -> () in XCTAssertNotNil(error) @@ -151,7 +151,7 @@ class NSButtonExtensionTests: XCTestCase { } DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { - self.button.kf_cancelAlternateImageDownloadTask() + self.button.kf.cancelAlternateImageDownloadTask() _ = stub!.go() } @@ -162,7 +162,7 @@ class NSButtonExtensionTests: XCTestCase { let expectation = self.expectation(description: "wait for downloading image") let url: URL? = nil - button.kf_setAlternateImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + button.kf.setAlternateImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in XCTFail("Progress block should not be called.") }) { (image, error, cacheType, imageURL) -> () in XCTAssertNil(image) diff --git a/Tests/KingfisherTests/StringExtensionTests.swift b/Tests/KingfisherTests/StringExtensionTests.swift index c62c4f800..d258eff8f 100644 --- a/Tests/KingfisherTests/StringExtensionTests.swift +++ b/Tests/KingfisherTests/StringExtensionTests.swift @@ -23,7 +23,7 @@ class StringExtensionTests: XCTestCase { func testStringMD5() { let s = "hello" - XCTAssertEqual(s.kf_MD5, "5d41402abc4b2a76b9719d911017c592") + XCTAssertEqual(s.kf.md5, "5d41402abc4b2a76b9719d911017c592") } } diff --git a/Tests/KingfisherTests/UIButtonExtensionTests.swift b/Tests/KingfisherTests/UIButtonExtensionTests.swift index a6cb24208..7ca27158a 100755 --- a/Tests/KingfisherTests/UIButtonExtensionTests.swift +++ b/Tests/KingfisherTests/UIButtonExtensionTests.swift @@ -71,7 +71,7 @@ class UIButtonExtensionTests: XCTestCase { cleanDefaultCache() - button.kf_setImage(with: url, for: .highlighted, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + button.kf.setImage(with: url, for: .highlighted, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in progressBlockIsCalled = true }) { (image, error, cacheType, imageURL) -> () in expectation.fulfill() @@ -80,7 +80,7 @@ class UIButtonExtensionTests: XCTestCase { XCTAssert(image != nil, "Downloaded image should exist.") XCTAssert(image! == testImage, "Downloaded image should be the same as test image.") XCTAssert(self.button.image(for: UIControlState.highlighted)! == testImage, "Downloaded image should be already set to the image for state") - XCTAssert(self.button.kf_webURL(for: .highlighted) == imageURL, "Web URL should equal to the downloaded url.") + XCTAssert(self.button.kf.webURL(for: .highlighted) == imageURL, "Web URL should equal to the downloaded url.") XCTAssert(cacheType == .none, "The cache type should be none here. This image was just downloaded. But now is: \(cacheType)") } waitForExpectations(timeout: 5, handler: nil) @@ -94,7 +94,7 @@ class UIButtonExtensionTests: XCTestCase { let url = Foundation.URL(string: URLString)! var progressBlockIsCalled = false - button.kf_setBackgroundImage(with: url, for: .normal, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + button.kf.setBackgroundImage(with: url, for: .normal, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in progressBlockIsCalled = true }) { (image, error, cacheType, imageURL) -> () in expectation.fulfill() @@ -103,7 +103,7 @@ class UIButtonExtensionTests: XCTestCase { XCTAssert(image != nil, "Downloaded image should exist.") XCTAssert(image! == testImage, "Downloaded image should be the same as test image.") XCTAssert(self.button.backgroundImage(for: .normal)! == testImage, "Downloaded image should be already set to the image for state") - XCTAssert(self.button.kf_backgroundWebURL(for: .normal) == imageURL, "Web URL should equal to the downloaded url.") + XCTAssert(self.button.kf.backgroundWebURL(for: .normal) == imageURL, "Web URL should equal to the downloaded url.") XCTAssert(cacheType == .none, "cacheType should be .None since the image was just downloaded.") } @@ -117,7 +117,7 @@ class UIButtonExtensionTests: XCTestCase { let stub = stubRequest("GET", URLString).andReturn(200)?.withBody(testImageData)?.delay() let url = URL(string: URLString)! - button.kf_setImage(with: url, for: UIControlState.highlighted, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + button.kf.setImage(with: url, for: UIControlState.highlighted, placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in XCTFail("Progress block should not be called.") }) { (image, error, cacheType, imageURL) -> () in XCTAssertNotNil(error) @@ -126,7 +126,7 @@ class UIButtonExtensionTests: XCTestCase { expectation.fulfill() } DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(Double(NSEC_PER_SEC) * 0.1)) / Double(NSEC_PER_SEC)) { () -> Void in - self.button.kf_cancelImageDownloadTask() + self.button.kf.cancelImageDownloadTask() _ = stub!.go() } @@ -140,7 +140,7 @@ class UIButtonExtensionTests: XCTestCase { let stub = stubRequest("GET", URLString).andReturn(200)?.withBody(testImageData)?.delay() let url = URL(string: URLString)! - button.kf_setBackgroundImage(with: url, for: UIControlState(), placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + button.kf.setBackgroundImage(with: url, for: UIControlState(), placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in XCTFail("Progress block should not be called.") }) { (image, error, cacheType, imageURL) -> () in XCTAssertNotNil(error) @@ -149,7 +149,7 @@ class UIButtonExtensionTests: XCTestCase { expectation.fulfill() } DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(Double(NSEC_PER_SEC) * 0.1)) / Double(NSEC_PER_SEC)) { () -> Void in - self.button.kf_cancelBackgroundImageDownloadTask() + self.button.kf.cancelBackgroundImageDownloadTask() _ = stub!.go() } @@ -160,7 +160,7 @@ class UIButtonExtensionTests: XCTestCase { let expectation = self.expectation(description: "wait for downloading image") let url: URL? = nil - button.kf_setBackgroundImage(with: url, for: UIControlState(), placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in + button.kf.setBackgroundImage(with: url, for: UIControlState(), placeholder: nil, options: nil, progressBlock: { (receivedSize, totalSize) -> () in XCTFail("Progress block should not be called.") }) { (image, error, cacheType, imageURL) -> () in XCTAssertNil(image)