计算机知识

当前位置:澳门新葡萄京 > 计算机知识 > 因为cell的重用机制

因为cell的重用机制

来源:http://www.hhmtch.com 作者:澳门新葡萄京 时间:2020-05-06 06:04

第五天任务

今天主要完成精华页面中cell内内容的处理。

  1. cell高度的计算
  2. cell中间内容的显示
  3. 精华模块的重构
  4. 查看图片
  5. 保存图片到相册

cell高度的计算

cell间距的设置,每个cell之间有10的间距,因为cell的重用机制,我们发现即使在tableView :didDeselectRowAtIndexPath方法中通过点击cell,减少cell的高度,当cell重新显示的时候还是会变回原来的高度,并且系统内部对cell进行了一些处理,已经在内部设置好cell的frame,所以我们通过重写cell的setFrame方法对系统设置cell的frame进行拦截,我们先做一些处理,然后在让系统设置。

//重写这个方法的目的: 能够拦截所有设置cell frame的操作- setFrame:frame{ // 先设置cell的高度减10,然后在让系统内部设置。 frame.size.height -= XMGMargin; [super setFrame:frame];}

cell高度的计算cell的高度计算需要根据每一个cell的不同内容进行计算,模型中添加type属性,用来区别cell中间内容。这里使用枚举。

typedef NS_ENUM(NSInteger , CLTopicType) { /** 全部 */ CLTopicTypeAll = 1, /** 图片 */ CLTopicTypePicture = 10, /** 段子 */ CLTopicTypeWord = 29, /** 音频 */ CLTopicTypeVoice = 31, /** 视频 */ CLTopicTypeVideo = 41,};

/** 中间内容类型 */@property(nonatomic,assign)CLTopicType type;

另外我们可以将cell高度计算分为五部分,看下图

图片 1cell高度计算分析

而cell的内容,文字,图片高度等只能在模型中拿到,所以在模型中添加cellHeight属性和contentF属性,重写cellHeight的get方法计算cell的高度。并且将计算好中间内容的fram用contentF存储起来,用来之后在cell中设置中间内容的frame。

计算高度的代码,其中需要注意的地方都已经写了注释。

// cell高度的计算-cellHeight{ // iOS8 开始cell不会缓存 cell的高度,每次显示cell都会来到这里计算一下,造成不必要计算 // 如果计算过一次高度就不要在计算了,直接返回模型的高度即可。 // if (_cellHeight)return _cellHeight; if (_cellHeight == 0) { // 1.头像高度 _cellHeight = 56; // 2.文字高度 需要提供文字的大小和显示的宽度 CGFloat textMaxW = [UIScreen mainScreen].bounds.size.width - 2 * CLMargin; // 需要给一个较大值的高度,如果计算出来的高度小于这个高度就会使用计算出来的高度 CGSize textMaxSize = CGSizeMake(textMaxW, MAXFLOAT); // CGSize textSize = [self.text sizeWithFont:[UIFont systemFontOfSize:15] constrainedToSize:textMaxSize]; CGSize textSize = [self.text boundingRectWithSize:textMaxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:15]} context:nil].size; _cellHeight  = textSize.height   CLMargin; // 3. 图片的高度,需要判断有没有图片显示 if (self.type != CLTopicTypeWord) { // 图片高度需要根据能显示的最大宽度等比进行计算 中间内容高度 = 中间内容宽度 * 图片实际高度 / 图片实际宽度 CGFloat Height = textMaxW * self.height / self.width; // 判断是否是大图,如果是大图则高度设置为250 if (Height >= [UIScreen mainScreen].bounds.size.height) { Height = 250; self.isBigPicture = YES; } self.contentF = CGRectMake(CLMargin, _cellHeight, textMaxW, Height); _cellHeight  = Height   CLMargin; } // 4. 最热评论高度计算 if (self.top_cmt) { // 4.1 最热评论标题高度 18 _cellHeight  = 18; // 如果最热评论是音频 NSString *contentText = topic.top_cmt.content; // 如果音频url有长度,说明是语音评论,需要将高度计算其中,防止用户名过长,热门评论高度计算不对 if (topic.top_cmt.voiceuri.length) { contentText = @"[语音消息]"; } // 4.2 最热评论内容高度 NSString *topCmtContent = [NSString stringWithFormat:@"%@ : %@",self.top_cmt.user.username,contentText]; // CGSize topCmtContentSize = [topCmtContent sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:textMaxSize]; CGSize topCmtContentSize = [topCmtContent boundingRectWithSize:textMaxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:14]} context:nil].size; _cellHeight  = topCmtContentSize.height   CLMargin; } // 5. 底部工具条   cell之间的间距10 _cellHeight  = 35   CLMargin; } return _cellHeight;}

