iOS基础---常用三方库:Masonry、SDWebImage

系列文章目录

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简介

定义

SnapKitMasonry 是iOS开发中用于自动布局的流行框架。它们简化了使用 Auto Layout 的代码,使开发者能够更加直观和高效地进行布局设计。(🔗code)

背景

Auto Layout 是Apple提供的一种布局系统,允许开发者定义视图之间的约束,以适应不同屏幕尺寸和设备。SnapKitMasonry 通过链式语法和简化的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_makeConstraintsmas_updateConstraintsmas_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.主要功能

  1. 异步图像下载: SDWebImage 支持异步下载图像,这意味着不会阻塞主线程,从而保持应用程序的响应速度。
  2. 图像缓存: 提供内存和磁盘缓存,减少重复下载,提高性能。缓存策略可以根据需要进行自定义。
  3. 图像解码和缩放: 支持图像的异步解码和缩放,进一步优化性能。
  4. GIF 支持: 内置对 GIF 动态图像的支持,能够方便地加载和显示 GIF 图像。
  5. 渐进式图像加载: 支持渐进式图像加载,提升用户体验,尤其在网络状况不佳时效果明显。
  6. URL 缓存: 支持 URL 的缓存,可以避免重复的网络请求。
  7. 自定义加载器和解码器: 用户可以自定义图像加载器和解码器,以满足特定需求。

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 的数组中,是内存级别的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/51004.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不同WEB下的的ApplicationContext的选择

依赖 ApplicationContext类型选择 默认情况下&#xff0c;spring通过选择的web端的框架来选择使用哪个ApplicationContext子类&#xff0c;默认情况下我们一般使用spring mvc框架&#xff0c;这个时候AC的实现类为 org.springframework.boot.web.servlet.context.AnnotationC…

在 LCD 上显示 png 图片-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板

在 LCD 上显示 png 图片 PNG 简介 无损压缩&#xff1a;PNG 使用 LZ77 派生算法进行无损压缩&#xff0c;确保图像质量不受损&#xff0c;且压缩比高 体积小&#xff1a;通过高压缩比&#xff0c;PNG 文件体积小&#xff0c;适合网络传输 索引彩色模式&#xff1a;PNG-8 格式…

计划外投料

计划外领料通过A07 R10发料/其他&#xff0c;进行计划外投料。移动类型仍然是261。 在科目分配页签。维护计划外投料的工单号。 在实际业务中&#xff0c;有的企业也会单独复制出一个移动类型。进行区分。 在COOIS中&#xff0c;有张表可以看出物料是计划内投料还是计划外。 …

MySQL查询执行(三):显示随机消息

假设有如下表结构&#xff1a; -- 创建表words CREATE TABLE words (id int(11) NOT NULL AUTO_INCREMENT,word varchar(64) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB;--数据生成存储过程 delimiter ;; create procedure idata() begindeclare i int;set i0;while i<…

[工具]GitHub + PicGo 搭建免费博客图床

文章目录 起因GitHub新建GitHub仓库新建token授予picgo权限 PicGOPicGO上传失败原因 起因 还是觉得个人博客记录最好还是不要money&#x1f625;&#xff0c;所以还是想白嫖&#xff0c;找到了GitHub PicGO的方式&#xff0c;记录一下。 GitHub 过程和搭建博客链接类似&…

DDOS攻击学习 - kali初学

文章目录 本地ssh配置nmap(网络连接的工具)nmap -sP IP地址nmap -p 1-65535 -A IP地址主机发现Ping扫描端口扫描时序扫描常用扫描方式指纹识别与探测全端口版本探测防火墙/IDS逃逸报文分段信息收集IP信息收集WHOIS查询数据库渗透测试MySQL列举数据库列举MySQL变量发起请求目录扫…

PostgreSQL的pg-collector工具

PostgreSQL的pg-collector工具 pg-collector 是一个用于 PostgreSQL 数据库的监控和数据收集工具。它主要用于收集 PostgreSQL 实例的性能指标、查询统计和日志信息&#xff0c;以便进行数据库性能分析和故障排查。通过收集这些数据&#xff0c;管理员可以更好地了解数据库的运…

day3 测试基础知识

1. 你认为性能测试的目的是什么&#xff1f;做好性能测试的工作的关键是什么&#xff1f; 性能测试工作的目的是检查系统是否满足在需求说明书中规定的性能&#xff0c;性能测试常常需要和强度测试结合起来&#xff0c;并常常要求同时进行软件和硬件的检测。 性能测试主要的关…

关于SpringBoot项目利用阿里EasyExcel快捷导入Excel文件入库初始化数据的简单实现

