• 【iOS】实现评论区展开效果



    前言

    在知乎日报的评论区中,用到了Masonry行高自适应来实现评论的展开,这里设计许多控件的约束问题,当时困扰了笔者许久,特此撰写博客记录

    实现行高自适应

    步骤1:
    设置tableView.rowHeight = UITableViewAutomaticDimension

    步骤2:
    设置tableView.estimatedRowHeight = 100

    解释:设置一个预估的行高,为了代码的易读性,还是尽量要设置一个跟cell的高差不多的值。

    步骤3:
    到了此步,就涉及到了我们行高自适应的核心思想:根据内容长短将我们的控件撑开从而实现tableviewcell撑开

    方法便是对控件设置上下约束但是不设置其高度,这里需要注意我们的bottom的约束一定要与contentviewbottom有关,否则无法撑开我们的contentview

    我们以一段小demo为例来讲解

        [_name mas_makeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(@30);
                make.top.equalTo(@30);
                make.height.equalTo(@40);
                make.width.equalTo(@100);
        }];
        
    //    [_time mas_makeConstraints:^(MASConstraintMaker *make) {
    //        make.left.equalTo(@30);
    //            make.height.equalTo(@40);
    //            make.width.equalTo(@100);
    //        make.bottom.equalTo(self.contentView.mas_bottom).offset(-20);
    //    }];
        
        [_content mas_makeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(@30);
                make.top.equalTo(self->_name.mas_bottom).offset(10);
    //            make.bottom.equalTo(self->_reply.mas_top).offset(-20);
            make.bottom.equalTo(self.contentView.mas_bottom).offset(-20);
    
                make.right.equalTo(self.contentView.mas_right).offset(-20);
        }];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    当我们没有给_content的文本进行赋值时,视图层级是这样的
    在这里插入图片描述
    同时我们的_content控件甚至没有在层级图上出现,但是一旦我们对其进行赋值,就会自动撑开cell的高度

        cell.content.text = @"FMDB中有三个常用的类FMDatabase 表示一个SQLite数据库,用来执行SQL语句。";
    
    • 1

    在这里插入图片描述

    实现评论展开效果

    现在我们已经实现用Masonry实现行高自适应,接下来我们讲讲我们知乎日报评论区中评论的展开。
    思路:
    1.首先我们可以得到回复的一段文本,我们需要根据文本的长短来判断我们的评论是否需要展开,也就是说我们的展开效果是否需要实现是与我们评论的长短有关的。

    -(CGSize)textHeightFromTextString:(NSString *)text width:(CGFloat)textWidth fontSize:(CGFloat)size {
        // 计算 label 需要的宽度和高度
        NSDictionary *dict = @{NSFontAttributeName:[UIFont systemFontOfSize:size]};
        CGRect rect = [text boundingRectWithSize:CGSizeMake(textWidth, MAXFLOAT) options:NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesFontLeading|NSStringDrawingUsesLineFragmentOrigin attributes:dict context:nil];
        
         CGSize size1 = [text sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:size]}];
        
        return CGSizeMake(size1.width, rect.size.height);
    }
    
     NSInteger count = [self textHeightFromTextString:reply width:303.667 fontSize:15.5].height /
                cell.replyLabel.font.lineHeight;
                if (count <= 2) {
                    cell.foldButton.hidden = YES;
                } else {
                    cell.foldButton.hidden = NO;
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这里需要注意,我们的回复label也需要实现行高自适应的效果,与content的代码结合起来便是

        [_name mas_makeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(@30);
                make.top.equalTo(@30);
                make.height.equalTo(@40);
                make.width.equalTo(@100);
        }];
        
        [_time mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(@30);
                make.height.equalTo(@40);
                make.width.equalTo(@100);
            make.bottom.equalTo(self.contentView.mas_bottom).offset(-20);
        }];
        
        [_content mas_makeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(@30);
                make.top.equalTo(self->_name.mas_bottom).offset(10);
                make.bottom.equalTo(self->_reply.mas_top).offset(-20);
                make.right.equalTo(self.contentView.mas_right).offset(-20);
        }];
        
        [_reply mas_makeConstraints:^(MASConstraintMaker *make) {
                make.top.equalTo(self->_content.mas_bottom).offset(20);
                make.bottom.equalTo(self.time.mas_top).offset(-10);
            make.right.equalTo(self.contentView.mas_right).offset(-20);
            make.left.equalTo(@30);
        }];
        
        [_foldButton mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(@140);
            make.height.equalTo(@40);
            make.bottom.equalTo(self.contentView.mas_bottom).offset(-20);
            make.width.equalTo(@100);
        }];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    这样我们的contentreply就都与我们的contentView的底部又了约束关系,从而实现了行高自适应
    在这里插入图片描述

    解决cell中的buttom的复用问题

    我们可以通过上面的动画看到,我们的评论区确实实现了评论展开,但是出现了buttom的复用问题,这里笔者将一下解决方案

    • 首先为每一个buttom设置一个tag值,并且我们需要通过我们的buttom得到我们的cell

    笔者使用的方法是- (nullable __kindof UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath;

    通过buttom的tag来确定我们的row,从而定位到我们选择的cell
    replyTableViewCell *cell = (replyTableViewCell *)[_commentTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:buttom.tag inSection:0]];

    当然还有另外的办法,就是通过查找父视图的方法来得到对应的cell

     1.button.superView = cell.contentView; 
     2.button.superView.superView = cell; 
     3.button.superView.superView.superView = UITableviewWrapperView; 
     4.button.superView.superView.superView.superView = UITableView; 
    
    • 1
    • 2
    • 3
    • 4

    这样一来就实现了得到对应buttom的cell

    replyTableViewCell *cell = (replyTableViewCell *)[_commentTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:buttom.tag inSection:0]];
    replyTableViewCell *cell = (replyTableViewCell *)buttom.superview.superview;
    
    • 1
    • 2
    • 然后我们对对应的cellreply.numberOfLines进行修改
        if (cell.reply.numberOfLines == 0) {
            NSLog(@"1");
            cell.reply.numberOfLines = 2;
            [buttom setTitle:@" · 展示更多" forState:UIControlStateNormal];
        } else {
            NSLog(@"2");
            cell.reply.numberOfLines = 0;
            [buttom setTitle:@" · 收起" forState:UIControlStateNormal];
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 最后使用[_commentTableView beginUpdates]; [_commentTableView endUpdates];这两个方式刷新我们的tabelView

    注意这里必须使用[_commentTableView beginUpdates];[_commentTableView endUpdates];,否则仍然会发生按钮的复用,原因是:

    [_commentTableView reloadData]:
    
    • 1

    重新加载整个表格视图的数据。 此方法会重新调用数据源和代理方法,并刷新所有的行和部分。

    [_commentTableView beginUpdates][_commentTableView endUpdates]:
    
    • 1

    用于执行一系列的插入、删除、选择和重新加载的动画,而不需要调用 reloadData。 通常与
    insertRowsAtIndexPaths:withRowAnimation:、deleteRowsAtIndexPaths:withRowAnimation:、reloadRowsAtIndexPaths:withRowAnimation:
    等方法一起使用。

    UITableView 的 reloadData 方法会重新加载整个表格视图的数据,包括所有的行和部分。这会导致表格的重绘,所有的可见单元格都会被重新加载,也就是会调用 cellForRowAtIndexPath: 方法获取新的单元格。
    如果在 cellForRowAtIndexPath: 方法中没有正确处理单元格的重用标识符和状态,就会导致按钮等子视图的状态混乱,因为这些子视图的状态没有被正确更新。
    而使用 beginUpdates 和 endUpdates 方法执行一系列的插入、删除、选择和重新加载的操作时,系统会尽量保持现有单元格的状态,而不是重新加载整个单元格。这样,单元格的复用机制仍然有效,减少了对整个表格的重绘,从而减小了混乱的可能性。

    同时使用[_commentTableView beginUpdates];[_commentTableView endUpdates];还优化了tableviewcell加载与刷新的性能开销

  • 相关阅读:
    【必知必会的MySQL知识】①初探MySQL
    HarmonyOS的功能及场景应用
    1月笔记本电脑行业分析:多品牌下滑但ThinkPad逆势增长!
    C++前缀和算法的应用:预算内的最多机器人数目
    【大数据之Kafka】十二、Kafka之offset位移及漏消费和重复消费
    【支付宝生态质量验收与检测技术】
    【洛谷】P2713 罗马游戏
    ORB-SLAM3算法学习—Frame构造—ORB特征提取和BRIEF描述子计算
    [项目设计] 从零实现的高并发内存池(四)
    AdaMixer--真正的降维打击!!
  • 原文地址:https://blog.csdn.net/weixin_72437555/article/details/134561876