-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Cheat Sheet v4
This documentation will describe some most common usage of Kingfisher. The code snippet is based on iOS. However, the similar code should also work for other platforms like macOS, by replacing the corresponding class (such as UIImage
to NSImage
, etc).
Some convenient extensions are supplied in Kingfisher. It covers most used features, and supports all regular image related UI components: UIImageView
, NSImageView
, UIButton
and NSButton
. We recommend to use these methods to keep your code simple.
let url = URL(string: "https://domain.com/image.jpg")!
imageView.kf.setImage(with: url)
Kingfisher will try to get the image from cache first. If not found, download and cache it for later use.
The
absoluteString
ofurl
will be used as the cache key by default.
let resource = ImageResource(downloadURL: url, cacheKey: "my_cache_key")
imageView.kf.setImage(with: resource)
let image = UIImage(named: "default_profile_icon")
imageView.kf.setImage(with: url, placeholder: image)
You could also use a customized
UIView
orNSView
as placeholder. Just make the view type conforming toPlaceholder
:
class MyView: UIView { /* Your implementation of view */ }
extension MyView: Placeholder { /* Just leave it empty */}
imageView.kf.setImage(with: url, placeholder: MyView())
The
MyView
instance will be added to / removed from theimageView
as needed.
imageView.kf.setImage(with: url, completionHandler: {
(image, error, cacheType, imageUrl) in
// image: Image? `nil` means failed
// error: NSError? non-`nil` means failed
// cacheType: CacheType
// .none - Just downloaded
// .memory - Got from memory cache
// .disk - Got from disk cache
// imageUrl: URL of the image
})
imageView.kf.indicatorType = .activity
imageView.kf.setImage(with: url)
let p = Bundle.main.path(forResource: "loader", ofType: "gif")!
let data = try! Data(contentsOf: URL(fileURLWithPath: p))
imageView.kf.indicatorType = .image(imageData: data)
imageView.kf.setImage(with: url)
struct MyIndicator: Indicator {
let view: UIView = UIView()
func startAnimatingView() { view.isHidden = false }
func stopAnimatingView() { view.isHidden = true }
init() {
view.backgroundColor = .red
}
}
let i = MyIndicator()
imageView.kf.indicatorType = .custom(indicator: i)
imageView.kf.setImage(with: url, progressBlock: {
receivedSize, totalSize in
let percentage = (Float(receivedSize) / Float(totalSize)) * 100.0
print("downloading progress: \(percentage)%")
myIndicator.percentage = percentage
})
imageView.kf.setImage(with: url, options: [.transition(.fade(0.2))])
let processor = RoundCornerImageProcessor(cornerRadius: 20)
imageView.kf.setImage(with: url, placeholder: nil, options: [.processor(processor)])
You can also use other processor to get blur/tint/black & white or some other effect.
let processor = BlurImageProcessor(blurRadius: 4) >> RoundCornerImageProcessor(cornerRadius: 20)
imageView.kf.setImage(with: url, placeholder: nil, options: [.processor(processor)])
imageView.kf.setImage(with: url, options: [.forceRefresh])
imageView.kf.setImage(with: url, options: [.onlyFromCache])
let uiButton: UIButton = //...
uiButton.kf.setImage(with: url, for: .normal, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)
uiButton.kf.setBackgroundImage(with: url, for: .normal, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)
let nsButton: NSButton = //...
nsButton.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)
nsButton.kf.setAlternateImage(with: url, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)
Kingfisher is composed with two main component: an ImageDownloader
for downloading images, and an ImageCache
to manipulate the cache. You can use either of them separately.
ImageDownloader.default.downloadImage(with: url, options: [], progressBlock: nil) {
(image, error, url, data) in
print("Downloaded Image: \(image)")
}
let image: UIImage = //...
ImageCache.default.store(image, forKey: "key_for_image")
let anotherImage: UIImage = //...
let imageData = //.. Data from anotherImage
ImageCache.default.store(anotherImage,
original: imageData,
forKey: "key_for_another_image",
toDisk: false)
ImageCache.default.isImageCached(forKey: "key_for_image")
// (cached: true, cacheType: .memory)
ImageCache.default.isImageCached(forKey: "key_for_another_image")
// (cached: true, cacheType: .memory)
// Force quit and relaunch
ImageCache.default.isImageCached(forKey: "key_for_image")
// (cached: true, cacheType: .disk)
ImageCache.default.isImageCached(forKey: "key_for_another_image")
// (cached: false, cacheType: .none)
ImageCache.default.retrieveImage(forKey: "key_for_image", options: nil) {
image, cacheType in
if let image = image {
print("Get image \(image), cacheType: \(cacheType).")
//In this code snippet, the `cacheType` is .disk
} else {
print("Not exist in cache.")
}
}
// From both memory and disk
ImageCache.default.removeImage(forKey: "key_for_image")
// Only from memory
ImageCache.default.removeImage(forKey: "key_for_image", fromDisk: false)
// 50 MB
ImageCache.default.maxDiskCacheSize = 50 * 1024 * 1024
// Default value is 0, which means no limit.
The
default
downloader will be used by image extension methods, if no customized one set.
ImageCache.default.calculateDiskCacheSize { size in
print("Used disk size by bytes: \(size)")
}
// Clear memory cache right away.
cache.clearMemoryCache()
// Clear disk cache. This is an async operation.
cache.clearDiskCache()
// Clean expired or size exceeded disk cache. This is an async operation.
cache.cleanExpiredDiskCache()
Kingfisher will purge the memory cache when received a memory warning, as well as clean the expired and size exceeded cache when needed. Normally there is no need to clean cache yourself. These methods exist in case of you want your users have more control on the cache.
// 3 days
ImageCache.default.maxCachePeriodInSecond = 60 * 60 * 24 * 3
// Default value is 60 * 60 * 24 * 7, which means 1 week.
Set this value to a negative value (like
-1
) will make the disk cache never expiring.
// Set a default path extension
KingfisherManager.shared.cache.pathExtension = "jpg"
Especially useful when using Kingfisher on macOS and wanting to add drag-drop. Most web input fields won't accept files without a path extension (like .jpg).
// 30 second
ImageDownloader.default.downloadTimeout = 30.0
// Default value is 15.
let downloader = ImageDownloader(name: "huge_image_downloader")
downloader.downloadTimeout = 150.0
let cache = ImageCache(name: "longer_cache")
cache.maxDiskCacheSize = 60 * 60 * 24 * 30
imageView.kf.setImage(with: url, options: [.downloader(downloader), .targetCache(cache)])
// In table view data source
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//...
cell.imageView.kf.setImage(with: url)
//...
}
// In table view delegate
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
//...
cell.imageView.kf.cancelDownloadTask()
}
let modifier = AnyModifier { request in
var r = request
r.setValue("", forHTTPHeaderField: "Access-Token")
return r
}
imageView.kf.setImage(with: url, placeholder: nil, options: [.requestModifier(modifier)])
// In ViewController
ImageDownloader.default.authenticationChallengeResponder = self
extension ViewController: AuthenticationChallengeResponsable {
func downloader(_ downloader: ImageDownloader,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
{
// Provide your `AuthChallengeDisposition` and `URLCredential`
let disposition: URLSession.AuthChallengeDisposition = // ..
let credential: URLCredential? = //..
completionHandler(disposition, credential)
}
}
// In ViewController
ImageDownloader.default.delegate = self
extension ViewController: ImageDownloaderDelegate {
func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool {
return code == 200 || code == 202
}
}
An
invalidStatusCode
error will be raised iffalse
is returned in this delegate method.
let imageDownloader: ImageDownloader = //...
// A configuration without persistent storage for caches is
// requsted for downloader working correctly. (Based on `URLSessionConfiguration.ephemeral`)
imageDownloader.sessionConfiguration = //...
ImageProcessor
is more likly a transformer of image. It convert some data to an image or an image to another. You can supply a processor to ImageDownloader
. The downloader will apply it to downloaded data/images, then send the processed images to the image view or cache if needed.
// Just without anything
imageView.kf.setImage(with: url)
// It equals to
imageView.kf.setImage(with: url, options: [.processor(DefaultImageProcessor.default)])
DefaultImageProcessor
converts downloaded data to a corresponded image object. PNG, JPEG and GIF are supported by default.
// Round corner
let processor = RoundCornerImageProcessor(cornerRadius: 20)
// Resizing
let processor = ResizingImageProcessor(referenceSize: CGSize(width: 100, height: 100))
// Cropping
let processor = CroppingImageProcessor(size: CGSize(width: 100, height: 100), anchor: CGPoint(x: 0.5, y: 0.5))
// Blur with a radius
let processor = BlurImageProcessor(blurRadius: 5.0)
// Overlay with a color & fraction
let processor = OverlayImageProcessor(overlay: .red, fraction: 0.7)
// Tint with a color
let processor = TintImageProcessor(tint: .blue)
// Adjust color
let processor = ColorControlsProcessor(brightness: 1.0, contrast: 0.7, saturation: 1.1, inputEV: 0.7)
// Black & White
let processor = BlackWhiteProcessor()
// Blend (iOS)
let processor = BlendImageProcessor(blendMode: .darken, alpha: 1.0, backgroundColor: .lightGray)
// Compositing
let processor = CompositingImageProcessor(compositingOperation: .darken, alpha: 1.0, backgroundColor: .lightGray)
imageView.kf.setImage(with: url, options: [.processor(processor)])
// Blur and then make round corner
let processor = BlurImageProcessor(blurRadius: 5.0) >> RoundCornerImageProcessor(cornerRadius: 20)
imageView.kf.setImage(with: url, options: [.processor(processor)])
// `>>` equals the `append(another:)` method of `ImageProcessor`.
// Above equals to:
let processor = BlurImageProcessor(blurRadius: 5.0).append(RoundCornerImageProcessor(cornerRadius: 20))
Make a type adopting to ImageProcessor
and implement identifier
and process
:
struct WebpProcessor: ImageProcessor {
// `identifier` should be the same for processors with same properties/functionality
// It will be used when storing and retrieving the image to/from cache.
let identifier = "com.yourdomain.webpprocessor"
// Convert input data/image to target image and return it.
func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
switch item {
case .image(let image):
print("already an image")
return image
case .data(let data):
return WebpFramework.createImage(from: webpData)
}
}
}
// Then pass it to the `setImage` methods:
let processor = WebpProcessor()
let url = URL(string: "https://yourdomain.com/example.webp")
imageView.kf.setImage(with: url, options: [.processor(processor)])
If you have a prepared CIFilter
, you can create a processor quickly from it.
struct MyCIFilter: CIImageProcessor {
let identifier = "com.yourdomain.myCIFilter"
let filter = Filter { input in
guard let filter = CIFilter(name: "xxx") else { return nil }
filter.setValue(input, forKey: kCIInputBackgroundImageKey)
return filter.outputImage
}
}
Kingfisher is smart enough to cache and get the processed images if you specified the correct ImageProcessor
in option.
Although the process
method of a ImageProcessor
is used to convert data or original image to another image. The identifier
will be used when caching the processed images.
Without the identifier
, Kingfisher will not be able to tell which is the correct image in cache (Think about you have to store two version of an image from the same url, one should be round cornered and another should be blurred. You need two different cache keys).
When you use the extension methods of Kingfisher, please make sure to pass the ImageProcessor
with options correctly, and Kingfisher will handle other things for you. However, there might be a chance that you need to interact with ImageCache
yourself, for examle, to check whether a processed image already cached or not. In such situation, besides of passing in the key, you also need to supply the identifier
:
let processor = WebpProcessor()
let url = URL(string: "https://yourdomain.com/example.webp")
imageView.kf.setImage(with: url, options: [.processor(processor)])
// Later
ImageCache.default.isImageCached(
forKey: url.cacheKey,
processorIdentifier: processor.identifier)
If you are trying to process a same image with multiple processors, it may be worth to pass .cacheOriginalImage
as an option. This will store the original downloaded image to cache as well:
let p = MyProcessor()
imageView.kf.setImage(with: url, options: [.processor(p), .cacheOriginalImage])
Kingfisher will now try to get the image at url
, apply processor p
on it, then store both original image and the processed one to cache.
When retrieving an image (by ImageView
extension or KingfisherManager
), Kingfisher will firstly check the version exactly matches input processor. If not found, Kingfisher will turn to get the original image and then apply processor on it (and of course store it to cache). By this mean, there is no need to access the Internet and it could bring a great performance. At last, if neither the processed image nor the original image could be found in cache, a downloading will be started and followed a processing phase.
CacheSerializer
will be used to convert some data to an image object for retrieving from disk cache and vice versa for storing to disk cache.
// Just without anything
imageView.kf.setImage(with: url)
// It equals to
imageView.kf.setImage(with: url, options: [.cacheSerializer(DefaultCacheSerializer.default)])
DefaultCacheSerializer
converts cached data to a corresponded image object and vice versa. PNG, JPEG and GIF are supported by default.
If you need to specify what the format of image should be, use FormatIndicatedCacheSerializer
. It provided serializers for all built-in supported format: FormatIndicatedCacheSerializer.png
, FormatIndicatedCacheSerializer.jpeg
and FormatIndicatedCacheSerializer.gif
.
By default (DefaultCacheSerializer
), Kingfisher will respect the input image format and try to keep it unchanged. However, there are some exceptions. A common case is that when you using a RoundCornerImageProcessor
, you may always want the alpha channel. If your original image is jpg, you may want to set the png serializer instead:
let roundCorner = RoundCornerImageProcessor(cornerRadius: 20)
imageView.kf.setImage(with: url,
options: [.processor(roundCorner),
.cacheSerializer(FormatIndicatedCacheSerializer.png)]
)
Make a type adopting to CacheSerializer
and implement data(with:original:)
and image(with:options:)
:
struct WebpCacheSerializer: CacheSerializer {
func data(with image: Image, original: Data?) -> Data? {
return WebpFramework.webpData(of: image)
}
func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? {
return WebpFramework.createImage(from: webpData)
}
}
// Then pass it to the `setImage` methods:
let serializer = WebpCacheSerializer()
let url = URL(string: "https://yourdomain.com/example.webp")
imageView.kf.setImage(with: url, options: [.cacheSerializer(serializer)])
You could prefetch some images and cache them before you display them on the screen. This is useful when you know a list of image resources you know they would probably be shown later.
let urls = ["http://example.com/image1.jpg", "http://example.com/image2.jpg"]
.map { URL(string: $0)! }
let prefetcher = ImagePrefetcher(urls: urls) {
skippedResources, failedResources, completedResources in
print("These resources are prefetched: \(completedResources)")
}
prefetcher.start()
// Later when you need to display these images:
imageView.kf.setImage(with: urls[0])
anotherImageView.kf.setImage(with: urls[1])
In iOS 10, Apple introduced a cell prefetching behavior. It could work seamlessly with Kingfisher's ImagePrefetcher
.
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.prefetchDataSource = self
}
extension ViewController: UICollectionViewDataSourcePrefetching {
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
let urls = indexPaths.flatMap { URL(string: $0.urlString) }
ImagePrefetcher(urls: urls).start()
}
}
See WWDC 16 - Session 219 for more about changing of it in iOS 10.
let imageView: UIImageView = ...
imageView.kf.setImage(with: URL(string: "your_animated_gif_image_url")!)
If you encountered to memory issues when dealing with large GIF, try to use
AnimatedImageView
instead of regular image view to display GIF. It will only decode several frames of your GIF image to get a smaller memory footprint (but high CPU load).
let imageView = AnimatedImageView()
imageView.kf.setImage(with: URL(string: "your_large_animated_gif_image_url")!)
imageView.kf.setImage(with: URL(string: "your_animated_gif_image_url")!,
options: [.onlyLoadFirstFrame])
It will be useful when you just want to display a static preview of the first frame from a GIF image.
Some processor/filter code is exposed, that means you can apply it seperately on an image regardless where it comes from. They are listed as below.
extension Image {
public func kf.image(withRoundRadius radius: CGFloat, fit size: CGSize, scale: CGFloat) -> Image { }
public func kf.resize(to size: CGSize) -> Image { }
public func kf.blurred(withRadius radius: CGFloat) -> Image { }
public func kf.overlaying(with color: Color, fraction: CGFloat) -> Image { }
public func kf.tinted(with color: Color) -> Image { }
public func kf.adjusted(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) -> Image { }
public func kf.apply(_ filter: Filter) -> Image { }
}
Please also see the full API Reference to find out more.
Installation - Cheat Sheet - FAQ - SwiftUI Support - API Reference - Donate