前面我已经写了4篇关于AutoLayout进阶的文章,但是讲的点都很散。我准备用这篇文章将前面所讲的知识串起来,运用到实际,然后将AutoLayout系列做一个总结。我们先来看看效果:
这是一个类似朋友圈的社交界面,也是我以前做过的项目的一部分,全部由基本的UITableView实现。为了避免暴露服务器接口,我去除了与服务器交互的部分以及一大部分业务逻辑,只剩下写死的数据和界面布局。当时为了完成项目任务,做这个界面大概花了5天的时间(demo外还有很多功能),为了实现功能和优化卡顿也费了不少心思,也帮助我对约束布局有了更深认识。话不多说,我们来看看怎么实现。
大图小图布局切换
看gif图我们可以看见,图片的布局有多样,准确的说最多显示9种,根据图片数量不同,布局也不同。网上有些人采用几个不同的cell来显示不同种类的图片,但我这为了节省代码量,我采用AutoLayout布局,并通过修改约束来实现不同个数的图片显示。
这个是我上半部分cell的布局。中间的大方框就是我放图片的位置,我在这单独摆放了一个UIView,方便后续的界面布局。这个方框的宽度不会改变,唯一会变的应该是高度(虚线显示的约束),因此我将这个高度约束设置成变量,在cell的.m
文件中,通过改变变量的值来动态调整UiView的高度,再把imageView填上去。
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bigPictureViewHeight;
- (void)createPicTureView:(NSArray *)picArray{
[self.pictureView removeAllSubviews];
[pictureArray removeAllObjects];
picWidth = 0;
picHeight = 0;
int rowPicCount = 1;
float bigPicViewWidth = screenWidth - space *2;
if ([picArray count]==0) {
self.bigPictureViewHeight.constant = 0;
return ;
}
else if ([picArray count] == 1) {
picWidth = (screenWidth - space *2) ;
picHeight = picWidth;
self.bigPictureViewHeight.constant = bigPicViewWidth;
rowPicCount =1;
}
else if ([picArray count] <=2){
picWidth = (screenWidth - space *2 - 5)/2;
picHeight = picWidth;
self.bigPictureViewHeight.constant = picHeight;
rowPicCount =2;
}
else if ([picArray count] <=4){
picWidth = (screenWidth - space *2 - 5)/2;
picHeight = picWidth;
self.bigPictureViewHeight.constant = bigPicViewWidth;
rowPicCount = 2;
}
else if ([picArray count] <=6){
picWidth = (screenWidth - space *2 - 10)/3;
picHeight = picWidth;
self.bigPictureViewHeight.constant = picHeight *2 +5;
rowPicCount = 3;
}
else if ([picArray count] <= 9){
picWidth = (screenWidth - space *2 - 10)/3;
picHeight = picWidth;
self.bigPictureViewHeight.constant = bigPicViewWidth;
rowPicCount = 3;
}
float x=0;
float y =0;
for (int i =0 ; i<[picArray count]; i++) {
UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(x, y , picWidth, picHeight)];
imageView.backgroundColor = [UIColor whiteColor];
imageView.clipsToBounds = YES;
imageView.contentMode =UIViewContentModeScaleAspectFill;
[imageView sd_getImageWithId:picArray[i] andSize:picWidth square:NO placeholderImage:[UIImage imageNamed:@"place_holder_album"]];
[self.pictureView addSubview:imageView];
[pictureArray addObject:imageView];
//白色圆角边框
UIImageView *cornerImageView = [[UIImageView alloc] initWithFrame:CGRectMake(x, y , picWidth, picHeight)];
cornerImageView.image = [UIImage stretchableImageNamed:@"white_corner"];
[self.pictureView addSubview:cornerImageView];
UIButton * button = [[UIButton alloc]initWithFrame:imageView.frame];
button.tag = i;
//点击图片展示
[button addTarget:self action:@selector(showPic:) forControlEvents:UIControlEventTouchUpInside];
[self.pictureView addSubview:button];
if ((i+1)%rowPicCount == 0) {
x= 0;
y = y + 5 + picHeight;
}
else {
x= x+picWidth +5;
}
}
}
这里其实有一点不足之处,如果每次显示cell都将控件remove再add一遍,其实是很耗性能的,如果有什么好的建议欢迎再评论区分享。
文字点击事件TTTAttributedLabel
文字点击事件获取一直都是这类界面的难点。我们都知道NSMutableAttributedString
可以改变字体颜色,大小等,但是不支持响应事件。如果专门为了这个功能去封装UIView
的touch
事件又太过麻烦。因此,我选择上网查找第三方控件,很幸运,TTTAttributedLabel
刚好能满足我的需求。
- 导入方法采用pod
pod 'TTTAttributedLabel', '~> 1.13.4'
- github地址:https://github.com/TTTAttributedLabel/TTTAttributedLabel
TTTAttributedLabel的使用很简单,首先设置addLinkToAddress:withRange:
方法,将响应的参数和范围传进去,然后设置delegate
。在代理的attributedLabel:didSelectLinkWithAddress:
会返回回调,在里面处理点击事件就可以了。TTTAttributedLabel还能像NSMutableAttributedString
那样,通过setText:afterInheritingLabelAttributesAndConfiguringWithBlock:
方法设置文字颜色,点击颜色等。关键代码如下:
TTTAttributedLabel * textLabel = [[TTTAttributedLabel alloc] initWithFrame:CGRectMake(0, 0, 20, 0)];
textLabel.extendsLinkTouchArea = NO;//网上说设置这个可以减少点击面积,从而使滑动更流畅
textLabel.font = FontRealityNormal;
NSString * userName1 = dataModel.username;
NSString * userName2 = dataModel.targetusername;
NSString * text = dataModel.text;
NSRange firstRange = NSMakeRange(0, userName1.length);
NSRange secondRange;
NSRange thirdRange;
NSString * result = [NSString stringWithFormat:@"%@",userName1];
if (userName2.length > 0) {
result = [NSString stringWithFormat:@"%@ 回复 %@",result,userName2];
secondRange = NSMakeRange(userName1.length+4, userName2.length);
}
else {
secondRange = NSMakeRange(userName1.length+4, 0);
}
result = [NSString stringWithFormat:@"%@ :%@",result,text];
thirdRange = NSMakeRange(0, result.length);
textLabel.lineSpacing = 5;
textLabel.preferredMaxLayoutWidth = screenWidth - 68 -30 -6-6-10;
if (self.type == TalkTypeNomal) {
textLabel.numberOfLines = 5;
}
else {
textLabel.numberOfLines = 0;
}
textLabel.linkAttributes = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:(NSString *)kCTUnderlineStyleAttributeName];
[textLabel setText:result afterInheritingLabelAttributesAndConfiguringWithBlock:^NSMutableAttributedString *(NSMutableAttributedString *mutableAttributedString) {
UIFont *boldSystemFont = [UIFont systemFontOfSize:13];
CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)boldSystemFont.fontName, boldSystemFont.pointSize, NULL);
if (font) {
//设置可点击文本的大小
[mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)font range:firstRange];
[mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)font range:secondRange];
//设置可点击文本的颜色
[mutableAttributedString addAttribute:(NSString*)kCTForegroundColorAttributeName value:(id)[[UIColor colorWithRed:62/255.0 green:81/255.0 blue:105/255.0 alpha:1] CGColor] range:firstRange];
[mutableAttributedString addAttribute:(NSString*)kCTForegroundColorAttributeName value:(id)[[UIColor colorWithRed:62/255.0 green:81/255.0 blue:105/255.0 alpha:1] CGColor] range:secondRange];
CFRelease(font);
}
return mutableAttributedString;
}];
textLabel.delegate = self;
[textLabel addLinkToAddress:@{@"kind":@"comment",@"object":dataModel} withRange:thirdRange];
[textLabel addLinkToAddress:@{@"kind":@"user",@"object":dataModel.userid} withRange:firstRange];
[textLabel addLinkToAddress:@{@"kind":@"user",@"object":dataModel.targetuserid} withRange:secondRange];
[self.commentView addSubview:textLabel];
///添加长按事件
UILongPressGestureRecognizer * longPressGestureRecognizer =[[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressGesture:)];
[textLabel addGestureRecognizer:longPressGestureRecognizer];
[textLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.commentView).offset(3);
make.left.equalTo(avatar.mas_right).offset(5);
make.right.equalTo(self.commentView).offset(-10);
make.bottom.equalTo(self.commentView).offset(-3);
}];
将评论添加到view上并布局我采用Masonry
设置约束,代码如上最后一段。参考:Autolayout进阶之代码编写约束(一)
自动计算cell高度
朋友圈类型的界面,自动计算高度是少不了的。这里我采用的是自己的一套框架,用法比较简单,而且我以前也介绍过,这里就不再累述。参考:AutoLayout进阶之可变cell高度
主要代码:
- (CGFloat)getHeightWidthInfo:(id)info{
[self setInfo:info];
[self layoutSubviews];
[self setNeedsUpdateConstraints];
[self updateConstraintsIfNeeded];
[self setNeedsLayout];
[self layoutIfNeeded];
CGFloat height = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height ;
return height;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
if((self.type == TalkTypeNomal || self.type == TalkTopic) && indexPath.row == 6){
return 50;
}
//先从缓存中查看数据,如果有数据就直接返回,如果没有再进行计算
float height = [tableViewHeightCache getHeightWithNSIndexPath:indexPath].floatValue;
if (height != 0) {
return height;
}
if (indexPath.row == 0) {
float height;
MCTalkMainViewCell * cell = [tableView MCTalkMainViewCell];
height = [cell getHeightWidthInfo:dataArray[indexPath.section]] ;
[tableViewHeightCache setHeightWithNSIndexPatch:indexPath andValue:@(height)];
return height;
}
else {
float height;
MCTalkCommentCell * cell = [tableView MCTalkCommentCell];
cell.type = self.type;
PZHListItemModel * listModel = dataArray[indexPath.section];
TalkCommentModel * commentModel = listModel.commentPoList[indexPath.row -1];
height = [(MCTalkCommentCell *)cell getHeightWidthInfo:@{@"listModel":listModel,@"commentModel":commentModel}];
[tableViewHeightCache setHeightWithNSIndexPatch:indexPath andValue:@(height)];
return height;
}
return 0.1;
}
总结
类似朋友圈最大的问题就是滑动卡顿。如果仔细观察,微信的朋友圈也是有略微卡顿的。因此要尽可能优化代码。我曾经将评论都放在一个cell,结果评论一多就根本划不动,原因是每条评论都设置了多个autoLayout约束,在一个cell中的话,计算高度十分复杂、缓慢。后来我将评论分成多个cell,这样就减少了计算量。一定要记得要缓存高度,这样能极大的优化加载速度。还有富文本label的使用尽量少,因为富文本本身就很消耗性能。
Show Me The Code!
github: https://github.com/NBaby/PZHCircleOfFriendsDemo
我是翻滚的牛宝宝,欢迎大家评论交流~