最后在tableView: heightForRowAtIndexPath:中拿到模型直接返回cellHeight即可

-tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ // 直接返回cell的高度即可 return self.topicArr[indexPath.row].cellHeight;}

至此cell的高度已经根据每个cell显示内容不同而决定,在这里需要强调一个问题:cell的高度没有必要再每次显示的时候都重新计算一遍,所以先对cellHeight进行判断,如果有值则直接返回即可,没有值在进行计算,避免不必要且耗时的计算。

cell中间内容的显示

cell中间内容分为四大模块,视频、音频、图片、段子。段子没有图片显示,我们使用xib来分别描述视频,音频,和图片的显示。如图

图片 2视频xib图片 3音频xib图片 4图片xib

这里图片里面又分为普通图片、gif图片、长图。需要根据图片的不同判断gif标识ImageView和点击查看大图Button是否隐藏。

因为之前在计算cell高度的时候使用模型中属性contentF存储了中间内容的frame,在CLTopicCell中的setTopic:方法中通过判断中间内容的类型,决定显示的内容

#pragma mark - 中间数据类型 if (topic.type == CLTopicTypeVideo) { self.videoView.hidden = NO; self.videoView.frame = topic.contentF; self.videoView.topic = topic; self.voiceView.hidden = YES; self.pictureView.hidden = YES; }else if (topic.type == CLTopicTypeVoice){ self.videoView.hidden = YES; self.voiceView.hidden = NO; self.voiceView.frame = topic.contentF; self.voiceView.topic = topic; self.pictureView.hidden = YES; }else if (topic.type == CLTopicTypeWord){ self.videoView.hidden = YES; self.voiceView.hidden = YES; self.pictureView.hidden = YES; }else if (topic.type == CLTopicTypePicture){ self.videoView.hidden = YES; self.voiceView.hidden = YES; self.pictureView.hidden = NO; self.pictureView.frame = topic.contentF; self.pictureView.topic = topic; }

注意:因为cell的重用机制,所以中间内容为video的cell很有可能重用到其他内容的cell中,所以需要显示自己内容的同时,隐藏其他两种内容的view,防止发生错乱,其中段子cell中没有图片显示,需要将其他三种cell的view全部隐藏。

另外:在这里根据模型中存储的中间内容的frame设置中间内容view的frame,此时发现,虽然我们计算好的中间内容的frame是正确的,但是显示在cell中的frame,只有x,y值正确,width和height不正确。这是因为在xib中使用了自动布局,从xib中加载进来的控件的autoresizingMask默认是UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight ,会自动将控件根据父控件进行伸缩,所以造成了width和height不正确。只需要在-awakeFromNib方法中取消其伸缩效果即可self.autoresizingMask = UIViewAutoresizingNone;

中间内容图片的显示

中间内容的图片url可以通过模型拿到,所以给三种类型的View添加模型属性,并在cell中根据类型设置view显示的时候,将模型赋值给view的模型属性,拿到模型属性即可拿到中间图片的url。视频和音频服务器也提供一张图片供显示,根据服务器返回得图片url赋值给iamgeView即可。

图片的设置稍有些复杂,数据库返回给我们三种图片,小图,中图和原图,我们这里先使用原图。在View的setTopic方法中设置imageView的图片即可。

其中图片需要添加判断是否为gif图片和是否为长图。

// 判断是否为gifif (topic.is_gif) { self.gifImageView.hidden = NO;}else{ self.gifImageView.hidden = YES;}// 判断是否为大图if(topic.isBigPicture){ self.seeBigButton.hidden = NO; // 设置imageView的裁剪,以显示顶部为准 self.imageView.contentMode = UIViewContentModeTop; self.imageView.clipsToBounds = YES;}else{ self.seeBigButton.hidden = YES; // 如果不是需要将大图的设置还原,防止cell重用设置 self.imageView.contentMode = UIViewContentModeScaleToFill; self.imageView.clipsToBounds = NO;}

