iOS解决方案只有在尚未使用SDWebImage在内存中缓存时才能使用动画加载图像

我几天来一直在努力解决这个问题。 以前我使用AFNetworking类来加载和缓存图像,但它没有在它的回调中提供缓存类型。 所以我曾经在每个控制器中跟踪哪些图像已经加载。 我浏览了SDWebImage,它提供了我正在寻找的东西 – SDImageCacheType

现在我试图想出一个易于使用(希望是一个单行)的解决方案来加载表格和集合视图中的图像,如果它们被下载或来自磁盘缓存。 基本上,当内存缓存瞬间提供图像时,这些任务需要一些时间才能获得实际图像,因此当图像来自内存时不需要动画。

我想出了这个UIImageView类别,我希望有更有经验的人提供一些反馈。 它似乎按预期工作。 我不确定我是在这里重新发明轮子,否则这种做法在某些情况下可能会适得其反。

概述是做两件事。 在完成块中,当图像准备好使用时,它根据使用的缓存级别设置动画持续时间。 第二个想法是可重复使用的单元机制,imageView在相关对象的帮助下跟踪加载操作,如果有正在进行的任务则取消它。 AFNetworking解决方案不需要这样做,因为默认情况下,当您设置新的图像时,它会取消正在进行的图像加载。 我不喜欢这样的事实,即操作被保留,它不应该是,但是关联的对象不提供__weak类型的引用afaik。 OBJC_ASSOCIATION_ASSIGN键似乎使对象__unsafe_unretained并导致错误的访问崩溃。

任何意见,将不胜感激。

 static char kOperationKey; - (id )scrb_setImageAnimatedWithURL:(NSURL *)url { id  lastOperation = objc_getAssociatedObject(self, &kOperationKey); if (lastOperation) { [lastOperation cancel]; } id  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 —

@ n00neimp0rtant的帮助下,我将我的类别更改为:

 - (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; } } }]; } 

我偶然发现了这一点并且已经在各种项目中做到了这一点。 这里的关键是阻止图像自动设置的选项。 然后我们检查缓存类型并做一个很棒的交叉淡入淡出。 您还可以在加载内容时设置占位符图像,或者如果在加载某些内容时需要纯色背景,则可以设置图像视图的背景颜色。

 [self.mediaImage sd_setImageWithURL:[NSURL URLWithString:content.previewUrl] placeholderImage:nil options:SDWebImageAvoidAutoSetImage completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { if ([imageURL isEqual:[NSURL URLWithString:content.previewUrl]]) { if (cacheType == SDImageCacheTypeMemory) { self.mediaImage.image = image; } else { [UIView transitionWithView:self.mediaImage duration:.3 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{ self.mediaImage.image = image; } completion:nil]; } } }]; 

您使用SDWebImageManager手动下载图像的任何特殊原因? SDWebImage已经提供了一个UIImageView类,其中包含可以直接在UIImageView上调用的方法。

这就是我如何解决这个问题:

 #import  // let's say we wanted to do a fade-in animation imageView.alpha = 0.f; [imageView sd_setImageWithURL:url completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { if (cacheType == SDImageCacheTypeNone) { [UIView animateWithDuration:0.3 animations:^{ imageView.alpha = 1.f; }]; } else { imageView.alpha = 1.f; } }]; 

另外,作为旁注,执行持续时间为0.0的动画可能在逻辑上是瞬时的,但iOS仍然会引入整个动画操作的开销,这不仅会降低性能,还会增加一点延迟,否则会没有动画就看不到。

使用这里列出的答案,我能够为swift整理一个版本。

 imageView.sd_setImage(with: imageURL, placeholderImage: imageView.image, options: .avoidAutoSetImage, completed: { (image, error, cache, url) in UIView.transition(with: self, duration: 1.35, options: .transitionCrossDissolve, animations: { self.imageView.image = image }, completion: nil) })