前面我已經(jīng)寫(xiě)了4篇關(guān)于AutoLayout進(jìn)階的文章,但是講的點(diǎn)都很散。我準(zhǔn)備用這篇文章將前面所講的知識(shí)串起來(lái),運(yùn)用到實(shí)際,然后將AutoLayout系列做一個(gè)總結(jié)。我們先來(lái)看看效果:

這是一個(gè)類似朋友圈的社交界面,也是我以前做過(guò)的項(xiàng)目的一部分,全部由基本的UITableView實(shí)現(xiàn)。為了避免暴露服務(wù)器接口,我去除了與服務(wù)器交互的部分以及一大部分業(yè)務(wù)邏輯,只剩下寫(xiě)死的數(shù)據(jù)和界面布局。當(dāng)時(shí)為了完成項(xiàng)目任務(wù),做這個(gè)界面大概花了5天的時(shí)間(demo外還有很多功能),為了實(shí)現(xiàn)功能和優(yōu)化卡頓也費(fèi)了不少心思,也幫助我對(duì)約束布局有了更深認(rèn)識(shí)。話不多說(shuō),我們來(lái)看看怎么實(shí)現(xiàn)。
大圖小圖布局切換
看gif圖我們可以看見(jiàn),圖片的布局有多樣,準(zhǔn)確的說(shuō)最多顯示9種,根據(jù)圖片數(shù)量不同,布局也不同。網(wǎng)上有些人采用幾個(gè)不同的cell來(lái)顯示不同種類的圖片,但我這為了節(jié)省代碼量,我采用AutoLayout布局,并通過(guò)修改約束來(lái)實(shí)現(xiàn)不同個(gè)數(shù)的圖片顯示。

這個(gè)是我上半部分cell的布局。中間的大方框就是我放圖片的位置,我在這單獨(dú)擺放了一個(gè)UIView,方便后續(xù)的界面布局。這個(gè)方框的寬度不會(huì)改變,唯一會(huì)變的應(yīng)該是高度(虛線顯示的約束),因此我將這個(gè)高度約束設(shè)置成變量,在cell的.m文件中,通過(guò)改變變量的值來(lái)動(dòng)態(tài)調(diào)整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;
//點(diǎn)擊圖片展示
[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;
}
}
}
這里其實(shí)有一點(diǎn)不足之處,如果每次顯示cell都將控件remove再add一遍,其實(shí)是很耗性能的,如果有什么好的建議歡迎再評(píng)論區(qū)分享。
文字點(diǎn)擊事件TTTAttributedLabel
文字點(diǎn)擊事件獲取一直都是這類界面的難點(diǎn)。我們都知道NSMutableAttributedString可以改變字體顏色,大小等,但是不支持響應(yīng)事件。如果專門為了這個(gè)功能去封裝UIView的touch事件又太過(guò)麻煩。因此,我選擇上網(wǎng)查找第三方控件,很幸運(yùn),TTTAttributedLabel剛好能滿足我的需求。
- 導(dǎo)入方法采用pod
pod 'TTTAttributedLabel', '~> 1.13.4' - github地址:https://github.com/TTTAttributedLabel/TTTAttributedLabel
TTTAttributedLabel的使用很簡(jiǎn)單,首先設(shè)置addLinkToAddress:withRange:方法,將響應(yīng)的參數(shù)和范圍傳進(jìn)去,然后設(shè)置delegate。在代理的attributedLabel:didSelectLinkWithAddress:會(huì)返回回調(diào),在里面處理點(diǎn)擊事件就可以了。TTTAttributedLabel還能像NSMutableAttributedString那樣,通過(guò)setText:afterInheritingLabelAttributesAndConfiguringWithBlock:方法設(shè)置文字顏色,點(diǎn)擊顏色等。關(guān)鍵代碼如下:
TTTAttributedLabel * textLabel = [[TTTAttributedLabel alloc] initWithFrame:CGRectMake(0, 0, 20, 0)];
textLabel.extendsLinkTouchArea = NO;//網(wǎng)上說(shuō)設(shè)置這個(gè)可以減少點(diǎn)擊面積,從而使滑動(dòng)更流暢
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:@"%@ 回復(fù) %@",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) {
//設(shè)置可點(diǎn)擊文本的大小
[mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)font range:firstRange];
[mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)font range:secondRange];
//設(shè)置可點(diǎn)擊文本的顏色
[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];
///添加長(zhǎng)按事件
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);
}];
將評(píng)論添加到view上并布局我采用Masonry設(shè)置約束,代碼如上最后一段。參考:Autolayout進(jìn)階之代碼編寫(xiě)約束(一)
自動(dòng)計(jì)算cell高度
朋友圈類型的界面,自動(dòng)計(jì)算高度是少不了的。這里我采用的是自己的一套框架,用法比較簡(jiǎn)單,而且我以前也介紹過(guò),這里就不再累述。參考:AutoLayout進(jìn)階之可變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;
}
//先從緩存中查看數(shù)據(jù),如果有數(shù)據(jù)就直接返回,如果沒(méi)有再進(jìn)行計(jì)算
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;
}
總結(jié)
類似朋友圈最大的問(wèn)題就是滑動(dòng)卡頓。如果仔細(xì)觀察,微信的朋友圈也是有略微卡頓的。因此要盡可能優(yōu)化代碼。我曾經(jīng)將評(píng)論都放在一個(gè)cell,結(jié)果評(píng)論一多就根本劃不動(dòng),原因是每條評(píng)論都設(shè)置了多個(gè)autoLayout約束,在一個(gè)cell中的話,計(jì)算高度十分復(fù)雜、緩慢。后來(lái)我將評(píng)論分成多個(gè)cell,這樣就減少了計(jì)算量。一定要記得要緩存高度,這樣能極大的優(yōu)化加載速度。還有富文本label的使用盡量少,因?yàn)楦晃谋颈旧砭秃芟男阅堋?/p>
Show Me The Code!
github: https://github.com/NBaby/PZHCircleOfFriendsDemo
我是翻滾的牛寶寶,歡迎大家評(píng)論交流~