我的iOS开发小屋iOS开发iOS开发技术分享

AutoLayout进阶之朋友圈界面实现

2016-07-18  本文已影响939人  o翻滚的牛宝宝o

前面我已经写了4篇关于AutoLayout进阶的文章,但是讲的点都很散。我准备用这篇文章将前面所讲的知识串起来,运用到实际,然后将AutoLayout系列做一个总结。我们先来看看效果:

2016-07-18 10_43_37.gif

这是一个类似朋友圈的社交界面,也是我以前做过的项目的一部分,全部由基本的UITableView实现。为了避免暴露服务器接口,我去除了与服务器交互的部分以及一大部分业务逻辑,只剩下写死的数据和界面布局。当时为了完成项目任务,做这个界面大概花了5天的时间(demo外还有很多功能),为了实现功能和优化卡顿也费了不少心思,也帮助我对约束布局有了更深认识。话不多说,我们来看看怎么实现。

大图小图布局切换

看gif图我们可以看见,图片的布局有多样,准确的说最多显示9种,根据图片数量不同,布局也不同。网上有些人采用几个不同的cell来显示不同种类的图片,但我这为了节省代码量,我采用AutoLayout布局,并通过修改约束来实现不同个数的图片显示。

上半部分cell布局

这个是我上半部分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可以改变字体颜色,大小等,但是不支持响应事件。如果专门为了这个功能去封装UIViewtouch事件又太过麻烦。因此,我选择上网查找第三方控件,很幸运,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

我是翻滚的牛宝宝,欢迎大家评论交流~

上一篇 下一篇

猜你喜欢

热点阅读