In my project, I have to transform same image to serval forms, like cornerRadius = 10 or 15, or blur that image before caching, and of course, I do want to cache it separately, but the only way I found to solve this problem is to write multiple subclass of SDWebImageManager, is there any easy way?
sample code showed as follows:
class BlurImageManager: SDWebImageManager {
static let shared = BlurImageManager()
var imageLayer = CALayer()
init() {
super.init(cache: SDImageCache.init(namespace: "xxxx"), downloader: SDWebImageDownloader.shared())
delegate = self
imageLayer.contentsGravity = "resizeAspectFill"
imageLayer.cornerRadius = 5
imageLayer.masksToBounds = true
}
}
extension BlurImageManager: SDWebImageManagerDelegate {
func imageManager(_ imageManager: SDWebImageManager, transformDownloadedImage image: UIImage?, with imageURL: URL?) -> UIImage? {
let image = image?.blurDarkImage()
let size = CGSize(width: kScreenWidth-20, height: 150)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
let context = UIGraphicsGetCurrentContext()!
imageLayer.frame.size = size
imageLayer.contents = image?.cgImage
imageLayer.render(in: context)
let output = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return output
}
}
First. A delegate do not need to subclass SDWebImageManager. You can use all the class which just conform the SDWebImageManagerDelegate protocol.
@interface MyObject : NSObject <SDWebImageManagerDelegate>
@end
So you can just place that cornerRadius or some property as configuration in your own class and implement the delegate method.
You may not fully understand my problem, I do know A delegate do not need to subclass SDWebImageManager, but the problem is that SDWebImageManager's singleton can have only one delegate, but I have multiple transform style to one image and cache it separately, can you come up some ideas about this case?
For this use case. Maybe you can try to create an delegate manager which hold multiple NSObject<SDWebImageDownloadDelegate instance. And set this delegate manager to SDWebImageManager.sharedManager.delegate. Then, when the delegate manager receive any method call, dispatch it to all the delegates item and choose one to return. The returned one will be cached (Like the naive code here). This can simulate multi-delegate design pattern.
Can this achieve your goal ?
I don't think that can solve it, cause for one image, SDWebImageManager's singleton would just cache it once in one place, while I do want to cache all transformed image of that image to disk, and use it in its right place.
Maybe the core issue is that I want the SD do the most things for me, like download(which is ok to use the shared instance), cache(which is not ok for this case to use the shared cache instance), find cache..., and then I would just use the api on SDWebImageManager's to load image:
- (nullable id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock;
Since this not the common case (Download one image and cache for multiple format), I think you could try yourself to find a better solution. Maybe you can just directly call -[SDImageCache storeImage:imageData:forKey:toDisk:completion:] in the delegate manager before method return to save all the delegate returned images to SD's cache.
Moreover, I think this should not be an issue but more like some business logic or usage, maybe it's not suitable to discuss on repo issues. 馃槾
sorry, I will close it.
@Josscii Hey. I'm creating a related new feature request. #2115
Previouly our category methods are hard-coded to use shared image manager. But now I provide a way to pass your custom manager even for these category methods. So you can set different manager for different imageView without override global one.
So that you can create each manager for each type of your transform. Then call the specify one to use. Can this solve your problem ?
let me check it, thanks for your nice work, I'll feed back!
@dreampiggy I watched your implementation and thought about it, I don't know If it is a good solution to this common problem, when you want to download a image once, while you want to transform it to multiple style, and even cache all the transformed image separately.
You might see the Swift image load framework kingfisher, it has a Processor options to specify, and it will automatically transform and cache it separately. Thanks your effort again.
@Josscii I see. Kiingfisher use the benefit from Swift's struct. However in Objective-C, we need to create instance instead (The best way is to create different subclass for specify transform and set delegate to manager itself or provide a block property because it can be reused).
In my opion, actually we can do the same things like those, but looks more complicated. Because these two framework's have a different design pattern and kingfisher do not have such a historic limit.
I add this pull request because I need to seperate cache path for different image request. But I do not want to directly use SDWebImageManager API to build from scratch.
And now I can do this without writing extra code:
NSURL *url1, *url2;
UIImageView *imageView1, *imageView2;
SDImageCache *cache1 = [[SDImageCache alloc] initWithNamespace:@"cachePath1"];
SDImageCache *cache2 = [[SDImageCache alloc] initWithNamespace:@"cachePath2"];
SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
SDWebImageManager *manager1 = [[SDWebImageManager alloc] initWithCache:cache1 downloader:downloader];
MySDWebImageManager *manager2 = [[MySDWebImageManager alloc] initWithCache:cache2 downloader:downloader];
manager1.delegate = self; // is a delegate instead of block, so in the `imageManager:transformDownloadedImage:withURL:`, you need a switch case to check manager instance, and then return the different transformed image
manager2.delegate = manager2; // or better, if the manager transform can be resued, create a subclass. With hard-coded method implementation or a new property block to provide transformer
manager2.transformBlock = ^UIImage *(UIImage *image, NSURL *url){ // some logic... };
[imageView1 sd_internalSetImageWithURL:url1 placeholderImage:nil options:0 operationKey:nil setImageBlock:nil progress:nil completed:nil context:@{SDWebImageInternalCustomManagerKey : manager1}]; // Sadly, Objective-C don't have optional arguments to omit :)
[imageView2 sd_internalSetImageWithURL:url2 placeholderImage:nil options:0 operationKey:nil setImageBlock:nil progress:nil completed:nil context:@{SDWebImageInternalCustomManagerKey : manager2}];
And also, in SD4.2, we introduce a SDWebImageCoder to build your custom coder. The methods are called from global queue so you can just directly use SDImageCahce to cache whatever you want for whatever times for single download image.
@dreampiggy yes, I totally understand your idea, we have to manage multiple manager because of the historic limit. One remain problem is that, if we want to download one image and transform it to multiple style and cache it separately, we might have to download it multiple times?
@Josscii No...Actually you just download once. Because next time you request it from whatever API(except directly call SDWebImageDownloader), it would be created from SDImageCache. It's a memory-disk cache :)
And as I told above, if you are Creative enough. You can directly call SDImageCahce API to save that transformed image to cache anytime. For example, in the delegate methods call. It's a little trick but maybe sometimes we have to use that.
@dreampiggy Ok, I got it. Thanks.
I think sometime we want to have separate process to transform image to whatever we want per uiimageview
I think method signature like this is very easy to use.
@sainttail See #2219 and 5.x branch...
I'm doing some documents related stuff. And maybe I need to ask the @bpoplauschi to release 5.0.0-beta soon. It takes too long time.
Oh, thank for your response, I've never seen that PR.
@dreampiggy hey, after diving into this problem and sd source code, I still think use custom sdwebimagemanager for processing purpose is not the best way.
I think we should add a processor for this, just like kingfisher.
what do you think?
@Josscii See 5.0 feature Image Transformer. It's what you expect for Kingfisher-like Processor. It allow you to chain the pipeline transformer. It works for cache & manager level. It provide many built-in image process util categories.
The 5.0 beta release is delayed because of many issue (personnel issue, code issue, communicate issue as well). I hope it can be released before Jully. But it all depends.
@dreampiggy 馃憣, nice work, I ignored the above messages, sorry.
@dreampiggy I looked into 5.0 feature, and I got a question, why didn't you query the memory cache using the transformed key here ?
@Josscii Seems a bug. I'll fix now. The image cache key should be used when you specify a custom transformer