I've been struggling with this problem for a couple of days now. Previously I used AFNetworking category to load and cache images, but it doesn't provide a cache type in it's callbacks. So I used to keep track in each controller which images have been already loaded. I looked through SDWebImage and it provides exactly what I was looking for - SDImageCacheType
.
Now i'm trying to come up with an easy to use (hopefully a one-liner) solution to load images in table and collection views with animation if they are downloaded or come from the disk cache. Basically these tasks take some time to get actual image, when memory cache provide images instantaneously, hence no animation needed when image comes from memory.
I've came up with this UIImageView category, and I would like someone more experienced to provide some feedback. It does seem to work as intended. I'm not sure that I'm reinventing the wheel here, or this approach might somehow backfire under certain circumstances.
In outline is does two separate things. In completion block, when image is ready to use, it sets animation duration depending on what cache level has been used. The second idea is for reusable cell mechanism, imageView keeps track of the loading operation with help of associated objects and if there's an ongoing task it cancels it. This was not needed with AFNetworking solution, since it cancels ongoing image loads by default when you set a new one.
I don't like the fact, that operation is retained, it's not supposed to be, but associated objects don't provide a __weak
type of reference afaik. OBJC_ASSOCIATION_ASSIGN
key seems to make object __unsafe_unretained
and leads to a bad access crash.
Any advice would be appreciated.
static char kOperationKey;
- (id <SDWebImageOperation>)scrb_setImageAnimatedWithURL:(NSURL *)url {
id <SDWebImageOperation> lastOperation = objc_getAssociatedObject(self, &kOperationKey);
if (lastOperation) {
[lastOperation cancel];
}
id <SDWebImageOperation> operation = [[SDWebImageManager sharedManager] downloadImageWithURL:url options:SDWebImageRetryFailed progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (finished && image) {
BOOL animated = NO;
if (cacheType == SDImageCacheTypeDisk || cacheType == SDImageCacheTypeNone) {
animated = YES;
}
if (animated) {
[UIView transitionWithView:self duration:ANIMATION_DURATION options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
self.image = image;
} completion:nil];
} else {
self.image = image;
}
}
}];
objc_setAssociatedObject(self, &kOperationKey, operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return operation;
}
---UPDATE---
With help from @n00neimp0rtant I've changed my category to this:
- (void)scrb_setImageAnimatedWithURL:(NSURL *)url {
[self sd_cancelCurrentImageLoad];
self.alpha = 0.0;
[self sd_setImageWithURL:url placeholderImage:nil options:SDWebImageRetryFailed completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (image) {
BOOL animated = NO;
if (cacheType == SDImageCacheTypeDisk || cacheType == SDImageCacheTypeNone) {
animated = YES;
}
self.image = image;
if (animated) {
[UIView animateWithDuration:ANIMATION_DURATION animations:^{
self.alpha = 1.0;
}];
} else {
self.alpha = 1.0;
}
}
}];
}