系列文章目录
iOS基础—多线程:GCD、NSThread、NSOperation
iOS基础—Category vs Extension
iOS基础—常用三方库:Masonry、SDWebImage
文章目录
- 系列文章目录
- 一、Masonry
- 1.Masonry简介
- 2.使用示例
- 3.源码剖析
- 二、SDWebImage
- 1.SDWebImage简介
- 2.主要功能
- 3.使用示例
- 三、实现100张远程图片的展示
- 1.NSURLSession下载
- 2.Masonry布局,SDWebImage加载图片
一、Masonry
1.Masonry简介
定义
SnapKit 和 Masonry 是iOS开发中用于自动布局的流行框架。它们简化了使用 Auto Layout 的代码,使开发者能够更加直观和高效地进行布局设计。(🔗code)
背景
Auto Layout 是Apple提供的一种布局系统,允许开发者定义视图之间的约束,以适应不同屏幕尺寸和设备。SnapKit 和 Masonry 通过链式语法和简化的API,使Auto Layout的使用变得更加方便。
简介
Masonry 是一个Objective-C库,提供了简洁的DSL(领域特定语言)来编写Auto Layout代码。它通过链式语法使得约束的定义更加直观和易读。
主要特性
- 链式语法
Masonry 使用链式语法来定义视图约束,使代码更加紧凑和易读。- 简化的API
提供了简化的API,减少了编写Auto Layout代码的复杂性。- 高可读性
Masonry 的代码风格接近自然语言,提高了代码的可读性和可维护性。
2.使用示例
Masonry 可以通过 CocoaPods 进行安装。在你的 Podfile 中添加以下内容,然后运行 pod install 命令:
pod 'Masonry'
这里我们以请求一张图片作为例子,然后用原生 AutoLayout 以及使用 Masonry 实现同一个布局的代码效果,来对比一下二者:
我们使用原生 AutoLayout 将图片布局到中间,并调整它的大小为长宽300:
// 禁用translatesAutoresizingMaskIntoConstraints,以便使用Auto Layout。
self.imgView.translatesAutoresizingMaskIntoConstraints = NO;// 水平方向居中
NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:self.imgViewattribute:NSLayoutAttributeCenterXrelatedBy:NSLayoutRelationEqualtoItem:self.viewattribute:NSLayoutAttributeCenterXmultiplier:1.0constant:0.0];// 垂直方向居中
NSLayoutConstraint *centerYConstraint = [NSLayoutConstraint constraintWithItem:self.imgViewattribute:NSLayoutAttributeCenterYrelatedBy:NSLayoutRelationEqualtoItem:self.viewattribute:NSLayoutAttributeCenterYmultiplier:1.0constant:0.0];// 添加约束到父视图
[self.view addConstraints:@[ centerXConstraint, centerYConstraint ]];// 设置UIImageView的宽度和高度
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:self.imgViewattribute:NSLayoutAttributeWidthrelatedBy:NSLayoutRelationEqualtoItem:nilattribute:NSLayoutAttributeNotAnAttributemultiplier:1.0constant:300.0]; // 设置为你需要的宽度NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:self.imgViewattribute:NSLayoutAttributeHeightrelatedBy:NSLayoutRelationEqualtoItem:nilattribute:NSLayoutAttributeNotAnAttributemultiplier:1.0constant:300.0]; // 设置为你需要的高度// 添加宽度和高度约束到UIImageView
[self.imgView addConstraints:@[ widthConstraint, heightConstraint ]];
实现的效果图如下:
下面我们使用 Masonry 来实现,这里将大小设置为长宽100,以作区分:
[self.imgView mas_makeConstraints:^(MASConstraintMaker *make) {make.center.equalTo(self.view);make.width.mas_equalTo(100); // 设置为你需要的宽度make.height.mas_equalTo(100); // 设置为你需要的高度}];
我们对比看出,使用 Masonry 比用原生 AutoLayout 来控制布局,代码大大简化了,可读性也更高!所以我们推荐使用 Masonry 来控制布局。
3.源码剖析
Masonry 提供了 mas_makeConstraints,mas_updateConstraints,mas_remakeConstraints 三个入口。
//创建一个MASConstraintMaker并在调用者中包含每个视图。
//定义的任何约束都会在每个视图上执行完成后添加到相应的视图或适当的父视图中。
//@param block 你可以在其中构建你想要应用到每个视图的约束的范围。
//@return 创建的MASConstraints数组。//mas_makeConstraints 是最常用的,通过它我们可以很方便得为一个UIView添加各种约束,mas_makeConstraints一般在viewDidLoad方法中调用来为view添加布局约束。
- (NSArray *)mas_makeConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block;//更新现有的约束。
- (NSArray *)mas_updateConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block;//重新定义约束。这个方法会移除之前的所有约束。
- (NSArray *)mas_remakeConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block;
详细解析可以看这篇文章:基于AutoLayout的开源布局库Masonry源码解析
二、SDWebImage
1.SDWebImage简介
SDWebImage 是一个用于 iOS 和 macOS 应用程序的强大的图像加载和缓存库,主要用于简化图像下载和缓存处理。它提供了异步下载和缓存图像的功能,极大地提高了应用程序的性能和用户体验。(这个库的介绍很详细,强推!!!🔗code)
2.主要功能
- 异步图像下载: SDWebImage 支持异步下载图像,这意味着不会阻塞主线程,从而保持应用程序的响应速度。
- 图像缓存: 提供内存和磁盘缓存,减少重复下载,提高性能。缓存策略可以根据需要进行自定义。
- 图像解码和缩放: 支持图像的异步解码和缩放,进一步优化性能。
- GIF 支持: 内置对 GIF 动态图像的支持,能够方便地加载和显示 GIF 图像。
- 渐进式图像加载: 支持渐进式图像加载,提升用户体验,尤其在网络状况不佳时效果明显。
- URL 缓存: 支持 URL 的缓存,可以避免重复的网络请求。
- 自定义加载器和解码器: 用户可以自定义图像加载器和解码器,以满足特定需求。
3.使用示例
SDWebImage 可以通过 CocoaPods安装,在 podfile 中添加:
pod 'SDWebImage', '~> 5.19'
下面代码用来加载一张图片,如果加载成功的话就会将图片添加到视图上,并且把大小设置为长宽400:
self.imgView = [[UIImageView alloc] init];
NSURL *imageURL = [NSURL URLWithString:@"http://www.pptbz.com/pptpic/UploadFiles_6909/201203/2012031220134655.jpg"];[self.imgView sd_setImageWithURL:imageURLplaceholderImage:[UIImage imageNamed:@"placeholder.png"]options:0completed:^(UIImage *_Nullable image, NSError *_Nullable error, SDImageCacheType cacheType, NSURL *_Nullable imageURL) {if (error){NSLog(@"Error: %@", error.localizedDescription);}else{NSLog(@"Successfully loaded image from %@", imageURL);[self.view addSubview:self.imgView];[self.imgView mas_makeConstraints:^(MASConstraintMaker *make) {make.center.equalTo(self.view);make.width.mas_equalTo(400); // 设置为你需要的宽度make.height.mas_equalTo(400); // 设置为你需要的高度}];}}];
对比用 AFNetworking 来请求一张图片的数据(如下图),用 SDWebImage 的方式更加简洁,并且提供了更多对图片的操作(缓存、异步下载等),来提高性能和体验。
三、实现100张远程图片的展示
我们用两种方式来展示100张图片,并且对比两种方式的区别:
- 使用原生代码实现:NSURLSession下载
- 使用第三方库实现:Masonry布局,SDWebImage加载图片来实现100张远程图片展示
这里将图片存储在图床中,并用 URL 外链进行访问:
- (void)initURLs {self.imageUrlString = @[@"https://s2.loli.net/2024/07/03/xOo3V2btpDKIMJE.jpg",@"https://s2.loli.net/2024/07/03/Cj2P4QA7OKdxDwu.jpg",@"https://s2.loli.net/2024/07/03/ODqIC73HGhKoamF.jpg",@"https://s2.loli.net/2024/07/03/MR7NweS54E6uWln.jpg",@"https://s2.loli.net/2024/07/03/n7TrzS21FAuta5X.jpg",@"https://s2.loli.net/2024/07/03/uI4RdqWAcjPQ5HJ.jpg",@"https://s2.loli.net/2024/07/03/Wr6wzbGoc1q7Zk5.jpg",@"https://s2.loli.net/2024/07/03/kYAfKo2zc3hGvn6.jpg",@"https://s2.loli.net/2024/07/03/j5qmWdMZ9wgP8Rx.jpg",@"https://s2.loli.net/2024/07/03/zWaXxm9S5ulPnyO.png",@"https://s2.loli.net/2024/07/03/cApX69nMWQou5ZB.jpg",@"https://s2.loli.net/2024/07/03/Lvnk1CrNY6F7ZHt.jpg",@"https://s2.loli.net/2024/07/03/oRBtdpFUPJg4wfN.jpg",@"https://s2.loli.net/2024/07/03/Xfhi45C2vJlOosT.jpg",@"https://s2.loli.net/2024/07/03/8d3ILQVC2ntm4yH.jpg",@"https://s2.loli.net/2024/07/03/kQTg3YSBnEAf8eK.jpg",@"https://s2.loli.net/2024/07/03/pTgrsnLCuBPyvMO.jpg",@"https://s2.loli.net/2024/07/03/XHFauvd4tjclNeV.jpg",@"https://s2.loli.net/2024/07/03/l2NcEXotAa6TJDe.png",@"https://s2.loli.net/2024/07/03/2bRKALpBQyEasfY.png",@"https://s2.loli.net/2024/07/03/T5rRAj2vx4kXG37.jpg",@"https://s2.loli.net/2024/07/03/17IkFPbKJAqWdoZ.jpg",@"https://s2.loli.net/2024/07/03/4ahneEkQCx3XvNf.jpg",@"https://s2.loli.net/2024/07/03/8jpYofGT7x4IMXJ.jpg",@"https://s2.loli.net/2024/07/03/TAcKvqRzXeiwYbu.jpg",@"https://s2.loli.net/2024/07/03/ekWu48lnDQyKcML.jpg",@"https://s2.loli.net/2024/07/03/16eZ2ETu8F4sxlL.jpg",@"https://s2.loli.net/2024/07/03/srYwlah712KmTH9.png",@"https://s2.loli.net/2024/07/03/xTwv3WMc4YNj1mR.jpg",@"https://s2.loli.net/2024/07/03/3tQ2YVCKZcsng45.jpg",@"https://s2.loli.net/2024/07/03/B7zXL2YCFOGDTEp.jpg",@"https://s2.loli.net/2024/07/03/TzHwCpIVtgWQBU9.jpg",@"https://s2.loli.net/2024/07/03/tVFAbLPr1dIuq2W.jpg",@"https://s2.loli.net/2024/07/03/cC2mUkxAtrhOBR8.jpg",@"https://s2.loli.net/2024/07/03/ojAyT8d3BLnhzlP.jpg",@"https://s2.loli.net/2024/07/03/Qs9fGTXERZwYgxH.jpg",@"https://s2.loli.net/2024/07/03/UDLHRF6BJwp35d9.jpg",@"https://s2.loli.net/2024/07/03/iKgBUDMXYv1xLVC.jpg",@"https://s2.loli.net/2024/07/03/RFOdw4QWViErYfh.jpg",@"https://s2.loli.net/2024/07/03/lKUPvGbVtDwLeyM.jpg",@"https://s2.loli.net/2024/07/03/EQr9mScjgkDZW5K.jpg",@"https://s2.loli.net/2024/07/03/6Uowmg13CMqZV5Y.jpg",@"https://s2.loli.net/2024/07/03/veKCSZ3G4knpr2R.jpg",@"https://s2.loli.net/2024/07/03/b29yawENv8PQYmA.jpg",@"https://s2.loli.net/2024/07/03/rWjLCyexS51wdkE.jpg",@"https://s2.loli.net/2024/07/03/HPn4wILAqv3kaXJ.jpg",@"https://s2.loli.net/2024/07/03/z2REKiSVLMDFgl6.jpg",@"https://s2.loli.net/2024/07/03/UgoTV9YCrtK1SW3.jpg",@"https://s2.loli.net/2024/07/03/CNz1fXcRSdP7rk5.jpg",@"https://s2.loli.net/2024/07/03/H4jdiRsU7yWGKDV.jpg",@"https://s2.loli.net/2024/07/03/7oTr2FqKhS6VGIM.jpg",@"https://s2.loli.net/2024/07/03/ALcpdOXCYqoh19P.jpg",@"https://s2.loli.net/2024/07/03/acqmhzNXn8YUsur.jpg",@"https://s2.loli.net/2024/07/03/cuk4fD8JHANb752.jpg",@"https://s2.loli.net/2024/07/03/mLycVAsXSidPkgE.jpg",@"https://s2.loli.net/2024/07/03/VGQa5rYnKTXoRsd.jpg",@"https://s2.loli.net/2024/07/03/nUvAJIeKgF2uypV.jpg",@"https://s2.loli.net/2024/07/03/xHTmFaitU3Ycjoy.jpg",@"https://s2.loli.net/2024/07/03/NdhKsIk1pWgyCMv.jpg",@"https://s2.loli.net/2024/07/03/5BwbyOGHdM2aJZ4.jpg",@"https://s2.loli.net/2024/07/03/7Zx9T2NzVJFo5Dp.jpg",@"https://s2.loli.net/2024/07/03/9efuOxy3Gn4jX6D.jpg",@"https://s2.loli.net/2024/07/03/s7XZPtom5l8KUrS.jpg",@"https://s2.loli.net/2024/07/03/MB5Qp4iWyacLACY.jpg",@"https://s2.loli.net/2024/07/03/pLj9w1VW8vYNzXU.jpg",@"https://s2.loli.net/2024/07/03/VINmDBM4b1zvogk.jpg",@"https://s2.loli.net/2024/07/03/MXV9ZlgYFQztcwH.jpg",@"https://s2.loli.net/2024/07/03/1AhCBz43y7UaxiT.jpg",@"https://s2.loli.net/2024/07/03/xOo3V2btpDKIMJE.jpg",@"https://s2.loli.net/2024/07/03/Cj2P4QA7OKdxDwu.jpg",@"https://s2.loli.net/2024/07/03/ODqIC73HGhKoamF.jpg",@"https://s2.loli.net/2024/07/03/MR7NweS54E6uWln.jpg",@"https://s2.loli.net/2024/07/03/n7TrzS21FAuta5X.jpg",@"https://s2.loli.net/2024/07/03/uI4RdqWAcjPQ5HJ.jpg",@"https://s2.loli.net/2024/07/03/Wr6wzbGoc1q7Zk5.jpg",@"https://s2.loli.net/2024/07/03/kYAfKo2zc3hGvn6.jpg",@"https://s2.loli.net/2024/07/03/j5qmWdMZ9wgP8Rx.jpg",@"https://s2.loli.net/2024/07/03/zWaXxm9S5ulPnyO.png",@"https://s2.loli.net/2024/07/03/cApX69nMWQou5ZB.jpg",@"https://s2.loli.net/2024/07/03/Lvnk1CrNY6F7ZHt.jpg",@"https://s2.loli.net/2024/07/03/oRBtdpFUPJg4wfN.jpg",@"https://s2.loli.net/2024/07/03/Xfhi45C2vJlOosT.jpg",@"https://s2.loli.net/2024/07/03/8d3ILQVC2ntm4yH.jpg",@"https://s2.loli.net/2024/07/03/kQTg3YSBnEAf8eK.jpg",@"https://s2.loli.net/2024/07/03/pTgrsnLCuBPyvMO.jpg",@"https://s2.loli.net/2024/07/03/XHFauvd4tjclNeV.jpg",@"https://s2.loli.net/2024/07/03/l2NcEXotAa6TJDe.png",@"https://s2.loli.net/2024/07/03/2bRKALpBQyEasfY.png",@"https://s2.loli.net/2024/07/03/T5rRAj2vx4kXG37.jpg",@"https://s2.loli.net/2024/07/03/17IkFPbKJAqWdoZ.jpg",@"https://s2.loli.net/2024/07/03/4ahneEkQCx3XvNf.jpg",@"https://s2.loli.net/2024/07/03/8jpYofGT7x4IMXJ.jpg",@"https://s2.loli.net/2024/07/03/TAcKvqRzXeiwYbu.jpg",@"https://s2.loli.net/2024/07/03/ekWu48lnDQyKcML.jpg",@"https://s2.loli.net/2024/07/03/16eZ2ETu8F4sxlL.jpg",@"https://s2.loli.net/2024/07/03/srYwlah712KmTH9.png",@"https://s2.loli.net/2024/07/03/xTwv3WMc4YNj1mR.jpg",@"https://s2.loli.net/2024/07/03/3tQ2YVCKZcsng45.jpg",@"https://s2.loli.net/2024/07/03/B7zXL2YCFOGDTEp.jpg",@"https://s2.loli.net/2024/07/03/tVFAbLPr1dIuq2W.jpg",@"https://s2.loli.net/2024/07/03/cC2mUkxAtrhOBR8.jpg",];
}
1.NSURLSession下载
#import "ViewController.h"@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) NSMutableArray<UIImage *> *images;
@property NSMutableArray<NSString *> *imageUrlString;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// 初始化图片数组self.images = [NSMutableArray array];// 创建并配置 UICollectionViewUICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];layout.itemSize = CGSizeMake(50, 50);layout.minimumInteritemSpacing = 10;layout.minimumLineSpacing = 10;layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10); // 设置边距self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];self.collectionView.dataSource = self;self.collectionView.delegate = self;[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];self.collectionView.backgroundColor = [UIColor whiteColor];[self.view addSubview:self.collectionView];// 下载图片[self downloadImages];
}- (void)downloadImages {// 假设我们有一个包含 100 个图片 URL 的数组NSMutableArray<NSURL *> *imageURLs = [NSMutableArray array];[self initURLs];for (int i = 0; i < 100; i++) {NSString *urlString = self.imageUrlString[i];[imageURLs addObject:[NSURL URLWithString:urlString]];}// 创建一个调度组,用于跟踪所有下载任务dispatch_group_t downloadGroup = dispatch_group_create();// 记录开始时间CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();for (NSURL *url in imageURLs) {dispatch_group_enter(downloadGroup);NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {if (data && !error) {UIImage *image = [UIImage imageWithData:data];if (image) {@synchronized (self.images) {[self.images addObject:image];}}}dispatch_group_leave(downloadGroup);}];[task resume];}// 在所有下载任务完成后刷新集合视图dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent();CFTimeInterval executionTime = endTime - startTime;NSLog(@"下载所有图片的耗时: %f 秒", executionTime);[self.collectionView reloadData];});
}#pragma mark - UICollectionViewDataSource- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {return self.images.count;
}- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];UIImageView *imageView = [[UIImageView alloc] initWithFrame:cell.contentView.bounds];imageView.image = self.images[indexPath.item];imageView.contentMode = UIViewContentModeScaleAspectFit;[cell.contentView addSubview:imageView];return cell;
}
@end
这里的运行结果如下,连续运行3次,平均耗时为8s:
下面对代码逻辑进行优化,我们考虑能否下载一张图片就立即刷新到视图上,而非下载完100张图片后,再一次性刷新到视图上,并且在这个过程中验证UI滚动情况下的 collectview 的 cell 复用是否有问题:
这里下载图片后将图片资源保存到一个数组中,并加锁保证数据一致性,然后在主线程中进行视图的刷新,这样的做法保证了下载图片后立即刷新到视图上。另外还遇到了一个问题,图片加载时会出现一个cell中重叠出现几张图片的情况:
解决这个问题只需要将之前cell中的内容移除,然后重新加载就行:
经过验证,UI滚动时图片的刷新无异常,因为异步下载图片完成后,调用了 reloadData 方法,触发了 UICollectionView 的重新加载。通过单元格重用机制,每次滚动时都会重新配置单元格,从而显示最新的图片。在多线程环境下,通过使用 @synchronized 关键字和主线程更新 UI,确保了数据的安全和 UI 的正确刷新。
修改后的代码如下:
//
// ViewController.m
// getPic
//
// Created by 大大怪将军 on 2024/7/7.
//#import "ViewController.h"@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) NSMutableArray<UIImage *> *images;
@property NSMutableArray<NSString *> *imageUrlString;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// 初始化图片数组self.images = [NSMutableArray array];// 创建并配置 UICollectionViewUICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];layout.itemSize = CGSizeMake(100, 100);layout.minimumInteritemSpacing = 10;layout.minimumLineSpacing = 10;layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10); // 设置边距self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];self.collectionView.dataSource = self;self.collectionView.delegate = self;[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];self.collectionView.backgroundColor = [UIColor whiteColor];[self.view addSubview:self.collectionView];// 下载图片[self downloadImages];
}- (void)downloadImages {// 假设我们有一个包含 100 个图片 URL 的数组NSMutableArray<NSURL *> *imageURLs = [NSMutableArray array];[self initURLs];for (int i = 0; i < 100; i++) {NSString *urlString = self.imageUrlString[i];[imageURLs addObject:[NSURL URLWithString:urlString]];}for (NSURL *url in imageURLs) {NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {if (data && !error) {UIImage *image = [UIImage imageWithData:data];if (image) {@synchronized (self.images) {[self.images addObject:image];}dispatch_async(dispatch_get_main_queue(), ^{[self.collectionView reloadData];});}}}];[task resume];}}#pragma mark - UICollectionViewDataSource- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {return self.images.count;
}- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];// 移除旧的UIImageViewfor (UIView *subview in cell.contentView.subviews) {[subview removeFromSuperview];}UIImageView *imageView = [[UIImageView alloc] initWithFrame:cell.contentView.bounds];imageView.image = self.images[indexPath.item];imageView.contentMode = UIViewContentModeScaleAspectFit;[cell.contentView addSubview:imageView];return cell;
}
@end
2.Masonry布局,SDWebImage加载图片
#import "ViewController.h"
#import <SDWebImage/UIImageView+WebCache.h>
#import <Masonry/Masonry.h>@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) NSMutableArray<NSString *> *imageUrlString;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// 初始化图片 URL 数组[self initURLs];// 创建 UICollectionViewFlowLayoutUICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];layout.itemSize = CGSizeMake(100, 100);layout.minimumInteritemSpacing = 10;layout.minimumLineSpacing = 10;// 创建 UICollectionViewself.collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];self.collectionView.dataSource = self;self.collectionView.delegate = self;self.collectionView.backgroundColor = [UIColor whiteColor];[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];[self.view addSubview:self.collectionView];// 使用 Masonry 设置 UICollectionView 布局[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {make.edges.equalTo(self.view);}];
}#pragma mark - UICollectionViewDataSource- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {return self.imageUrlString.count;
}- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];// 移除之前的 UIImageView[cell.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];// 创建 UIImageViewUIImageView *imageView = [[UIImageView alloc] init];imageView.contentMode = UIViewContentModeScaleAspectFill;imageView.clipsToBounds = YES;[cell.contentView addSubview:imageView];// 使用 Masonry 设置 UIImageView 布局[imageView mas_makeConstraints:^(MASConstraintMaker *make) {make.edges.equalTo(cell.contentView);}];// 使用 SDWebImage 加载远程图片NSString *urlString = self.imageUrlString[indexPath.item];NSURL *url = [NSURL URLWithString:urlString];[imageView sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:@"placeholder"]];return cell;
}
@end
运行的结果如下,加载速度非常快,几乎视图一出现,图片就加载到视图上了:
后者的图片是缓存在了 IPhone 的磁盘上,所以退出App后冷启,图片还是能加载出来。但是前者只要 Xcode 程序中止,冷启后图片无法加载,因为是将图片下载保存到一个 UIImage 的数组中,是内存级别的。