-
-
Notifications
You must be signed in to change notification settings - Fork 6k
Advanced Usage
This section will introduce some advanced usages for SDWebImage. You can find the code snippet for basic usage and the minimum library version supports the feature. The code snippet is using Objective-C and Swift based on iOS. (Some feature's usage may be a little different crossing the platforms)
Custom download operation is a way to custom some specify logic for advanced usage. Mostly you don't need to do this because we have many configurations to control the behavior for download. Only if you find no way to achieve your goal, you can create a subclass of NSOperation
which conforms to SDWebImageDownloaderOperation
protocol. However, the best practice is subclassing SDWebImageDownloaderOperation
directly and override some functions because some protocol method is not easy to implement.
- Objective-C
@interface MyWebImageDownloaderOperation : SDWebImageDownloaderOperation
@end
- Swift
class MyWebImageDownloaderOperation : SDWebImageDownloaderOperation {}
For example, the HTTP redirection policy. You can implements the URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler: method in your custom operation class.
After you create a custom download operation class, you can now tell the downloader use that class instead of built-in one.
- Objective-C
[[SDWebImageDownloader sharedDownloader].config setOperationClass:[MyWebImageDownloaderOperation class]];
- Swift
SDWebImageDownloader.shared().config.setOperationClass(MyWebImageDownloaderOperation.self)
Custom coder is a way to provide the function to decode or encode for specify image format. Through we have some built-in coder for common image format like JPEG/PNG/GIF/WebP. You can add custom coder to support more image format without any hacking.
For basic custom coder, it must conform to the protocol SDWebImageCoder
. For custom coder supports progressive decoding, it need to conform SDWebImageProgressiveCoder
.
- Objective-C
@interface MyWebImageCoder : NSObject <SDWebImageCoder>
@end
- Swift
class MyWebImageCoder : NSObject, SDWebImageCoder {}
After you create a custom coder class, don't forget to register that into the coder manager.
- Objective-C
MyWebImageCoder *coder = [MyWebImageCoder new];
[[SDWebImageCodersManager sharedInstance] addCoder:coder];
- Swift
let coder = MyWebImageCoder()
SDImageCodersManager.sharedInstance().addCoder(coder)
For more practical usage, check the demo project SDWebImageProgressiveJPEGDemo, which integrate Concorde to support better progressive JPEG decoding.
There are also the list of custom coders from the contributors. See Coder Plugin List for detailed information.
SDWebImage does not enable full GIF decoding/encoding for all UIImageView
by default in 4.x. Because it may reduce the performance for FLAnimatedImageView
. However, you can enable this for all UIImageView
instance by simply adding the built-in GIF coder.
For macOS, NSImage
supports built-in GIF decoding. However, the frame duration is limit to 50ms and may not correctly rendering some GIF images. However, if you enable GIF coder, we will use a subclass of NSBitmapImageRep
to fix the frame duration to match the standard way modern browsers rendering GIF images. See #2223 for more detail.
For SDWebImage 5.x, we provide the GIF coder by default. We suggest user use SDAnimatedImageView
for GIF playback, which is much more efficient compared to UIImageView
.
- Objective-C
[SDWebImageCodersManager.sharedManager addCoder:SDWebImageGIFCoder.sharedCoder];
- Swift
SDImageCodersManager.shared.addCoder(SDWebImageGIFCoder.shared)
- Nothing. But GIF does not supports alpha channel. If you need full alpha animation, try considerate APNG/AWebP and other formats.
SDWebImage from v5.0.0, provide the built-in APNG support via SDWebImageAPNGCoder
. The default coders manager contains APNG coder as well. If not, you can add by yourself:
- Objective-C
[SDWebImageCodersManager.sharedManager addCoder:SDWebImageAPNGCoder.sharedCoder];
- Swift
SDImageCodersManager.shared.addCoder(SDWebImageAPNGCoder.shared)
Note: For SDWebImage 4.x, you can use the third-party implementation maintained by SDWebImage org: SDWebImageAPNGCoder. 5.x does not need this coder plugin.
- on macOS 10.13.4 and below, the APNG encoder encoding feature does not works as expected. The animation will loss and can be only rendered as static PNG images on other platform. Apple fix this on macOS 10.13.4. See #2952 and Radar FB5350343
SDWebImage from v5.2.0 supports iOS 13's built-in HEIC animated image format support. The default coders manager does not contains HEIC coder, you can add this by yourself:
- Objective-C
[SDWebImageCodersManager.sharedManager addCoder:SDWebImageHEICCoder.sharedCoder];
- Swift
SDImageCodersManager.shared.addCoder(SDWebImageHEICCoder.shared)
Note: For iOS 12 and below user, you can use the third-party implementation maintained by SDWebImage org: SDWebImageHEIFCoder.
- iOS 13's HEIC decoder, have bad performance on decoding large HEIC animated images, the same as Safari on iOS. See #2854
- iOS 13's HEIC encoder, will loss the frame duration for each frames. See #3120
- iOS 13's HEIC encoder, have bad performance on encoding large HEIC animated images. We suggest to do encoding on Image Server site, not client.
SDWebImager from v5.9.0 supports iOS 14's built-in WebP animated image format support. The default coders manager does not contains AWebP coder, you can add this by yourself:
- Objective-C
[SDWebImageCodersManager.sharedManager addCoder:SDWebImageAWebPCoder.sharedCoder];
- Swift
SDImageCodersManager.shared.addCoder(SDImageAWebPCoder.shared)
Note:
- The AWebP render logic from Apple's ImageIO is a little different from Google's WebP standard, which may cause some render issue for specified AWebP (which use the
blend_mode == WEBP_MUX_BLEND
, you can usewebpmux -info
command line to check that). See https://github.com/SDWebImage/SDWebImage/issues/3558 - For iOS 13 and below user (or if you're care about performance/render), you can use the third-party implementation maintained by SDWebImage org: SDWebImageWebPCoder.
- iOS 14's AWebP coder, have bad performance on decoding large AWebP images, the more frames we decode, we longer time we spent. See #3132
- iOS 14's AWebP coder does not supports encoding. For encoding, you still need SDWebImageWebPCoder
- Objective-C
// Add WebP support, do this just after launching (AppDelegate)
[SDImageCodersManager.sharedManager addCoder:SDImageWebPCoder.shared];
- Swift
// Add WebP support, do this just after launching (AppDelegate)
SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared)
- Objective-C
// Remove APNG support, do this just after launching (AppDelegate)
[SDImageCodersManager.sharedManager removeCoder:SDImageAPNGCoder.shared];
- Swift
// Remove APNG support, do this just after launching (AppDelegate)
SDImageCodersManager.shared.removeCoder(SDImageAPNGCoder.shared)
- Objective-C
NSData *webpData;
SDImageCoderOptions *options; // If you don't need animation, use `SDImageCoderDecodeFirstFrameOnly`
UIImage *image = [SDImageWebPCoder.sharedCoder decodedImageWithData:webpData options:options];
- Swift
let webpData: Data
let options: SDImageCoderOptions // If you don't need animation, use `.decodeFirstFrameOnly`
let image = SDImageWebPCoder.shared.decodedImage(with: webpData, options: options)
- Objective-C
// For Animated Image, supply frames to create animated image and encode
NSArray<SDImageFrame *> *frames;
UIImage *animatedImage = [SDImageCoderHelper animatedImageWithFrames:frames];
SDImageCoderOptions *options;
NSData *webpData = [SDImageWebPCoder.sharedCoder encodedDataWithImage:animatedImage format:SDImageFormatWebP options:options];
// For Static Image, just use normal UIImage
NSData *webpData = [SDImageWebPCoder.sharedCoder encodedDataWithImage:staticImage format:SDImageFormatWebP options:options];
- Swift
// For Animated Image, supply frames to create animated image and encode
let frames: [SDImageFrame]
let aniamtedImage = SDImageCoderHelper.animatedImage(with: frames)
let options: SDImageCoderOptions
let webpData = SDImageWebPCoder.shared.encodedData(with: animatedImage, format: .webP, options: options)
// For Static Image, just use normal UIImage
let webpData = SDImageWebPCoder.shared.encodedData(with: staticImage, format: .webP, options: options)
For most simple or common cases, you can use the convenient UIImage Category. Remember to register coders.
- Objective-C
UIImage *image = [UIImage sd_imageWithData:webpData];
NSData *webpData =[image sd_imageDataAsFormat:SDImageFormatWebP];
- Swift
let image = UIImage.sd_image(with: webpData)
let webpData = image.sd_imageData(as .webP)
Some of image server provider, may use HTTP Accept Header to detect client supported format. By default, SDWebImage use image/*,*/*;q=0.8
, you can also modify this header for your usage.
For example, WebP use image/webp
MIME type for Accept header, APNG use image/apng
. This sample code setup the Accept Header to the same as Google Chrome:
- Objective-C
[[SDWebImageDownloader sharedDownloader] setValue:@"image/webp,image/apng,image/*,*/*;q=0.8" forHTTPHeaderField:@"Accept"];
- Swift
SDWebImageDownloader.shared.setValue("image/webp,image/apng,image/*,*/*;q=0.8", forHTTPHeaderField:"Accept")
Image progress use a NSProgress to allow user to observe the download progress. Through you can still use the progressBlock
. But using NSProgress
with KVO make your code more easy to maintain. The code snippet use KVOController to simplify the usage for KVO.
- Objective-C
UIProgressView *progressView;
[self.KVOController observe:imageView.sd_imageProgress keyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNew block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
float progress = [change[NSKeyValueChangeNewKey] floatValue];
dispatch_main_async_safe(^{ // progress value updated in background queue
[progressView setProgress:progress animated:YES]; // update UI
});
}];
- Swift
let progressView = UIProgressView();
self.kvoController.observe(imageView.sd_imageProgress, keyPath: #keyPath(Progress.fractionCompleted), options: [.new]) { (observer, object, change) in
if let progress = (change[NSKeyValueChangeKey.newKey.rawValue] as? NSNumber)?.floatValue {
DispatchQueue.main.async { // progress value updated in background queue
progressView.setProgress(progress, animated: true) // update UI
}
}
}
Image transition is used to provide custom transition animation after the image load finished. You can use the built-in convenience method to easily specify your desired animation.
- Objective-C
imageView.sd_imageTransition = SDWebImageTransition.fadeTransition;
NSURL *url = [NSURL URLWithString:@"https://foo/bar.jpg"];
[imageView sd_setImageWithURL:url];
- Swift
imageView.sd_imageTransition = .fade
let url = URL(string: "https://foo/bar.jpg")
imageView.sd_setImage(with: url)
And you can custom complicated animation using the prepares
and animations
property.
- Objective-C
SDWebImageTransition *transition = SDWebImageTransition.fadeTransition;
transition.prepares = ^(__kindof UIView * _Nonnull view, UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
view.transform = CGAffineTransformMakeRotation(M_PI);
};
transition.animations = ^(__kindof UIView * _Nonnull view, UIImage * _Nullable image) {
view.transform = CGAffineTransformIdentity;
};
imageView.sd_imageTransition = transition;
- Swift:
let transition = SDWebImageTransition.fade
transition.prepares = { (view, image, imageData, cacheType, imageURL) in
view.transform = .init(rotationAngle: .pi)
}
transition.animations = { (view, image) in
view.transform = .identity
}
imageView.sd_imageTransition = transition
Image transition is only applied for image from network by default. If you want it been applied for image from cache as well, using SDWebImageForceTransition
option.
SDWebImage 5.0 provide a full stack solution for animated image loading, rendering and decoding.
Since animated image format like GIF, WebP, APNG appears everywhere in today's Internet and Apps. It's important to provide a better solution to keep both simple and powerful. Since FLAnimatedImage
we used in 4.x, is tied to GIF format only, use a NSObject
subclass image representation which is not integrated with our framework, and we can not do further development since it's a third-party lib. We decide to build our own solution for this task.
UIImage/NSImage
is really familiar for Cocoa/Cocoa Touch developer. Which hold a reference for bitmap image storage and let it works for UIImageView/NSImageView
. However, these class for animated image support not works so well for many aspects. You can check -[SDImageCoderHelper animatedImageWithFrames:]
for detailed reason.
So we create a new class called SDAnimatedImage
, which is subclass of UIImage/NSImage
. This class will use our animated image coder and support animated image rendering for SDAnimatedImageView
. By subclassing UIImage/NSImage
, this allow it to be integrated with most framework APIs. Including our SDImageCache
's memory cache, as well as SDWebImageManager
, SDWebImageDownloader
's completion block.
All the methods not listed in the header files will just fallback to the super implementation. Which allow you to just set this SDAnimatedImage
to normal UIImageView/NSImageView
class and show a static poster image. At most of the time, you don't need complicated check for this class. It just works.
This animated image works together with SDAnimatedImageView
(see below), and we have a SDAnimatedImageView+WebCache
category to use all the familiar view category methods.
- Objective-C:
SDAnimatedImageView *imageView = [SDAnimatedImageView new];
[imageView sd_setImageWithURL:url];
- Swift:
let imageView = SDAnimatedImageView()
imageView.sd_setImage(with: url)
SDWebImage provide an animated image view called SDAnimatedImageView
to do animated image rendering. It's a subclass of UIImageView/NSImageView
, which means you can integrate it easier for most of framework APIs.
When the SDAnimatedImageView
was trigged by setImage:
, it will decide how to rendering the image base on image instance. If the image is a SDAnimatedImage
, it will try to do animated image rendering, and start animation immediately. When the image is a UIImage/NSImage
, it will call super method instead to behave like a normal UIImageView/NSImageView
(Note UIImage/NSImage
also can represent an animated one using animatedImageWithImages:duration: API)
The benefit from this custom image view is that you can control the rendering behavior base on your use case. We all know that animated image will consume much more CPU & memory because it needs to decode all image frames into memory.
UIImageView
with animated UIImage
on iOS/tvOS will always store all frames into memory, which may consume huge memory for big animated images (like 100 frames GIF). Which have risk of OOM (Out of memory).
However, NSImageView
on macOS will always decode animated image just in time without cache, consume huge CPU usage for big animated images (like Animated WebP because of low decoding speed).
So now we have SDAnimatedImageView
, by default it will decode and cache the image frames with a buffer (where the buffer size is calculated based on current memory status), when the buffer is out of limit size, the older image frame will be purged to free up memory. This allows you to keep a balance in both CPU & memory. However, you can also set up your own desired buffer size and control the behavior, check maxBufferSize
property in SDAnimatedImageView
for more detailed information.
- Objective-C
SDAnimatedImageView *imageView = [SDAnimatedImageView new];
// this only support bundle file but not asset catalog
// you can also use other initializer like `imageWithData:scale:`
SDAnimatedImage *animatedImage = [SDAnimatedImage imageNamed:@"image.gif"];
// auto start animation
imageView.image = animatedImage;
// when you want to stop
[imageView stopAnimating];
- Swift
let imageView = SDAnimatedImageView()
// this only support bundle file but not asset catalog
// you can also use other initializer like `init?(data:scale:)`
let animatedImage = SDAnimatedImage(named: "image.gif")
// auto start animation
imageView.image = animatedImage
// when you want to stop
imageView.stopAnimating()
Note from 5.3.0, we do refactoring on the rendering part and provide a new component called SDAnimatedImagePlayer
, see below
Another feature, SDAnimatedImageView
supports progressive animated image rendering. Which behave just looks like when Chrome showing a large GIF image. (The animation pause at last downloaded frame, and continue when new frames available).
To enable this, at first you must specify SDWebImageProgressiveLoad
in the options for view category method. This behavior can also be controlled by shouldIncrementalLoad
property in SDAnimatedImageView
. You can disable it and just showing the first poster image during progressive image loading.
- Objective-C:
UIImageView *imageView;
[imageView sd_setImageWithURL:url placeholderImage:nil options:SDWebImageProgressiveLoad];
- Swift:
let imageView: UIImageView
imageView.sd_setImage(with: url, placeholderImage: nil, options: [.progressiveLoad])
For normal UIImageView/NSImageView
, because of the limitation of UIKit, we can not actually pause/resume the rendering frame. So, when you use SDWebImageProgressiveLoad
, we only rendering the first poster frame. This can make best compatible for common use case.
Note: In 4.x, the SDWebImageProgressiveLoad
is named SDWebImageProgressiveDownload
. However, 5.x 's third-party loader can have its own explanation of What is progressive. For example, the Photos Framework loader SDWebImagePhotosPlugin use SDWebImageProgressiveLoad
to quickly render the degraded image firstly, then again with the full pixels image.
To support animated image view rendering. All the image coder plugin should support to decode individual frame instead of just return a simple image using the SDImageCoder
method decodedImageWithData:options:
. So this is why we introduce a new protocol SDAnimatedImageCoder
.
In SDAnimatedImageCoder
, you need to provide all information used for animated image rendering. Such as frame image, frame duration, total frame count and total loop count. All the built-in animated coder (including GIF
, WebP
, APNG
) support this protocol, which means you can use it to show these animated image format right in SDAnimatedImageView
.
- Objective-C
id<SDAnimatedImageCoder> coder = [[SDImageGIFCoder alloc] initWithAnimatedImageData:gifData options:nil];
UIImage *firstFrame = [coder animatedImageFrameAtIndex:0];
- Swift
let coder = SDImageGIFCoder(animatedImageData: gifData)
let firstFrame = coder.animatedImageFrame(at: 0)
However, if you have your own desired image format, you can try to provide your own by implementing this protocol, add your coder plugin, then all things done.
Note if you also want to support progressive animated image rendering. Your custom coder must conform both SDAnimatedImageCoder
and SDProgressiveImageCoder
. The coder will receive updateIncrementalData:finished:
call when new data is available. And you should update your decoder and let it produce the correct animatedImageFrameCount
and other method list in SDAnimatedImageCoder
, which can be decoded with current available data.
For advanced user, since SDAnimatedImage
also represents a protocol, which allow you to customize your own animated image class. You can specify SDWebImageContextAnimatedImageClass
for your custom implementation class to load.
For example, the SDWebImageFLPlugin use this to support FLAnimatedImage for user migrated from 4.x.
Here is also a plugin for YYImage, which you can directly load YYImage
with YYAnimatedImageView
using SDWebImage's loading & caching system, see SDWebImageYYPlugin for detailed information.
Image transformer is the feature, that allows you to do some Image Process on the image which is just loaded and decoded from the image data. For example, you can resize or crop the current loaded image, then produce the output image to be used for the final result and store to cache.
The different with this and Image Coder is that image coder is focused on the decoding from compressed image format data (JPEG/PNG/WebP) to the image memory representation (typically UIImage/NSImage
). But image transformer is focused on the input and output of image memory representation.
For basic image transformer. It should conforms to SDImageTransformer
protocol. We provide many common built-in transformers for list below, which supports iOS/tvOS/macOS/watchOS all Apple platforms. You can also create your own with the custom protocol.
- Rounded Corner (Clip a rounded corner):
SDImageRoundCornerTransformer
- Resize (Scale down or up):
SDImageResizingTransformer
- Crop (Crop a rectangle):
SDImageCroppingTransformer
- Flip (Flip the orientation):
SDImageFlippingTransformer
- Rotate (Rotate a angle):
SDImageRotationTransformer
- Tint Color (Overlay a color mask):
SDImageTintTransformer
- Blur Effect (Gaussian blur):
SDImageBlurTransformer
- Core Image Filter (CIFilter based, exclude watchOS):
SDImageFilterTransformer
And also, you can chain multiple transformers together, to let the input image been processed from the first one to the last one. By using SDImagePipelineTransformer
with a list of transformers.
- Objective-C
id<SDImageTransformer> transformer1 = [SDImageFlippingTransformer transformerWithHorizontal:YES vertical:NO];
id<SDImageTransformer> transformer2 = [SDImageRotationTransformer transformerWithAngle:M_PI_4 fitSize:YES];
id<SDImageTransformer> pipelineTransformer = [SDImagePipelineTransformer transformerWithTransformers:@[transformer1, transformer2]];
- Swift
let transformer1 = SDImageFlippingTransformer(horizontal: true, vertical: false)
let transformer2 = SDImageRotationTransformer(angle: .pi / 4, fitSize: true)
let pipelineTransformer = SDImagePipelineTransformer(transformers: [transformer1, transformer2])
To apply a transformer for image requests, provide the context arg with you transformer, or you can specify a default transformer for image manager level to process all the image requests from that manager.
- Objective-C:
id<SDImageTransformer> transformer = [SDImageResizingTransformer transformerWithSize:CGSizeMake(300, 300) scaleMode:SDImageScaleModeFill];
UIImageView *imageView;
[imageView sd_setImageWithURL:url placeholderImage:nil options:0 context:@{SDWebImageContextImageTransformer: transformer}];
- Swift:
let transformer = SDImageResizingTransformer(size: CGSize(300, 300), scaleMode: .fill)
let imageView: UIImageView
imageView.sd_setImage(with: url, placeholderImage: nil, context: [.imageTransformer: transformer])
During image loading, the image trasnforming is processed inside SDWebImageManager
class, not the SDWebImageDownloader
. So, if you want to directly download an image and do transforming, without any cacheing, DO NOT use SDWebImageDownloader
. Instead, pass the .fromLoaderOnly
(which disable the cache query step) and .storeCacheType = SDImageCacheType.None.rawValue
(which disable the cache store step) to SDWebImageManager
to achieve the same effect.
- Objective-C:
id<SDImageTransformer> transformer = [SDImageResizingTransformer transformerWithSize:CGSizeMake(300, 300) scaleMode:SDImageScaleModeFill];
[SDWebImageManager.sharedManager loadImageWithURL:url
options:SDWebImageFromLoaderOnly
context:@{SDWebImageContextStoreCacheType: @(SDImageCacheTypeNone)}
progress:nil
completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
// the `image` is resized
}];
- Swift:
let transformer = SDImageResizingTransformer(size: CGSize(300, 300), scaleMode: .fill)
SDWebImageManager.shared.loadImage(
with: url,
options: [.fromLoaderOnly],
context: [.storeCacheType: SDImageCacheType.None.rawValue]
progress: nil
completed: { [weak self] (image, data, error, cacheType, finished, url) in
// the `image` is resized
}
)
Our built-in image transformers are only the wrapper which call the UIImage/NSImage
extension for image transform process. You can also use that for common image transform usage.
The list of the common image extension method is almost equivalent to the built-in image transformer above. See UIImage+Transform.h
for more detail information.
- Objective-C:
UIImage *image;
UIImage *croppedImage = [image sd_croppedImageWithRect:CGRectMake(0, 0, 100, 100)];
- Swift:
let image: UIImage
let croppedImage = image.sd_croppedImage(with: CGRect(0, 0, 100, 100))
This is an advanced feature to allow user to customize the image loader used by SDWebImage.
For example, if you have your own network stack, you can consider to use that to build a loader instead of SDWebImageDownloader
. And it's not limited to be network only. You can check SDWebImagePhotosPlugin, which use custom loader to provide Photos Library loading system based on Photos URL.
To create a custom loader, you need a class which conforms to SDImageLoader
protocol. See SDImageLoader
- Objective-C
@interface MyImageLoader : NSObject <SDImageLoader>
@end
- Swift
class MyImageLoader : NSObject, SDImageLoader {}
Note: Except the image data request task, your loader may also face the issue of image decoding because the protocol method output the UIImage/NSImage
instance. Since the decoding process may be changed from version to version, the best practice is to use SDImageLoaderDecodeImageData
or SDImageLoaderDecodeProgressiveImageData
(For progressive image loading) method. This is the built-in logic for decoding in our SDWebImageDownloader
. Using this method to generate the UIImage/NSImage
, can keep the best compatibility of your custom loader for SDWebImage.
Since SDImageLoader
is a protocol, we implement a multiple loader system, which contains multiple loaders and choose the proper loader based on the request URL. By using SDImageLoadersManager
, you can register multiple loaders as you want to handle different type of request.
- Objective-C
id<SDImageLoader> loader1 = SDWebImageDownloader.sharedDownloader;
id<SDImageLoader> loader2 = SDWebImagePhotoLoader.sharedLoader;
SDImageLoadersManager.sharedLoader.loaders = @[loader1, loader2];
// Assign loader to manager
SDWebImageManager *manager = [[SDWebImageManager alloc] initWithCache:SDImageCache.sharedCache loader: SDImageLoadersManager.sharedManager];
// Start use
// If you want to assign loader to default manager, use `defaultImageLoader` class property before shared manager initialized
SDWebImageManager.defaultImageLoader = SDImageLoadersManager.sharedManager;
- Swift
let loader1 = SDWebImageDownloader.shared
let loader2 = SDWebImagePhotosLoader.shared
SDImageLoadersManager.shared.loaders = [loader1, loader2]
// Assign loader to manager
let manager = SDWebImageManager(cache: SDImageCache.shared, loader: SDImageLoadersManager.shared)
// Start use
// If you want to assign loader to default manager, use `defaultImageLoader` class property before shared manager initialized
SDWebImageManager.defaultImageLoader = SDImageLoadersManager.shared
This is an advanced feature to allow user to customize the image cache used by SDWebImage.
In 5.x, we separate the memory cache to SDMemoryCache
class, which focus on memory object storage only. If you want to custom the memory cache, your class should conform to SDMemoryCache
protocol (SDMemoryCacheProtocol
in Swift).
- Objective-C
@interface MyMemoryCache <SDMemoryCache>
@end
- Swift
class MyMemoryCache : SDMemoryCacheProtocol {}
To specify custom memory cache class, use memoryCacheClass
property of SDImageCacheConfig
.
- Objective-C
SDImageCacheConfig.defaultCacheConfig.memoryCacheClass = MyMemoryCache.class
- Swift
SDImageCacheConfig.default.memoryCacheClass = MyMemoryCache.self
In 5.x, we separate the disk cache to SDDiskCache
class, which focus on disk data storage only. If you want to custom the disk cache, your class should conform to SDDiskCache
protocol (SDDiskCacheProtocol
in Swift).
- Objective-C
@interface MyDiskCache <SDDiskCache>
@end
- Swift
class MyDiskCache : SDDiskCacheProtocol {}
To specify custom disk cache class, use diskCacheClass
property of SDImageCacheConfig
.
- Objective-C
SDImageCacheConfig.defaultCacheConfig.diskCacheClass = MyDiskCache.class
- Swift
SDImageCacheConfig.default.diskCacheClass = MyDiskCache.self
Most of time, custom memory cache or disk cache is enough. Since most of SDImageCache
class method are just a wrapper to call the disk cache or memory cache implementation. However, some times we need more control for the cache behavior, so we introduce another protocol called SDImageCache
(SDImageCacheProtocol
in Swift), which can be used to replace SDImageCache
class totally for image cache system.
To create a custom loader, you need a class which conforms to SDImageCache
protocol. See SDImageCache
for more detailed information.
- Objective-C
@interface MyImageCache : NSObject <SDImageCache>
@end
- Swift
class MyImageCache : NSObject, SDImageCacheProtocol {}
Note: Except the image data query task, your cache may also face the issue of image decoding because the protocol method output the UIImage/NSImage
instance. Since the decoding process may be changed from version to version, the best practice is to use SDImageCacheDecodeImageData
method. This is the built-in logic for decoding in our SDImageCache
. Using this method to generate the UIImage/NSImage
, can keep the best compatibility of your custom cache for SDWebImage.
Since SDImageCache
is a protocol, we implement a multiple cache system, which contains multiple cache and choose the proper cache based on the policy to handle cache CRUD method. By using SDImageCachesManager
, you can register multiple cache as you want to handle cache CRUD method.
Since different cache CRUD method may need different priority policy unlike SDImageLoadersManager
, we provide 4 policy to control the order. See SDImageCachesManagerOperationPolicy
for more detailed information.
- Objective-C
id<SDImageCache> cache1 = [[SDImageCache alloc] initWithNamespace:@"cache1"];
id<SDImageCache> cache2 = [[SDImageCache alloc] initWithNamespace:@"cache2"];
SDImageCachesManager.sharedManager.caches = @[cache1, cache2];
SDImageCachesManager.sharedManager.storeOperationPolicy = SDImageCachesManagerOperationPolicyConcurrent; // When any `store` method called, both of cache 1 && cache 2 will be stored in concurrently
// Assign cache to manager
SDWebImageManager *manager = [[SDWebImageManager alloc] initWithCache:SDImageCachesManager.sharedManager loader:SDWebImageDownloader.sharedDownloader];
// Start use
// If you want to assign cache to default manager, use `defaultImageCache` class property before shared manager initialized
SDWebImageManager.defaultImageCache = SDImageCachesManager.sharedManager;
let cache1 = SDImageCache(namespace: "cache1")
let cache2 = SDImageCache(namespace: "cache2")
SDImageCachesManager.shared.caches = [cache1, cache2]
SDImageCachesManager.shared.storeOperationPolicy = .concurrent // When any `store` method called, both of cache 1 && cache 2 will be stored in concurrently
// Assign cache to manager
let manager = SDWebImageManager(cache:SDImageCachesManager.shared, loader:SDWebImageDownloader.shared)
// Start use
// If you want to assign cache to default manager, use `defaultImageCache` class property before shared manager initialized
SDWebImageManager.defaultImageCache = SDImageCachesManager.shared
SDWebImage have both SDWebImageOptions
(enum) and SDWebImageContext
(dictionary) to hold extra control options during image loading. These args are available right in sd_setImageWithURL:
method, for user to control the detail behavior for individual image requests.
We recommend to treat each image request independent pipeline which does not effect each others. This can avoid sharing global status and cause issues. However, sometimes, users may still prefer a global control for these options, instead of changing each method calls. For this propose, we have options processor.
Option Processor is a hook block in the manager level, which let user to interact with the original options and context, process them based on your usage, and return the final result to load. This can make a central control and perform complicated logic, better than just a default options and context feature, compared to the similar API on Kingfisher.
Here are some common use cases to use options processor:
- Objective-C
SDWebImageManager.sharedManager.optionsProcessor = [SDWebImageOptionsProcessor optionsProcessorWithBlock:^SDWebImageOptionsResult * _Nullable(NSURL * _Nullable url, SDWebImageOptions options, SDWebImageContext * _Nullable context) {
options |= SDWebImageAvoidDecodeImage;
return [[SDWebImageOptionsResult alloc] initWithOptions:options context:context];
}];
- Swift
SDWebImageManager.shared.optionsProcessor = SDWebImageOptionsProcessor() { url, options, context in
var mutableOptions = options
mutableOptions.insert(.avoidDecodeImage)
return SDWebImageOptionsResult(options: mutableOptions, context: context)
}
- Objective-C
SDWebImageManager.sharedManager.optionsProcessor = [SDWebImageOptionsProcessor optionsProcessorWithBlock:^SDWebImageOptionsResult * _Nullable(NSURL * _Nullable url, SDWebImageOptions options, SDWebImageContext * _Nullable context) {
// Check `SDAnimatedImageView`
if (!context[SDWebImageContextAnimatedImageClass]) {
options |= SDWebImageDecodeFirstFrameOnly;
}
return [[SDWebImageOptionsResult alloc] initWithOptions:options context:context];
}];
- Swift
SDWebImageManager.shared.optionsProcessor = SDWebImageOptionsProcessor() { url, options, context in
var mutableOptions = options
if let context = context, let _ = context[.animatedImageClass] {
// Check `SDAnimatedImageView`
} else {
mutableOptions.insert(.decodeFirstFrameOnly)
}
return SDWebImageOptionsResult(options: mutableOptions, context: context)
}
From 5.0.0, SDWebImage provide a SDAnimatedImageView
(see above) for the animated image rendering solution. However, sometimes, we may want to do animation processing, where we don't have or don't want to create either UIView
or SDAnimatedImage
objects. For those cases, SDAnimatedImageView
can not solve the problem.
For example, we can not use SDAnimatedImageView
on watchOS animation, where we don't have a CALayer. Another case is the new SwiftUI environment, which don't have any UIKit behavior.
In 5.3.0, we do refactoring on SDAnimatedImageView
, separate the rendering part from the player part. The SDAnimatedImagePlayer
is the solution for this.
A player, does not care about how you consume the frames (whether to render it on CALayer, on WatchKit, or SwiftUI, or even some digital process pipeline). It also does not care about how you generate the frames (whether SDAnimatedImage, SDImageGIFCoder. Even the video format frames - see showcase SDWebImageVideoCoder). It only drive the logic to calculate the duration, frame rate, and callback the handler to process.
The simple usage for WatchKit animation, can be concluded into this:
- Objective-C
id<SDAnimatedImageProvider> provider; // Anything conforms to protocol like `SDAnimatedImage`
SDAnimatedImagePlayer *player = [SDAnimatedImagePlayer playerWithProvider:provider];
WKInterfaceImage *target; // Anything can receive image
player.animationFrameHandler = ^(NSUInteger index, UIImage * frame) {
[target setImage:frame];
};
- Swift
let provider: SDAnimatedImageProvider // Anything conforms to protocol like `SDAnimatedImage`
let player = SDAnimatedImagePlayer(provider: provider)
let target: WKInterfaceImage // Anything can receive image
player.animationFrameHandler = { index, frame in
target.setImage(frame)
}
Note that the provider can represent mutable content, which means provider can update their internal data structure, to provide more frames for playing. But note you should update the frame count or loop count by calling the SDAnimatedImagePlayer
method. Our SDAnimatedImageView
use this design to support progressive animate image loading.
Animated Player also has small details control like playback rate, frame skip, etc. Check the latest documentation for details.
Sometime, you want to load image contents that are encrypted. Such as AES/DES/Base64 algorithm. For previous release, you have to write custom operation and complicated code for this task.
From 5.3.0, we provide a simple solution, to write a custom handler block to decrypt the original image data. Then we process it as the default download and decoding part. For example, a base64 image data decryptor:
- Objective-C
NSURL *url;
id<SDWebImageDownloaderDecryptor> decryptor = SDWebImageDownloaderDecryptor.base64Decryptor;
[imageView sd_setImageWithURL:url placeholderImage:nil options:0 context:@{SDWebImageContextDownloadDecryptor : decryptor}];
- Swift
let url: URL
let decryptor = SDWebImageDownloaderDecryptor.base64
imageView.sd_setImage(with: url, context:[.downloadDecryptor : decryptor])
You can also build your own data decryptor algorithm use the protocol method or block, which is always get called on the URLSession delegate queue (A global serial queue)
Previously, for all the image (static or animated), SDWebImage will try to decode the full pixel image, whatever the size image is. This may suitable for most cases. However, since we focus on Web images, sometimes you may load images which size is much more than you view size. This may consume unused RAM and have a OOM of high-risk.
From 5.0.0 version, you can use the Image Transformer, to scale down the size you want. This may solve the problem in some aspects. However, before transformer is called, the large bitmap pixel already been decoded on the RAM, For example, one huge World Map image which contains 10000x10000 pixels, if transformer is called, it already allocated 381MB in RAM, and have to allocated another 381MB for temp buffer, this may cause a OOM.
So, a better way for thumbnail, it's only to decode the small size, instead of decoding full pixels and scaling it down. Both Apple's Image/IO and some third-party codec (like libwebp/libheif) support this feature. In 5.5.0 version, we introduce the solution for thumbnail decoding.
To use thumbnail decoding, you can just use the context option, to pass the desired limit size you want. Like this:
- Objective-C
CGFloat scale = UIScreen.mainScreen.scale; // Will be 2.0 on 6/7/8 and 3.0 on 6+/7+/8+ or later
CGSize thumbnailSize = CGSizeMake(200 * scale, 200 * scale); // Thumbnail will bounds to (200,200) points
[imageView sd_setImageWithURL:url placeholderImage:nil options:0 context:@{SDWebImageContextImageThumbnailPixelSize : @(thumbnailSize)];
- Swift
let scale = UIScreen.main.scale // Will be 2.0 on 6/7/8 and 3.0 on 6+/7+/8+ or later
let thumbnailSize = CGSize(width: 200 * scale, height: 200 * scale) // Thumbnail will bounds to (200,200) points
imageView.sd_setImage(with: url, placeholderImage: nil, context: [.imageThumbnailPixelSize : thumbnailSize])
Just easy.
We also note that there are already one SDWebImageScaleDownLargeImages
option in SDWebImage, which does not works so well in previous version. From v5.5.0, when using this option, we will translate this into the correspond thumbnail size limit, using the bytes limit you provided.
The default bytes limit on iOS is 60MB (to keep compatible with old version of SDWebImage). Which result (3966, 3966)
pixel. If this is not suitable for your case, you can change the default value in global. But actually, we recommended to use the context option instead, which is far more flexible. (Remember, you can also use Options Processor to control context globally)
- Objective-C
SDImageCoderHelper.defaultScaleDownLimitBytes = 1000 * 1000 * 4; // (1000, 1000) pixels
- Swift
SDImageCoderHelper.defaultScaleDownLimitBytes = 1000 * 1000 * 4 // (1000, 1000) pixels
Note:
- When you specify different thumbnail size, they does not hit the same cache key, each thumbnail image will be cached separately. Like the behavior of transformer.
- By default, we keep the full image's aspect ratio for thumbnail. You can also use
.imagePreserveAspectRatio
to control this behavior to stretch the image. - When the thumbnail pixel size is larger than full image pixel size, we will do only full pixel decoding, never scale up. For this propose, use resize transformer instead.
- These two context options, applied for Vector Image as well, such as PDF, SVG format in our coder plugin list. When you limit the pixel size, they will fallback to produce a bitmap version instead.
Thumbnail encoding is used to encode an exist UIImage/NSImage
to your target desired size, without pre-scale it down.
The scale part is handled by the encoder, which is far more performant than using CoreGraphics API. And it's more smart for some image format who support embed thumbnail item (JPEG/HEIF/AVIF).
- Objective-C
// Image thumbnail encoding
UIImage *image;
NSData *thumbnailData = [[SDImageIOCoder sharedCoder] encodedDataWithImage:image format:SDImageFormatHEIC options:@{SDImageCoderEncodeMaxPixelSize : @(CGSizeMake(200, 200)}]; // encoding max pixel size
- Swift
// Image thumbnail encoding
let image: UIImage
let thumbnailData = SDImageIOCoder.shared.encodedData(with: image, format: .heic, options: [.encodeMaxPixelSize: CGSize(width: 200, height: 200)]) // encoding max pixel size
Note:
- The thumbnail always keep aspect ratio from the full image currently.
- When the thumbnail pixel size is larger than full image pixel size, we will do only full pixel encoding, never scale up. For this propose, use resize transformer instead.
- For JPEG/HEIF/AVIF, which image format container itself support embed thumbnail (think like EXIF), currently we don't write the standalone thumbnail item into the bitstream. This feature will be available in 5.8.0.