其中判断gif服务器提供了是否为gif的属性,直接判断即可,判断是否为大图,需要我们自己添加isBigPicture属性,并且回到设置cell高度的方法,如果中间内容的高度超过一个屏幕高度,则表示是长图,设置isBigPicture为YES。并且同样需要注意cell重用的问题,设置显示gif标识和查看大图button显示就需要在相对的方法中设置隐藏,防止cell重用时发生错乱。

中间内容显示的一些细节处理此时中间内容已经可以显示,但是还是需要做一些细节处理。

  1. 长图显示的处理,此时我们看到的长图的显示是这样的

    图片 5未处理长图显示

    图片被压缩填充在ImageView中,此时在判断如果是长图的方法中修改imageView的contentMode即可

// 设置imageView的内容以顶端对齐显示,多余的会被裁剪掉self.imageView.contentMode = UIViewContentModeTop;

同样,防止cell重用发生错乱如果不是长图需要设置

self.imageView.contentMode = UIViewContentModeScaleToFill;
  1. 图片显示进度条,进度条使用的DACircularProgress第三方。方法非常简单,这里不在赘述。可以使用sd的方法监听下载进度。
// 设置图片并显示进度[self.imageView sd_setImageWithURL:[NSURL URLWithString:topic.large_image]placeholderImage:nil options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) { // receivedSize :已经下载的进度 // expectedSize :完整大小 CGFloat progress =1.0 * receivedSize / expectedSize; self.progressView.progress = progress; self.progressView.progressLabel.text = [NSString stringWithFormat:@"%.0f%%",progress * 100]; self.progressView.hidden = NO;} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { self.progressView.hidden = YES;}];
  1. 视频和音频view播放次数,播放次数的显示非常简单,并且视频和音频一样,只不过修改一下控件即可
-setTopic:(CLTopic *)topic{ _topic = topic; [self.imageView sd_setImageWithURL:[NSURL URLWithString:topic.large_image]]; NSInteger minute = topic.videotime / 60; NSInteger second = topic.videotime % 60; self.videoTimeLabel.text = [NSString stringWithFormat:@"zd:zd",minute , second]; self.playCountLabel.text = [NSString stringWithFormat:@"%zd次播放",topic.playcount];}
  1. 监控网络状态对显示图片进行简单优化前面提到过服务器返回给我们的图片数据有三种小图,中图,大图,我们可以使用AFN对用户当前网络进行判断,如果当前用户使用的是蜂窝网络,则加载小图,为用户节省流量,同时也加快cell中图片显示的速度。如果用户在wifi环境下,则加载原图,提高图片清晰度。如果用户当前没有网络,则提醒用户没有网络。
//使用AFN进行网络判断AFNetworkReachabilityStatus status = [AFNetworkReachabilityManager sharedManager].networkReachabilityStatus;if (status == AFNetworkReachabilityStatusReachableViaWWAN) { // 手机自带网络 [self.imageView sd_setImageWithURL:[NSURL URLWithString:topic.small_image]];} else if (status == AFNetworkReachabilityStatusReachableViaWiFi) { // WIFI [self.imageView sd_setImageWithURL:[NSURL URLWithString:topic.large_image]];} else { // 网络有问题, 清空当前显示的图片 self.imageView.image = nil;}

注意:模拟器无法区分网络状态,需要真机测试。

精华模块的重构

全部界面完成之后,我们发现之后的视频,音频,图片,段子的页面显示非常简单,直接将全部界面的代码复制过去,修改数据请求的参数即可,1为全部,41为视频,31为音频,10为图片,29为段子。但是这样一来,造成了大量的重复代码,精华控制器的5个子控制器内代码基本相同,此时可以使用继承来重构代码。创建基类CLTopicViewController继承自UITableViewController,其他五个子类继承CLTopicViewController,同样将代码复制过来。重构的方法很多种,我们通过比较选择最好的一种。

  1. 通过判断控制器的类型,根据不同控制器类型不同,确定不同的请求参数
if ([NSStringFromClass(self.class) isEqualToString:@"CLAllViewController"]) { params[@"type"] = @"1";} else if ([NSStringFromClass(self.class) isEqualToString:@"CLVideoViewController"]) { params[@"type"] = @"41";} else if ([NSStringFromClass(self.class) isEqualToString:@"CLVoiceViewController"]) { params[@"type"] = @"31";} else if ([NSStringFromClass(self.class) isEqualToString:@"CLPictureViewController"]) { params[@"type"] = @"10";} else if ([NSStringFromClass(self.class) isEqualToString:@"CLWordViewController"]) { params[@"type"] = @"29";}

