IOS development-Do you actually use SDWebImage?
SDWebImage is currently the most popular third-party image download framework, with a high usage rate. But will you actually use it? This article will analyze how to properly use SDWebImage through examples. Use Cases: images on the custom UITableViewCell must be displayed. If the network status is WiFi, the high-definition image is displayed. If the network status is cellular mobile network, the image thumbnail is displayed. Example:
To listen to the network status, we recommend that you use AFNetWorking.
Import a third-party framework AFNetWorking to the project on GitHub or using cocoaPod. Listen to the network status in application: didfinishlaunchingwitexceptions: Method in the AppDelegate. m file.
// AppDelegate. in the m file-(BOOL) application :( UIApplication *) application didfinishlaunchingwitexceptions :( NSDictionary *) launchOptions {// monitor the network status [[AFNetworkReachabilityManager sharedManager] startMonitoring];} // The following code uses AFNetworkReachabilityManager * mgr = [AFNetworkReachabilityManager sharedManager] In the method to listen to the network status; if (mgr. isReachableViaWiFi) {// when using Wifi, download the source image} else {// others, download the thumbnail }}
At this time, iOS learners start to complain: Isn't it very easy? Therefore, the following code is completed.
// Use MVC to download the image-setItem :( CustomItem *) item {_ item = item; UIImage * placeholder = [UIImage imageNamed: @ "placeholderImage"]; AFNetworkReachabilityManager * mgr = [AFNetworkReachabilityManager sharedManager]; if (mgr. isReachableViaWiFi) {// download the source image [self. imageView sd_setImageWithURL: [NSURL URLWithString: item. originalImage] placeholderImage: placeholder];} else {// others, download the thumbnail [self. imageView sd_setImageWithURL: [NSURL URLWithString: item. thumbnailImage] placeholderImage: placeholder] ;}}
At this point, the corresponding picture can be downloaded basically according to the current network status, but it is unreasonable in actual development. Note the following:
SDWebImage will automatically help developers cache images (including memory cache and sandbox cache), so we need to set
WiFi
Environment download
HD Image
Next time
Cellular Network Status
The following application should be displayed.
HD Image
Instead of downloading thumbnails. Many application setting modules provide one function:
HD images are still displayed in the mobile network environment
. This function is actually to record the settings in the sandbox. If you want to save the data to your local computer, you can view my home article in another short book.
IOS local data access is enough here. When the user is offline, the service cannot be properly processed.
Therefore, we began to improve it. To make it easier for readers to understand, I first post the pseudocode:
-SetItem :( CustomItem *) item {_ item = item; if (the source image is cached) {self. imageView. image = source image;} else {if (Wifi environment) {download display source image} else if (mobile phone built-in Network) {if (still download source image in 3G/4G environment) {download display source image} else {download display thumbnail} else {if (Cache has thumbnail) {self. imageView. image = thumbnail;} else // process the offline status {self. imageView. image = placeholder image ;}}}}
Implement the above pseudo code: the reader can correspond to the above pseudo code one by one. Recommended during exercises
Write pseudocode first
, And pay more attention to writing real code.
Note
Description.
-SetItem :( CustomItem *) item {_ item = item; // placeholder image UIImage * placeholder = [UIImage imageNamed: @ "placeholderImage"]; // obtain the source image from the memory/sandbox cache. UIImage * originalImage = [[SDImageCache nvidimagecache] imageFromDiskCacheForKey: item. originalImage]; if (originalImage) {// if the source image is cached in the memory \ sandbox, the source image is directly displayed (no matter what network status it is) self. imageView. image = originalImage;} else {// memory \ sandbox cache no source image AFNetworkReachabilityManager * mgr = [AFNetworkReachabilityManager sharedManager]; if (mgr. isReachableViaWiFi) {// download the source image [self. imageView sd_setImageWithURL: [NSURL URLWithString: item. originalImage] placeholderImage: placeholder];} else if (mgr. isReachableViaWWAN) {// use the built-in network of the mobile phone. // assume that nsuserults ults is stored in the sandbox. // [[NSUserDefaults standardUserDefaults] setBool: NO forKey: @ "alwaysDownloadOriginalImage"]; // [[NSUserDefaults standardUserDefaults] synchronize]; # warning reads the user's configuration items from the sandbox: whether to download the source image BOOL alwaysDownloadOriginalImage = [[NSUserDefaults standardUserDefaults] boolForKey: @ "alwaysDownloadOriginalImage"]; if (alwaysDownloadOriginalImage) {// download the source image [self. imageView sd_setImageWithURL: [NSURL URLWithString: item. originalImage] placeholderImage: placeholder];} else {// download the thumbnail [self. imageView sd_setImageWithURL: [NSURL URLWithString: item. thumbnailImage] placeholderImage: placeholder] ;}} else {// No network UIImage * thumbnailImage = [[SDImageCache includimagecache] imageFromDiskCacheForKey: item. thumbnailImage]; if (thumbnailImage) {// memory \ sandbox cache contains the thumbnail self. imageView. image = thumbnailImage;} else {// process the offline status, and whether there is any cache self. imageView. image = placeholder ;}}}}
Solved? The real pitfalls have just begun. Before expressing the above Code, let's analyze the cache mechanism of UITableViewCell. See: There is a tableView in progress
At the same time
Three uitableviewcells are displayed.
tableViewCell
Contains a sub-Control of imageView, and each cell has
Corresponding Model attributes
Set the image content of the imageView.
Note:
: The cache pool is empty because no cell is pushed to the screen.
When a cell is pushed out of the screen, the system will automatically put the cell into the automatic cache pool.Note:
: The UIImage data model corresponding to the cell is not cleared! Or point to the previous cell.
When the next cell enters the screen, the system will find the corresponding cell based on the ID registered by tableView and apply it. The cell that enters the cache pool isRepeat
Add to tableView, in the Data Source method of tableViewtableView: cellForRowAtIndexPath:
CellCorresponding Model
Data. In this case, the cell corresponds
The above is a brief introduction to the tableView reuse mechanism. Come back, so where is the real pitfalls mentioned above? Describe it in one scenario: when a user is in a Wi-Fi network speed
Not fast enough
(Images cannot be downloaded immediately). In the above Code, the large image is downloaded in a Wi-Fi environment. Therefore
A certain amount of time
To complete the download. At this time, users don't want to wait.
Last opened
The image displayed when the App is in
Cell below
. Note:
In this case, the cell image download operation is not paused and is still in the image download status.
. When you are viewing
Last App opened
When the image is displayed (the image downloaded last time the App was opened, SDWebImage will help us cache the image without downloading it), and the image needs to be displayed.
The image when the App was last opened
The cell uses tableView
Reuse Mechanism
The cell obtained from the cache pool remains in
Cell above
When the high-definition image has been downloaded, the default practice of SDWebImage is to immediately set the downloaded image to ImageView, so we will display the above image in the cell shown below at this time, resulting in
Data disorder
This is a very serious BUG. So how can we solve this thorny problem? If we can use this cell out of the cache pool
Download operation
Remove, so there will be no data disorder. At this time, you may ask me: how to remove the download operation? Does the download operation help us with SDWebImage? That's right. SDWebImage helped us download images. Let's take a look at the SDWebImage source code to see how it was done.
-(Void) sd_setImageWithURL :( NSURL *) url placeholderImage :( UIImage *) placeholder options :( SDWebImageOptions) options progress :( SDWebImageDownloaderProgressBlock) progressBlock completed :( SDWebImageCompletionBlock) completedBlock {// disable the current image download operation [self sd_cancelCurrentImageLoad]; objc_setAssociatedObject (self, & imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); if (! (Options & SDWebImageDelayPlaceholder) {dispatch_main_async_safe (^ {self. image = placeholder;});} if (url) {// check if activityView is enabled or not if ([self showActivityIndicatorView]) {[self addActivityIndicator];} _ weak _ typeof (self) wself = self; id
Operation = [SDWebImageManager. sharedManager downloadImageWithURL: url options: options progress: progressBlock completed: ^ (UIImage * image, NSError * error, SDImageCacheType cacheType, BOOL finished, NSURL * imageURL) {[wself removeActivityIndicator] if (! Wself) return; dispatch_main_sync_safe (^ {if (! Wself) return; if (image & (options & SDWebImageAvoidAutoSetImage) & completedBlock) {completedBlock (image, error, cacheType, url); return;} else if (image) {wself. image = image; [wself setNeedsLayout];} else {if (options & SDWebImageDelayPlaceholder) {wself. image = placeholder; [wself setNeedsLayout] ;}} if (completedBlock & finished) {completedBlock (image, error, cacheType, url) ;}}) ;}]; [self injection: operation forKey: @ "UIImageViewImageLoad"];} else {callback (^ {[self removeActivityIndicator]; if (completedBlock) {NSError * error = [NSError errorWithDomain: SDWebImageErrorDomain code:-1 userInfo: @ {NSLocalizedDescriptionKey: @ "Trying to load a nil url"}]; completedBlock (nil, error, SDImageCacheTypeNone, url );}});}}
We are surprised to find that when SDWebImage downloads images, the first thing is to disable the current download operation of imageView! Are you beginning to lament how amazing SDWebImage is? That's right. We only need to set the code we wrote to directly access the local cache code using SDWebImage! The following is the complete version code.
-SetItem :( CustomItem *) item {_ item = item; // placeholder image UIImage * placeholder = [UIImage imageNamed: @ "placeholderImage"]; // obtain the original image UIImage * originalImage = [[SDImageCache nvidimagecache] imageFromDiskCacheForKey: item. originalImage]; if (originalImage) {// if the memory \ sandbox caches the source image, the source image will be displayed directly (no matter what network status it is) [self. imageView sd_setImageWithURL: [NSURL URLWithString: item. originalImage] placeholderImage: placeholder];} else {// memory \ sandbox cache no source image AFNetworkReachabilityManager * mgr = [AFNetworkReachabilityManager sharedManager]; if (mgr. isReachableViaWiFi) {// download the source image [self. imageView sd_setImageWithURL: [NSURL URLWithString: item. originalImage] placeholderImage: placeholder];} else if (mgr. isReachableViaWWAN) {// use the built-in network of the mobile phone. // assume that nsuserults ults is stored in the sandbox. // [[NSUserDefaults standardUserDefaults] setBool: NO forKey: @ "alwaysDownloadOriginalImage"]; // [[NSUserDefaults standardUserDefaults] synchronize]; # warning reads the user's configuration items from the sandbox: whether to download the source image BOOL alwaysDownloadOriginalImage = [[NSUserDefaults standardUserDefaults] boolForKey: @ "alwaysDownloadOriginalImage"]; if (alwaysDownloadOriginalImage) {// download the source image [self. imageView sd_setImageWithURL: [NSURL URLWithString: item. originalImage] placeholderImage: placeholder];} else {// download the thumbnail [self. imageView sd_setImageWithURL: [NSURL URLWithString: item. thumbnailImage] placeholderImage: placeholder] ;}} else {// No network UIImage * thumbnailImage = [[SDImageCache includimagecache] imageFromDiskCacheForKey: item. thumbnailImage]; if (thumbnailImage) {// memory \ sandbox cache contains a thumbnail [self. imageView sd_setImageWithURL: [NSURL URLWithString: item. thumbnailImage] placeholderImage: placeholder];} else {[self. imageView sd_setImageWithURL: nil placeholderImage: placeholder] ;}}}
This article introduces you here. If you have any questions or errors, please note. If you like it, just pay attention to it.