参考地址:https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/
前面一篇说了异步绘制文字,异步渲染图片,这篇主要是预排版,经过这三种处理之后,基本上已经非常流畅了.
下面的demo就使用了这三种处理来做优化.
我使用的demo是我之前做的一个列表:https://www.cnblogs.com/alan12138/p/9619336.html
下面是优化前的demo地址和效果:
github:https://github.com/alan12138/InfoFlow
可以看到滑动很快的时候到最后已经达到33FPS的程度了,当然如果正常浏览不那么快的时候还是没有很大差别的.
下面是优化后的demo地址和效果:
github:https://github.com/alan12138/Interview-question/tree/master/3/InfoFlow
可以看到无论如何滑动,基本稳定在60FPS.
因为图片异步渲染和异步绘制文字我上一篇博文已经写过了所以这篇主要写一下预排版.
预排版主要做的就是这件事:
1 dispatch_async(dispatch_get_global_queue(0, 0), ^{ 2 //造一些数据 3 self.feeds = [NSMutableArray array]; 4 for (int i = 0; i < 130; i++) { 5 APMyFeed *myFeed = [[APMyFeed alloc] init]; 6 myFeed.headIcon = @"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2118739199,3378602431&fm=26&gp=0.jpg"; 7 myFeed.name = @"王者小能手"; 8 myFeed.sex = @"性别女"; 9 ... 10 } 11 12 myFeed.time = @"5分钟前"; 13 myFeed.location = @"南京·麒麟国际企业研发园"; 14 myFeed.comment = @"12"; 15 myFeed.zan = @"10"; 16 myFeed.expand = NO; 17 [self.feeds addObject:myFeed]; 18 } 19 20 self.layouts = [NSMutableArray array]; 21 for (APMyFeed *feed in self.feeds) { 22 APMyFeedLayout *layout = [[APMyFeedLayout alloc] init]; 23 layout.feed = feed; 24 [self.layouts addObject:layout]; 25 } 26 27 dispatch_async(dispatch_get_main_queue(), ^{ 28 [self.mainTableView reloadData]; 29 }); 30 });
获取数据之后,异步将所有控件排版通过已获取的数据提前计算出来,并保存在一个数组中,处理完之后再返回主线程刷新列表加载数据.
数组中的每个元素都是APMyFeedLayout类型,其中保存了每一条的数据和通过数据计算出来的排版数据.如此所有控件的排版和行高计算都已经在列表加载之前完成了,避免了滑动过程中的大部分计算,所以在滑动过程中只剩下了一件事,就是把计算好的数据赋值而已.
1 #pragma mark - UITableView 2 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 3 return self.layouts.count; 4 } 5 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 6 APMyFeedTableViewCell *cell = [APMyFeedTableViewCell cellWithTableView:tableView]; 7 8 APMyFeedLayout *layout = self.layouts[indexPath.row]; 9 cell.layout = layout; 10 return cell; 11 } 12 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 13 APMyFeedLayout *layout = self.layouts[indexPath.row]; 14 return layout.height; 15 }
排版的计算在APMyFeedLayout中进行,主要就是在setFeed的时候通过feed数据计算出每个控件的frame,并保存在每个APMyFeedLayout对象中,用于后面使用.
1 @implementation APMyFeedLayout 2 3 - (void)setFeed:(APMyFeed *)feed { 4 _feed = feed; 5 6 CGFloat screenW = [UIScreen mainScreen].bounds.size.width; 7 8 self.iconRect = CGRectMake(10, 10, 45, 45); 9 CGFloat nameW = [CommonUtils calcWidthWithTitle:feed.name font:nameFont]; 10 CGFloat nameH = [CommonUtils calcLabelHeight:feed.name fontSize:nameFont width:nameW]; 11 self.nameRect = CGRectMake(CGRectGetMaxX(self.iconRect) + 11, 17, nameW, nameH); 12 self.sexRect = CGRectMake(CGRectGetMaxX(self.iconRect) + 9, CGRectGetMaxY(self.nameRect) + 6, 12, 12); 13 14 15 ... 16 17 self.seperatorViewRect = CGRectMake(0, CGRectGetMaxY(self.delRect) + 10, screenW, 15); 18 19 self.height = CGRectGetMaxY(self.seperatorViewRect); 20 } 21 22 @end
最后就是在cell中赋值了:
1 - (void)setLayout:(APMyFeedLayout *)layout { 2 _layout = layout; 3 4 [self.headIconBtn sd_setImageWithURL:[NSURL URLWithString:layout.feed.headIcon] forState:UIControlStateNormal]; 5 self.headIconBtn.frame = layout.iconRect; 6 [self.headIconBtn addRadius:self.headIconBtn.bounds.size.width / 2 corners:UIRectCornerAllCorners bgColor:[UIColor whiteColor]]; 7 8 self.nameLabel.text = layout.feed.name; 9 self.nameLabel.frame = layout.nameRect; 10 11 [self.sexIconView setImage:[UIImage imageNamed:layout.feed.sex]]; 12 self.sexIconView.frame = layout.sexRect; 13 14 self.contentLabel.text = layout.feed.content; 15 self.contentLabel.frame = layout.contentRect; 16 17 self.timeLabel.text = layout.feed.time; 18 self.timeLabel.frame = layout.timeRect; 19 20 self.locationLabel.text = layout.feed.location; 21 self.locationLabel.frame = layout.locationRect; 22 23 self.delBtn.frame = layout.delRect; 24 25 self.zanLabel.text = layout.feed.zan; 26 self.zanLabel.frame = layout.zanLabelRect; 27 28 self.commentLabel.text = layout.feed.comment; 29 self.commentLabel.frame = layout.commentLabelRect; 30 31 ... 32 }