缺点:需要些大量繁琐代码,下拉刷新和上拉加载都需要重新判断一遍,并且这里由父控制器来设置子控制器的type,违背了谁的内容由其自己管理的代码原则。

  1. 给基类添加一个type属性
/** 帖子的类型 */// @property (nonatomic, assign) CLTopicType type;

然后我们在给主控制器添加子控制器的时候就可以设置子控制器的 type属性,举一个例子,其他子控制器相同。

CLAllViewController *all = [[CLAllViewController alloc]init];all.type = CLTopicTypeAll;[self addChildViewController:all]; 

其实此时我们直接使用基类即可,主控制器中添加5个基类控制器,每个基类控制器的type属性不同,但是这样做很有局限性,如果之后有需求需要往子控制器中添加单独的控件,或者个性化设置,还是需要在基类中进行判断,延展性非常不好。

  1. 通过重写基类type属性的get方法基类中提供type的get方法,我们可以在子类中重写基类的get方法,返回type,get方法只能子类可以重写,其他类也没有办法改变子类的type。保证了父类中的某个内容, 只允许由子类来修改或提供, 不能由外界来修改或提供,并且我们可以在子类中对子类单独的界面做一些个性化的设置,延展性非常好。
 - (CLTopicType)type{ return CLTopicTypeAll;}

其实也可以为属性添加readonly,这个属性会生成一个type的get方法和 _type成员变量,相较于上面的方法,多创建了没有必要的成员变量。并且需要考虑代码顺序问题,如果在父类中对type属性有一些调用,则会出现问题,因为type在super方法之后设置。

至此我们通过继承并重写type的get方法对精华模块进行了重构。子控制器内的代码变得非常简单,只需要重写覆盖父类的get方法即可,并且可以在子类中对子类进行一些个性化的设置。

查看图片

对于图片cell,点击图片会Mode出一个控制器来显示图片,同样使用xib来描述图片显示控制器,创建CLSeeBigViewController控制器,通过xib描述控制器view

图片 6CLSeeBigViewController的view

在图片的view,CLTopicPictureView中为中间显示图片的iamgeView添加点击事件,imageView默认不支持交互,需要开启交互。

self.imageView.userInteractionEnabled = YES;[self.imageView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector]];- seeBig{ CLSeeBigViewController *seeBig = [[CLSeeBigViewController alloc] init]; seeBig.topic = self.topic; [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:seeBig animated:YES completion:nil];}

接下来需要在CLSeeBigViewController中进行一些判断,首先有可能是长图,长图的长度肯定超过一个屏幕大小,所以CLSeeBigViewController中需要使用scrollView来显示长图,因为xib中已经在CLSeeBigViewController的view上添加了返回和保存按钮,所以scrollView需要使用insertSubview:atIndex添加在最底层,防止后加入的scrollView覆盖挡住返回和保存按钮。并在scrollView中添加imageView。对图片的长度进行计算,如果长度没有超过一个屏幕大小,则根据屏幕的宽高比计算出图片的高度,居中显示在屏幕中,保证imageView占据整个屏幕的宽度。如果长度超过一个屏幕大小,则设置imageView的y值为0,scrollView的contentSize横向为0,纵向为图片的高度。最后通过scrollView的代理方法对imageView的缩放比例进行设置。

#import "CLSeeBigViewController.h"#import "CLTopic.h"#import <UIImageView WebCache.h>@interface CLSeeBigViewController ()<UIScrollViewDelegate>/** 图片控件 */@property (nonatomic, weak) UIImageView *imageView;@end@implementation CLSeeBigViewController- viewDidLoad { [super viewDidLoad]; // scrollView UIScrollView *scrollView = [[UIScrollView alloc] init]; scrollView.delegate = self; scrollView.frame = [UIScreen mainScreen].bounds; [self.view insertSubview:scrollView atIndex:0]; // imageView UIImageView *imageView = [[UIImageView alloc] init]; [imageView sd_setImageWithURL:[NSURL URLWithString:self.topic.large_image]]; [scrollView addSubview:imageView]; imageView.cl_width = scrollView.cl_width; imageView.cl_height = self.topic.height * imageView.cl_width / self.topic.width; imageView.cl_x = 0; if (imageView.cl_height >= scrollView.cl_height) { // 图片高度超过整个屏幕 imageView.cl_y = 0; // 滚动范围 scrollView.contentSize = CGSizeMake(0, imageView.cl_height); } else { // 居中显示 imageView.cl_centerY = scrollView.cl_height * 0.5; } self.imageView = imageView; // 缩放比例 CGFloat scale = self.topic.width / imageView.cl_width; if (scale > 1.0) { scrollView.maximumZoomScale = scale; }}- back { [self dismissViewControllerAnimated:YES completion:nil];}- save {}#pragma mark - <UIScrollViewDelegate>//返回一个scrollView的子控件进行缩放- viewForZoomingInScrollView:(UIScrollView *)scrollView{ return self.imageView;}@end