一、问题描述 无论新项目还是旧项目&#xff0c;都会出现数据维护、数据初始化等操作&#xff0c;手动录显然很low(领导会骂你)&#xff0c;所以一般采用批量导入导出。这里你还在用原始读取excel逐行逐列去读取吗&#xff1f;2024了ok&#xff1f;利用工具是我们cv大师的一贯…

在 OpenEuler24.03 源码安装 PG16.3

在ANOLIS 23上源码安装了16.1&#xff0c;在OpenEuler24.03上PG16.3&#xff0c;安装也是一样的吗&#xff1f; 抱着这样的态度&#xff0c;我试​着去安装&#xff0c;如果不关闭SELINUX&#xff0c;还是有一个差异的&#xff0c;同时&#xff0c;发现即使是最小安装&#xf…

ElasticSearch学习篇15_《检索技术核心20讲》进阶篇之TopK检索

背景 学习极客实践课程《检索技术核心20讲》https://time.geekbang.org/column/article/215243&#xff0c;文档形式记录笔记。 相关问题&#xff1a; ES全文检索是如何进行相关性打分的&#xff1f;ES中计算相关性得分的时机?如何加速TopK检索&#xff1f;三种思路 精准To…

GEE:设置ui.Map.Layer上交互矢量边界填充颜色为空,只显示边界

一、目标 最近在GEE的交互功能鼓捣一些事情&#xff0c;在利用buffer功能实现了通过选点建立一个矩形后&#xff0c;需要将该矩形填充颜色设为空&#xff0c;只留边界。 然而通过正常设置layer的可视化参数并不能实现这一目的。因此只能另辟蹊径&#xff0c;改为定义矢量边界…

【JavaEE精炼宝库】 网络编程套接字——初识网络编程 | UDP数据报套接字编程

文章目录 一、网络编程基础1.1 网络编程的意义&#xff1a;1.2 网络编程的概念&#xff1a;1.3 网络编程的术语解释&#xff1a;1.4 常见的客户端服务端模型&#xff1a; 二、Socket 套接字2.1 Socket 套接字的概念&#xff1a;2.2 Socket 套接字的分类&#xff1a; 三、UDP数据…

fetchApi === 入门篇

目录 fetch 基本认知 fetch 如何使用 Response对象&#xff08;了解&#xff09; 常见属性 常见方法 fetch 配置参数 fetch发送post请求 fetch 函数封装 fetch 实战 - 图书管理案例 渲染功能 添加功能 删除数据 完整代码 fetch 基本认知 思考&#xff1a; 以前开发…

NumpyPandas:Pandas库(25%-50%)

目录 前言 一、列操作 1.修改变量列 2.筛选变量列 3.删除变量列 4.添加变量列 二、数据类型的转换 1.查看数据类型 2.将 ok的int类型转换成float类型 3.将ar的float类型转换成int类型 三、建立索引 1.建立DataFrame时建立索引 2.在读入数据时建立索引 3.指定某列或…

virtualbox ubuntu扩充磁盘大小

首先在虚拟存储管理里面修改磁盘大小 然后安装gparted sudo gparted 打开管理工具 选中要调整的区域右键选择调整区域大小 拖动上述位置就可以实现扩容。完成后点击应用 然后重启虚拟机即可。

基于深度学习技术及强大的专家团队,针对多个工业垂类场景进行算法优化的智慧城管开源了。

智慧城管视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。 基于深度学习技…

微服务的入门

带着问题进行学习&#xff1a; 1. 对服务进行拆分后&#xff0c;物理上是隔离的&#xff0c;数据上也是隔离的&#xff0c;如何进行不同服务之间进行访问呢&#xff1f; 2.前端是怎么样向后端发送请求的&#xff1f; 通过http请求&#xff0c;通过url&#xff0c;请求的…

wireshark--流量分析利器

&#x1f3bc;个人主页&#xff1a;金灰 &#x1f60e;作者简介:一名简单的大一学生;易编橙终身成长社群的嘉宾.✨ 专注网络空间安全服务,期待与您的交流分享~ 感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持&#xff01;❤️ &#x1f34a;易编橙终身成长社群&#…

传输层(port)UDP/TCP——解决怎么发,发多少,出错了怎么办

**传输层&#xff1a;**负责数据能够从发送端传输接收端. 传输层所封装的报头里一定有&#xff1a;源端口号和目的端口号的。 **端口号&#xff1a;**可以标识一台主机中的唯一一个进程&#xff08;运用程序&#xff09;&#xff0c;这样当数据传输到传输层的时候就可以通过端…