Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork6k
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 ofNSOperation which conforms toSDWebImageDownloaderOperation protocol. However, the best practice is subclassingSDWebImageDownloaderOperation directly and override some functions because some protocol method is not easy to implement.
- Objective-C
@interfaceMyWebImageDownloaderOperation :SDWebImageDownloaderOperation@end
- Swift
classMyWebImageDownloaderOperation:SDWebImageDownloaderOperation{}
For example, the HTTP redirection policy. You can implements theURLSession: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
[[SDWebImageDownloadersharedDownloader].configsetOperationClass:[MyWebImageDownloaderOperationclass]];
- 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 protocolSDWebImageCoder. For custom coder supportsprogressive decoding, it need to conformSDWebImageProgressiveCoder.
- Objective-C
@interfaceMyWebImageCoder :NSObject <SDWebImageCoder>@end
- Swift
classMyWebImageCoder:NSObject,SDWebImageCoder{}
After you create a custom coder class, don't forget to register that into the coder manager.
- Objective-C
MyWebImageCoder *coder = [MyWebImageCodernew];[[SDWebImageCodersManagersharedInstance]addCoder:coder];
- Swift
letcoder=MyWebImageCoder()SDImageCodersManager.sharedInstance().addCoder(coder)
For more practical usage, check the demo projectSDWebImageProgressiveJPEGDemo, which integrateConcorde to support better progressive JPEG decoding.
There are also the list of custom coders from the contributors.SeeCoder Plugin List for detailed information.
SDWebImage does not enable full GIF decoding/encoding for allUIImageView by default in 4.x. Because it may reduce the performance forFLAnimatedImageView. However, you can enable this for allUIImageView 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 ofNSBitmapImageRep 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 useSDAnimatedImageView for GIF playback, which is much more efficient compared toUIImageView.
- Objective-C
[SDWebImageCodersManager.sharedManageraddCoder: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 viaSDWebImageAPNGCoder. The default coders manager contains APNG coder as well. If not, you can add by yourself:
- Objective-C
[SDWebImageCodersManager.sharedManageraddCoder: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.sharedManageraddCoder: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.sharedManageraddCoder: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 -infocommand line to check that). Seehttps://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 needSDWebImageWebPCoder
- Objective-C
// Add WebP support, do this just after launching (AppDelegate)[SDImageCodersManager.sharedManageraddCoder: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.sharedManagerremoveCoder: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.sharedCoderdecodedImageWithData:webpDataoptions:options];
- Swift
letwebpData:Dataletoptions:SDImageCoderOptions // If you don't need animation, use `.decodeFirstFrameOnly`letimage=SDImageWebPCoder.shared.decodedImage(with: webpData, options: options)
- Objective-C
// For Animated Image, supply frames to create animated image and encodeNSArray<SDImageFrame *> *frames;UIImage *animatedImage = [SDImageCoderHelperanimatedImageWithFrames:frames];SDImageCoderOptions *options;NSData *webpData = [SDImageWebPCoder.sharedCoderencodedDataWithImage:animatedImageformat:SDImageFormatWebPoptions:options];// For Static Image, just use normal UIImageNSData *webpData = [SDImageWebPCoder.sharedCoderencodedDataWithImage:staticImageformat:SDImageFormatWebPoptions:options];
- Swift
// For Animated Image, supply frames to create animated image and encodeletframes:[SDImageFrame]letaniamtedImage=SDImageCoderHelper.animatedImage(with: frames)letoptions:SDImageCoderOptionsletwebpData=SDImageWebPCoder.shared.encodedData(with: animatedImage, format:.webP, options: options)// For Static Image, just use normal UIImageletwebpData=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 = [UIImagesd_imageWithData:webpData];NSData *webpData =[imagesd_imageDataAsFormat:SDImageFormatWebP];
- Swift
letimage=UIImage.sd_image(with: webpData)letwebpData= image.sd_imageData(as.webP)
Some of image server provider, may useHTTP Accept Header to detect client supported format. By default, SDWebImage useimage/*,*/*;q=0.8, you can also modify this header for your usage.
For example,WebP useimage/webp MIME type for Accept header,APNG useimage/apng. This sample code setup the Accept Header to the same as Google Chrome:
- Objective-C
[[SDWebImageDownloadersharedDownloader]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 aNSProgress to allow user to observe the download progress. Through you can still use theprogressBlock. But usingNSProgress withKVO make your code more easy to maintain. The code snippet useKVOController to simplify the usage for KVO.
- Objective-C
UIProgressView *progressView;[self.KVOControllerobserve:imageView.sd_imageProgresskeyPath:NSStringFromSelector(@selector(fractionCompleted))options:NSKeyValueObservingOptionNewblock:^(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 [progressViewsetProgress:progressanimated:YES];// update UI });}];
- Swift
letprogressView=UIProgressView();self.kvoController.observe(imageView.sd_imageProgress, keyPath: #keyPath(Progress.fractionCompleted), options:[.new]){(observer, object, change)iniflet 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 = [NSURLURLWithString:@"https://foo/bar.jpg"];[imageViewsd_setImageWithURL:url];
- Swift
imageView.sd_imageTransition=.fadeleturl=URL(string:"https://foo/bar.jpg")imageView.sd_setImage(with: url)
And you can custom complicated animation using theprepares andanimations 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:
lettransition=SDWebImageTransition.fadetransition.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, usingSDWebImageForceTransition 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. SinceFLAnimatedImage we used in 4.x, is tied to GIF format only, use aNSObject 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 forUIImageView/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 calledSDAnimatedImage, which is subclass ofUIImage/NSImage. This class will use our animated image coder and support animated image rendering forSDAnimatedImageView. By subclassingUIImage/NSImage, this allow it to be integrated with most framework APIs. Including ourSDImageCache's memory cache, as well asSDWebImageManager,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 thisSDAnimatedImage to normalUIImageView/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 withSDAnimatedImageView (see below), and we have aSDAnimatedImageView+WebCache category to use all the familiar view category methods.
- Objective-C:
SDAnimatedImageView *imageView = [SDAnimatedImageViewnew];[imageViewsd_setImageWithURL:url];
- Swift:
letimageView=SDAnimatedImageView()imageView.sd_setImage(with: url)
SDWebImage provide an animated image view calledSDAnimatedImageView to do animated image rendering. It's a subclass ofUIImageView/NSImageView, which means you can integrate it easier for most of framework APIs.
When theSDAnimatedImageView was trigged bysetImage:, it will decide how to rendering the image base on image instance. If the image is aSDAnimatedImage, it will try to do animated image rendering, and start animation immediately. When the image is aUIImage/NSImage, it will call super method instead to behave like a normalUIImageView/NSImageView (NoteUIImage/NSImage also can represent an animated one usinganimatedImageWithImages: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 animatedUIImage 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 haveSDAnimatedImageView, 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, checkmaxBufferSize property inSDAnimatedImageView for more detailed information.
- Objective-C
SDAnimatedImageView *imageView = [SDAnimatedImageViewnew];// this only support bundle file but not asset catalog// you can also use other initializer like `imageWithData:scale:`SDAnimatedImage *animatedImage = [SDAnimatedImageimageNamed:@"image.gif"];// auto start animationimageView.image = animatedImage;// when you want to stop[imageViewstopAnimating];
- Swift
letimageView=SDAnimatedImageView()// this only support bundle file but not asset catalog// you can also use other initializer like `init?(data:scale:)`letanimatedImage=SDAnimatedImage(named:"image.gif")// auto start animationimageView.image= animatedImage// when you want to stopimageView.stopAnimating()
Note from 5.3.0, we do refactoring on the rendering part and provide a new component calledSDAnimatedImagePlayer, seebelow
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 specifySDWebImageProgressiveLoad in the options for view category method. This behavior can also be controlled byshouldIncrementalLoad property inSDAnimatedImageView. You can disable it and just showing the first poster image during progressive image loading.
- Objective-C:
UIImageView *imageView;[imageViewsd_setImageWithURL:urlplaceholderImage:niloptions:SDWebImageProgressiveLoad];
- Swift:
letimageView:UIImageViewimageView.sd_setImage(with: url, placeholderImage:nil, options:[.progressiveLoad])
For normalUIImageView/NSImageView, because of the limitation of UIKit, we can not actually pause/resume the rendering frame. So, when you useSDWebImageProgressiveLoad, we only rendering the first poster frame. This can make best compatible for common use case.
Note: In 4.x, theSDWebImageProgressiveLoad is namedSDWebImageProgressiveDownload. However, 5.x 's third-party loader can have its own explanation ofWhat is progressive. For example, the Photos Framework loaderSDWebImagePhotosPlugin useSDWebImageProgressiveLoad 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 theSDImageCoder methoddecodedImageWithData:options:. So this is why we introduce a new protocolSDAnimatedImageCoder.
InSDAnimatedImageCoder, 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 (includingGIF,WebP,APNG) support this protocol, which means you can use it to show these animated image format right inSDAnimatedImageView.
- Objective-C
id<SDAnimatedImageCoder> coder = [[SDImageGIFCoderalloc]initWithAnimatedImageData:gifDataoptions:nil];UIImage *firstFrame = [coderanimatedImageFrameAtIndex:0];
- Swift
letcoder=SDImageGIFCoder(animatedImageData: gifData)letfirstFrame= 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 bothSDAnimatedImageCoder andSDProgressiveImageCoder. The coder will receiveupdateIncrementalData:finished: call when new data is available. And you should update your decoder and let it produce the correctanimatedImageFrameCount and other method list inSDAnimatedImageCoder, which can be decoded with current available data.
For advanced user, sinceSDAnimatedImage also represents a protocol, which allow you to customize your own animated image class. You can specifySDWebImageContextAnimatedImageClass for your custom implementation class to load.
For example, theSDWebImageFLPlugin use this to supportFLAnimatedImage for user migrated from 4.x.
Here is also a plugin for YYImage, which you can directly loadYYImage withYYAnimatedImageView using SDWebImage's loading & caching system, seeSDWebImageYYPlugin for detailed information.
Image transformer is the feature, that allows you to do someImage 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 andImage Coder is that image coder is focused on the decoding from compressed image format data (JPEG/PNG/WebP) to the image memory representation (typicallyUIImage/NSImage). But image transformer is focused on theinput andoutput of image memory representation.
For basic image transformer. It should conforms toSDImageTransformer 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 usingSDImagePipelineTransformer with a list of transformers.
- Objective-C
id<SDImageTransformer> transformer1 = [SDImageFlippingTransformertransformerWithHorizontal:YESvertical:NO];id<SDImageTransformer> transformer2 = [SDImageRotationTransformertransformerWithAngle:M_PI_4fitSize:YES];id<SDImageTransformer> pipelineTransformer = [SDImagePipelineTransformertransformerWithTransformers:@[transformer1, transformer2]];
- Swift
lettransformer1=SDImageFlippingTransformer(horizontal:true, vertical:false)lettransformer2=SDImageRotationTransformer(angle:.pi/4, fitSize:true)letpipelineTransformer=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 = [SDImageResizingTransformertransformerWithSize:CGSizeMake(300,300)scaleMode:SDImageScaleModeFill];UIImageView *imageView;[imageViewsd_setImageWithURL:urlplaceholderImage:niloptions:0context:@{SDWebImageContextImageTransformer: transformer}];
- Swift:
lettransformer=SDImageResizingTransformer(size:CGSize(300,300), scaleMode:.fill)letimageView:UIImageViewimageView.sd_setImage(with: url, placeholderImage:nil, context:[.imageTransformer: transformer])
During image loading, the image trasnforming is processed insideSDWebImageManager class, not theSDWebImageDownloader. So, if you want to directly download an image and do transforming, without any cacheing, DO NOT useSDWebImageDownloader. Instead, pass the.fromLoaderOnly (which disable the cache query step) and.storeCacheType = SDImageCacheType.None.rawValue (which disable the cache store step) toSDWebImageManager to achieve the same effect.
- Objective-C:
id<SDImageTransformer> transformer = [SDImageResizingTransformertransformerWithSize:CGSizeMake(300,300)scaleMode:SDImageScaleModeFill];[SDWebImageManager.sharedManagerloadImageWithURL:urloptions:SDWebImageFromLoaderOnlycontext:@{SDWebImageContextStoreCacheType: @(SDImageCacheTypeNone)}progress:nilcompleted:^(UIImage * _Nullable image,NSData * _Nullable data,NSError * _Nullable error, SDImageCacheType cacheType,BOOL finished,NSURL * _Nullable imageURL) {// the `image` is resized}];
- Swift:
lettransformer=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 theUIImage/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. SeeUIImage+Transform.h for more detail information.
- Objective-C:
UIImage *image;UIImage *croppedImage = [imagesd_croppedImageWithRect:CGRectMake(0,0,100,100)];
- Swift:
letimage:UIImageletcroppedImage= 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 ofSDWebImageDownloader. And it's not limited to be network only. You can checkSDWebImagePhotosPlugin, 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 toSDImageLoader protocol. SeeSDImageLoader
- Objective-C
@interfaceMyImageLoader :NSObject <SDImageLoader>@end
- Swift
classMyImageLoader:NSObject,SDImageLoader{}
Note: Except the image data request task, your loader may also face the issue of image decoding because the protocol method output theUIImage/NSImage instance. Since the decoding process may be changed from version to version, the best practice is to useSDImageLoaderDecodeImageData orSDImageLoaderDecodeProgressiveImageData (For progressive image loading) method. This is the built-in logic for decoding in ourSDWebImageDownloader. Using this method to generate theUIImage/NSImage, can keep the best compatibility of your custom loader for SDWebImage.
SinceSDImageLoader is a protocol, we implement a multiple loader system, which contains multiple loaders and choose the proper loader based on the request URL. By usingSDImageLoadersManager, 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 managerSDWebImageManager *manager = [[SDWebImageManageralloc]initWithCache:SDImageCache.sharedCacheloader: SDImageLoadersManager.sharedManager];// Start use// If you want to assign loader to default manager, use `defaultImageLoader` class property before shared manager initializedSDWebImageManager.defaultImageLoader = SDImageLoadersManager.sharedManager;
- Swift
letloader1=SDWebImageDownloader.sharedletloader2=SDWebImagePhotosLoader.sharedSDImageLoadersManager.shared.loaders=[loader1, loader2]// Assign loader to managerletmanager=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 initializedSDWebImageManager.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 toSDMemoryCache class, which focus on memory object storage only. If you want to custom the memory cache, your class should conform toSDMemoryCache protocol (SDMemoryCacheProtocol in Swift).
- Objective-C
@interfaceMyMemoryCache <SDMemoryCache>@end
- Swift
classMyMemoryCache:SDMemoryCacheProtocol{}
To specify custom memory cache class, usememoryCacheClass property ofSDImageCacheConfig.
- Objective-C
SDImageCacheConfig.defaultCacheConfig.memoryCacheClass = MyMemoryCache.class
- Swift
SDImageCacheConfig.default.memoryCacheClass=MyMemoryCache.self
In 5.x, we separate the disk cache toSDDiskCache class, which focus on disk data storage only. If you want to custom the disk cache, your class should conform toSDDiskCache protocol (SDDiskCacheProtocol in Swift).
- Objective-C
@interfaceMyDiskCache <SDDiskCache>@end
- Swift
classMyDiskCache:SDDiskCacheProtocol{}
To specify custom disk cache class, usediskCacheClass property ofSDImageCacheConfig.
- 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 ofSDImageCache 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 calledSDImageCache(SDImageCacheProtocol in Swift), which can be used to replaceSDImageCache class totally for image cache system.
To create a custom loader, you need a class which conforms toSDImageCache protocol. SeeSDImageCache for more detailed information.
- Objective-C
@interfaceMyImageCache :NSObject <SDImageCache>@end
- Swift
classMyImageCache:NSObject,SDImageCacheProtocol{}
Note: Except the image data query task, your cache may also face the issue of image decoding because the protocol method output theUIImage/NSImage instance. Since the decoding process may be changed from version to version, the best practice is to useSDImageCacheDecodeImageData method. This is the built-in logic for decoding in ourSDImageCache. Using this method to generate theUIImage/NSImage, can keep the best compatibility of your custom cache for SDWebImage.
SinceSDImageCache 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 usingSDImageCachesManager, you can register multiple cache as you want to handle cache CRUD method.
Since different cache CRUD method may need different priority policy unlikeSDImageLoadersManager, we provide 4 policy to control the order. SeeSDImageCachesManagerOperationPolicy for more detailed information.
- Objective-C
id<SDImageCache> cache1 = [[SDImageCachealloc]initWithNamespace:@"cache1"];id<SDImageCache> cache2 = [[SDImageCachealloc]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 managerSDWebImageManager *manager = [[SDWebImageManageralloc]initWithCache:SDImageCachesManager.sharedManagerloader:SDWebImageDownloader.sharedDownloader];// Start use// If you want to assign cache to default manager, use `defaultImageCache` class property before shared manager initializedSDWebImageManager.defaultImageCache = SDImageCachesManager.sharedManager;
letcache1=SDImageCache(namespace:"cache1")letcache2=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 managerletmanager=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 initializedSDWebImageManager.defaultImageCache=SDImageCachesManager.shared
SDWebImage have bothSDWebImageOptions (enum) andSDWebImageContext (dictionary) to hold extra control options during image loading. These args are available right insd_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 adefault options and context feature, compared to the similar API onKingfisher.
Here are some common use cases to use options processor:
- Objective-C
SDWebImageManager.sharedManager.optionsProcessor = [SDWebImageOptionsProcessoroptionsProcessorWithBlock:^SDWebImageOptionsResult *_Nullable(NSURL * _Nullable url, SDWebImageOptions options, SDWebImageContext * _Nullable context) { options |= SDWebImageAvoidDecodeImage;return [[SDWebImageOptionsResultalloc]initWithOptions:optionscontext:context];}];
- Swift
SDWebImageManager.shared.optionsProcessor=SDWebImageOptionsProcessor(){ url, options, contextinvarmutableOptions= options mutableOptions.insert(.avoidDecodeImage)returnSDWebImageOptionsResult(options: mutableOptions, context: context)}
- Objective-C
SDWebImageManager.sharedManager.optionsProcessor = [SDWebImageOptionsProcessoroptionsProcessorWithBlock:^SDWebImageOptionsResult *_Nullable(NSURL * _Nullable url, SDWebImageOptions options, SDWebImageContext * _Nullable context) {// Check `SDAnimatedImageView`if (!context[SDWebImageContextAnimatedImageClass]) { options |= SDWebImageDecodeFirstFrameOnly; }return [[SDWebImageOptionsResultalloc]initWithOptions:optionscontext:context];}];
- Swift
SDWebImageManager.shared.optionsProcessor=SDWebImageOptionsProcessor(){ url, options, contextinvarmutableOptions= optionsiflet context= context,let _=context[.animatedImageClass]{ // Check `SDAnimatedImageView`}else{ mutableOptions.insert(.decodeFirstFrameOnly)}returnSDWebImageOptionsResult(options: mutableOptions, context: context)}
From 5.0.0, SDWebImage provide aSDAnimatedImageView (seeabove) 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 eitherUIView orSDAnimatedImage objects. For those cases,SDAnimatedImageView can not solve the problem.
For example, we can not useSDAnimatedImageView 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 onSDAnimatedImageView, separate the rendering part from the player part. TheSDAnimatedImagePlayer is the solution for this.
A player, does not care abouthow 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 abouthow you generate the frames (whether SDAnimatedImage, SDImageGIFCoder. Even the video format frames - see showcaseSDWebImageVideoCoder). 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 = [SDAnimatedImagePlayerplayerWithProvider:provider];WKInterfaceImage *target;// Anything can receive imageplayer.animationFrameHandler = ^(NSUInteger index, UIImage * frame) { [targetsetImage:frame];};
- Swift
letprovider:SDAnimatedImageProvider // Anything conforms to protocol like `SDAnimatedImage`letplayer=SDAnimatedImagePlayer(provider: provider)lettarget:WKInterfaceImage // Anything can receive imageplayer.animationFrameHandler={ index, framein 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 theSDAnimatedImagePlayer method. OurSDAnimatedImageView use this design to support progressive animate image loading.
Animated Player also has small details control like playback rate, frame skip, etc. Check the latestdocumentation for details.
Sometime, you want to load image contents that are encrypted. Such as AES/DES/Base64 algorithm. For previous release, you have to writecustom 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;[imageViewsd_setImageWithURL:urlplaceholderImage:niloptions:0context:@{SDWebImageContextDownloadDecryptor : decryptor}];
- Swift
leturl:URLletdecryptor=SDWebImageDownloaderDecryptor.base64imageView.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 theImage 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 laterCGSize thumbnailSize = CGSizeMake(200 * scale,200 * scale);// Thumbnail will bounds to (200,200) points[imageViewsd_setImageWithURL:urlplaceholderImage:niloptions:0context:@{SDWebImageContextImageThumbnailPixelSize : @(thumbnailSize)];
- Swift
letscale=UIScreen.main.scale // Will be 2.0 on 6/7/8 and 3.0 on 6+/7+/8+ or laterletthumbnailSize=CGSize(width:200* scale, height:200* scale) // Thumbnail will bounds to (200,200) pointsimageView.sd_setImage(with: url, placeholderImage:nil, context:[.imageThumbnailPixelSize: thumbnailSize])
Just easy.
We also note that there are already oneSDWebImageScaleDownLargeImages 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 useOptions 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
.imagePreserveAspectRatioto 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 ourcoder plugin list. When you limit the pixel size, they will fallback to produce a bitmap version instead.
Thumbnail encoding is used to encode an existUIImage/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 encodingUIImage *image;NSData *thumbnailData = [[SDImageIOCodersharedCoder]encodedDataWithImage:imageformat:SDImageFormatHEICoptions:@{SDImageCoderEncodeMaxPixelSize : @(CGSizeMake(200,200)}];// encoding max pixel size
- Swift
// Image thumbnail encodingletimage:UIImageletthumbnailData=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.