保存图片到相册

保存图片到相册需要用到的框架

#import <AssetsLibrary/AssetsLibrary.h> // iOS9开始废弃#import <Photos/Photos.h> // iOS9开始推荐

首先来看一下系统相簿的内容

图片 7系统相簿

如果仅仅是将图片保存到系统中相机胶卷相簿中,<AssetsLibrary/AssetsLibrary.h>提供了非常简单的函数。

UIImageWriteToSavedPhotosAlbum(self.imageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);

这个函数中的SEL方法必须按照一定格式传三个参数才可以,方法内部已经给出说明

图片 8UIImageWriteToSavedPhotosAlbum

  • API

访问系统相册需要获得用户授权,且只会请求一次,如果用户点击了不允许,则永远不允许访问相册,此时需要提醒用户去[设置]-[隐私]-[照片]中开启。

图片 9取得用户授权

我们这里想要实现将图片保存到项目自己创建的相簿中,其实将图片保存到项目自己创建的相簿中,也需要先将图片保存到相机胶卷相簿中,然后在转移到自己创建的相簿中。

将图片保存到自己创建相簿的步骤

1.判断用户授权情况

// 获取用户授权状态PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];// 授权状态PHAuthorizationStatusRestricted, 因为家长控制, 导致应用无法方法相册(跟用户的选择没有关系)很少出现。如果这种状态提醒用户 系统原因无法访问相册 PHAuthorizationStatusDenied, 用户拒绝当前应用访问相册(用户当初点击了"不允许") 如果用户拒绝,提醒用户去[设置]-[隐私]-[照片]中开启。PHAuthorizationStatusAuthorized,用户允许当前应用访问相册(用户当初点击了"好") 如果获得用户授权,则开始保存图片PHAuthorizationStatusNotDetermined, 用户还没有做出选择如果用户还没有做出选择,则对用户授权信息进行请求,如果用户点击了不允许则什么都不做,点击了好则开始保存图片

2.将图片存储在交卷相册中3.判断是否已经创建自己相簿4.如果已经创建了则获得曾经创建过的相簿,获得图片,获取添加图片到相簿中的请求,将图片添加到相簿5.如果没有创建相簿,创建相簿的请求,获得创建相簿,获得图片,获取图片添加到相簿的请求,将图片添加到相簿中

直接来看保存图片到相册的save按钮点击事件吧,<Photos/Photos.h>框架的设计虽然使用起来繁琐,但是非常巧妙,如果想对"相册"进行修改, 那么修改代码必须放在[PHPhotoLibrary sharedPhotoLibrary]performChanges方法的block中,并且将图片添加到相簿中、创建相簿都是耗时操作,他们都在子线程中执行。所以如果做添加过程中想要修改UI,例如提醒用户保存成功或失败等,需要会到主线程中执行。

- save { /* PHAuthorizationStatusNotDetermined, 用户还没有做出选择 PHAuthorizationStatusDenied, 用户拒绝当前应用访问相册(用户当初点击了"不允许") PHAuthorizationStatusAuthorized 用户允许当前应用访问相册(用户当初点击了"好") PHAuthorizationStatusRestricted, 因为家长控制, 导致应用无法方法相册(跟用户的选择没有关系) */ PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus]; if (status == PHAuthorizationStatusRestricted) { // 因为家长控制,导致应用无法访问相册 [SVProgressHUD showErrorWithStatus:@"因为系统原因,无法访问系统相册"]; }else if (status == PHAuthorizationStatusDenied){ // 用户点击了不允许 CLLog(@"设置-隐私-照片-百思不得姐xx_cc-允许"); }else if (status == PHAuthorizationStatusAuthorized){ // 获得用户授权,在这里保存图片 [self saveImage]; }else if (status == PHAuthorizationStatusNotDetermined){ // 用户还没有选择进行授权 [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { // 用户点击好或者不允许 都会到这里,如果不允许则什么都不做,如果好,则保存图片 if (status == PHAuthorizationStatusAuthorized) { // 保存图片 [self saveImage]; } }]; } }/** 保存图片到相册 */- saveImage{ // PHAsset : 一个资源, 比如一张图片一段视频 // PHAssetCollection : 一个相簿 // PHAsset的标识, 利用这个标识可以找到对应的PHAsset对象 __block NSString *assetLocalIdentifier = nil; // 如果想对"相册"进行修改, 那么修改代码必须放在[PHPhotoLibrary sharedPhotoLibrary]的performChanges方法的block中 [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ // 1.保存图片A到"相机胶卷"中 // 创建图片的请求 assetLocalIdentifier = [PHAssetCreationRequest creationRequestForAssetFromImage:self.imageView.image].placeholderForCreatedAsset.localIdentifier; } completionHandler:^(BOOL success, NSError * _Nullable error) { // 这个方法在子线程中执行,所以需要返回到主线程中去修改UI if (success == NO) { [self showError:@"保存图片失败!"]; return; } // 2.获得相簿 PHAssetCollection *createdAssetCollection = [self createdAssetCollection]; if (createdAssetCollection == nil) { // 这个方法在子线程中执行,所以需要返回到主线程中去修改UI [self showError:@"创建相簿失败!"]; return; } [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ // 3.添加"相机胶卷"中的图片A到"相簿"D中 // 获得图片 PHAsset *asset = [PHAsset fetchAssetsWithLocalIdentifiers:@[assetLocalIdentifier] options:nil].lastObject; // 添加图片到相簿中的请求 PHAssetCollectionChangeRequest *request = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:createdAssetCollection]; // 添加图片到相簿 [request addAssets:@[asset]]; } completionHandler:^(BOOL success, NSError * _Nullable error) { // 这个方法在子线程中执行,所以需要返回到主线程中去修改UI if (success == NO) { [self showError:@"保存图片失败!"];; } else { [self showSuccess:@"保存图片成功!"];; } }]; }];}/** * 获得相簿 * 如果已经找到应用对应的相簿则直接添加到相簿,如果没有找到则创建新的相簿 */- (PHAssetCollection *)createdAssetCollection{ // 相簿名字 CLAssetCollectionTitle static NSString * CLAssetCollectionTitle = @"百思不得姐xx_cc"; // 从已存在相簿中查找这个应用对应的相簿 PHFetchResult<PHAssetCollection *> *assetCollections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil]; for (PHAssetCollection *assetCollection in assetCollections) { if ([assetCollection.localizedTitle isEqualToString:CLAssetCollectionTitle]) { return assetCollection; } } // 没有找到对应的相簿, 得创建新的相簿 // 错误信息 NSError *error = nil; // PHAssetCollection的标识, 利用这个标识可以找到对应的PHAssetCollection对象 __block NSString *assetCollectionLocalIdentifier = nil; // 这个方法在主线程张中执行,等相簿创建完毕之后才会返回 [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{ // 创建相簿的请求 CLAssetCollectionTitle 表示相簿名字 assetCollectionLocalIdentifier = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:CLAssetCollectionTitle].placeholderForCreatedAssetCollection.localIdentifier; } error:&error]; // 如果有错误信息 if  return nil; // 获得刚才创建的相簿 return [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[assetCollectionLocalIdentifier] options:nil].lastObject;}// 注意:因为添加图片到相簿,和创建相簿都在子线程中执行,所以修改UI需要回到主线程,我们这里进行了封装。- showSuccess:(NSString *)text{ dispatch_async(dispatch_get_main_queue(), ^{ [SVProgressHUD showSuccessWithStatus:text]; });}- showError:(NSString *)text{ dispatch_async(dispatch_get_main_queue(), ^{ [SVProgressHUD showErrorWithStatus:text]; });}

虽然将图片保存到自己创建的项目相簿中比较繁琐,但是写一次基本上就可以循环利用了,上面的代码稍作修改完全可以使用到别的项目中。

总结

今天主要完成了cell内部的一些细节操作,计算cell的高度,显示cell内容,查看图片等等,同时对精华模块进行重构,使得精华模块的结构更加清晰。看一下第五天成果

图片 10第五天成果

文中如果有不对的地方欢迎指出。我是xx_cc,一只长大很久但还没有二够的家伙。

本文由澳门新葡萄京发布于计算机知识,转载请注明出处:因为cell的重用机制

关键词: 六天 第五天 简单