长文本在复杂cell里的收缩与展开

在处理UITableView的复杂cell时,如果包含多种元素如UILabel、UIImageView等,长文本的收缩与展开操作会变得复杂,因为需要考虑其他元素的布局调整。通过在MODEL层保存各种高度数据,并在显示时动态计算,可以实现这一功能。详细实现和示例GIF可在GitHub上的项目查看。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一般情况下,我们在cell里处理单纯的长文本收缩与展开是不难的,但是遇到一些复杂cell的时候,里面有各种uilabe、uiimageview、图片、声音、视频时,问题就变得比较复杂,因为你处理了cell里其中一个长文本时,其他cell里的元素得重新布局。

效果如下


由于上传限制,复杂cell的GIF见 GitHub   https://github.com/targetcloud/baisibudejie






实现思路如下:

在MODEL层,保存各种高度数据

//
//  TGTopicNewM.h
//  baisibudejie
//
//  Created by targetcloud on 2017/5/30.
//  Copyright © 2017年 targetcloud. All rights reserved.
//

#import <Foundation/Foundation.h>
@class TGUserNewM;
@class TGCommentNewM;
@interface TGTopicNewM : NSObject
@property (nonatomic, copy) NSString *ID;
@property (nonatomic, copy) NSString *type;//audio image video gif text
@property (nonatomic, assign) NSInteger status;
@property (nonatomic, assign) NSInteger up;
@property (nonatomic, assign) NSInteger down;
@property (nonatomic, assign) NSInteger forward;
@property (nonatomic, assign) NSInteger comment;
@property (nonatomic, copy) NSString *share_url;
@property (nonatomic, copy) NSString *passtime;
@property (nonatomic, assign) NSInteger bookmark;
@property (nonatomic, copy) NSString *text;
@property (nonatomic, strong) TGUserNewM *u;

@property (nonatomic, assign) NSInteger video_playfcount;
@property (nonatomic, assign) NSInteger video_width;
@property (nonatomic, assign) NSInteger video_height;
@property (nonatomic, copy) NSString *video_uri;
@property (nonatomic, assign) NSInteger video_duration;
@property (nonatomic, assign) NSInteger  video_playcount;
@property (nonatomic, copy) NSString * video_thumbnail;
@property (nonatomic, copy) NSString * video_thumbnail_small;

@property (nonatomic, strong) NSArray<TGCommentNewM *> *top_comments;

@property (nonatomic, copy) NSString * image_medium;
@property (nonatomic, copy) NSString * image_big;
@property (nonatomic, assign) NSInteger image_height;
@property (nonatomic, assign) NSInteger image_width;
@property (nonatomic, copy) NSString * image_small;
@property (nonatomic, copy) NSString * image_thumbnail_small;

@property (nonatomic, copy) NSString * images_gif;
@property (nonatomic, assign) NSInteger gif_width;
@property (nonatomic, copy) NSString * gif_thumbnail;
@property (nonatomic, assign) NSInteger gif_height;

@property (nonatomic, assign) NSInteger audio_playfcount;
@property (nonatomic, assign) NSInteger audio_height;
@property (nonatomic, assign) NSInteger audio_width;
@property (nonatomic, assign) NSInteger audio_duration;
@property (nonatomic, assign) NSInteger audio_playcount;
@property (nonatomic, copy) NSString * audio_uri;
@property (nonatomic, copy) NSString * audio_thumbnail;
@property (nonatomic, copy) NSString * audio_thumbnail_small;

@property (nonatomic, assign,readonly) NSInteger width;
@property (nonatomic, assign,readonly) NSInteger height;
@property (nonatomic, copy,readonly) NSString * image;

@property (nonatomic, assign,readonly) CGFloat cellHeight;
@property (nonatomic, assign,readonly) CGFloat middleY;//用于收缩展开
@property (nonatomic, assign,readonly) CGFloat defaultHeight;//用于收缩展开
@property (nonatomic, assign,readonly) CGFloat textHeight;//用于收缩展开

@property (nonatomic, assign) CGRect middleFrame;
@property (nonatomic, assign, getter=isBigPicture) BOOL bigPicture;

@property (nonatomic, assign, getter=isShowAllWithoutComment) BOOL showAllWithoutComment;//用于评论VC
@property (nonatomic, assign,readonly) CGFloat cellHeightWithoutComment;//用于评论VC

@property (nonatomic, assign) CGFloat picProgress;
@property (nonatomic, assign,getter=is_voicePlaying) BOOL voicePlaying;
@property (nonatomic, assign,getter=is_videoPlaying) BOOL videoPlaying;
@property (nonatomic, assign,readonly) CGFloat commentVH;
@property (nonatomic, copy,readonly) NSMutableAttributedString * attrStrM;
@end

//
//  TGTopicNewM.m
//  baisibudejie
//
//  Created by targetcloud on 2017/5/30.
//  Copyright © 2017年 targetcloud. All rights reserved.
//

#import "TGTopicNewM.h"
#import "TGCommentNewM.h"
#import "TGUserNewM.h"

static CGFloat const MiddleHeightConstraint = 300;

@implementation TGTopicNewM
{
    CGFloat _cellHeight;//cell高,会变化
    CGFloat _defaultHeight;//第一次计算的缺省高度,不变,如长文本未展开前的高度
    CGFloat _cellHeightWithoutComment;//没有评论的cell高度(已展开状态)
    CGFloat _textHeight;
    CGFloat _commentVH;
    CGRect _middleFrame;
}

+ (NSDictionary *)mj_objectClassInArray {
    return @{
             @"top_comments" : [TGCommentNewM class]
             };
}

+ (NSDictionary *)mj_replacedKeyFromPropertyName {
    return @{
             @"ID" : @"id",
             
             @"video_playfcount" : @"video.playfcount",
             @"video_width" : @"video.width",
             @"video_height" : @"video.height",
             @"video_uri" : @"video.video[0]",
             @"video_duration" : @"video.duration",
             @"video_playcount" : @"video.playcount",
             @"video_thumbnail" : @"video.thumbnail[0]",
             @"video_thumbnail_small" : @"video.thumbnail_small[0]",
             
             @"image_medium" : @"image.medium[0]",
             @"image_big" : @"image.big[0]",
             @"image_height" : @"image.height",
             @"image_width" : @"image.width",
             @"image_small" : @"image.small[0]",
             @"image_thumbnail_small" :@"image.thumbnail_small[0]",
             
             @"images_gif" :@"gif.images[0]",
             @"gif_width" :@"gif.width",
             @"gif_thumbnail" :@"gif.gif_thumbnail[0]",
             @"gif_height" :@"gif.height",
             
             @"audio_playfcount" :@"audio.playfcount",
             @"audio_height" :@"audio.height",
             @"audio_width" :@"audio.width",
             @"audio_duration" :@"audio.duration",
             @"audio_playcount" :@"audio.playcount",
             @"audio_uri" :@"audio.audio[0]",
             @"audio_thumbnail" :@"audio.thumbnail[0]",
             @"audio_thumbnail_small" :@"audio.thumbnail_small[0]",
             
             };
}

-(NSString * )image{
    return [_type isEqualToString:@"video"] ? _video_thumbnail :
           [_type isEqualToString:@"image"] ? _image_big :
           [_type isEqualToString:@"gif"] ? _gif_thumbnail :
           [_type isEqualToString:@"audio"] ? _audio_thumbnail : @"";
}

-(NSInteger) width{
    return [_type isEqualToString:@"video"] ? _video_width :
           [_type isEqualToString:@"image"] ? _image_width :
           [_type isEqualToString:@"gif"] ? _gif_width :
           [_type isEqualToString:@"audio"] ? _audio_width : 0;
}

-(NSInteger) height{
    return [_type isEqualToString:@"video"] ? _video_height :
           [_type isEqualToString:@"image"] ? _image_height :
           [_type isEqualToString:@"gif"] ? _gif_height :
           [_type isEqualToString:@"audio"] ? _audio_height : 0;
}

- (CGFloat)cellHeight{
    if (_cellHeight) return _cellHeight;
    _commentVH = 0;
    _cellHeightWithoutComment = 0;
    _cellHeight += 55;
    
    CGSize textMaxSize = CGSizeMake(ScreenW - 2 * Margin , MAXFLOAT);
    
    _textHeight = [self.text boundingRectWithSize:textMaxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:14]} context:nil].size.height + 1;//+1是为了容错
    //   [self.text sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:textMaxSize].height;
    //长文本过长导致高度过高应受到高度约束
    _cellHeightWithoutComment = _cellHeight + _textHeight + Margin;
    _cellHeight += (_textHeight > TextHeightConstraint ? TextHeightConstraint : _textHeight) + Margin;
    
    if (![self.type isEqualToString: @"text"]) { //图片、视频
        CGFloat middleW = textMaxSize.width;
        CGFloat middleX = Margin;
        CGFloat middleH = middleW * self.height / self.width;
        if (middleH >= ScreenH) { // 显示的图片高度超过一个屏幕,就是超长图片
            middleH = MiddleHeightConstraint;
            _bigPicture = YES;
        }
        CGFloat middleY = _cellHeight;
        _middleY = middleY;
        _middleFrame = CGRectMake(middleX, middleY, middleW, middleH);
        //TGLog(@"%ld,%ld, %@ , %@ ,%@", self.height, self.width,self.type,self.text, NSStringFromCGRect(_middleFrame))
        _cellHeight += middleH + Margin;
        _cellHeightWithoutComment += middleH + Margin;
    }
    
    if (self.top_comments.count > 0){//评论
        textMaxSize = CGSizeMake(ScreenW - 2 * Margin - 10, MAXFLOAT);
        NSMutableAttributedString *attrStrM = [[NSMutableAttributedString alloc] init];
        for (int i=0 ; i <self.top_comments.count; i++){
            TGCommentNewM * com = self.top_comments[i];
            NSString *content = com.content;
            NSString *username = com.u.name;
            NSString *cmtText = [NSString stringWithFormat:@"%@ : %@", username,content];
            NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString : cmtText];
            [attrStr addAttribute:NSFontAttributeName
                            value:[UIFont systemFontOfSize:12.0f]
                            range:NSMakeRange(0, attrStr.length)];
            [attrStr addAttribute:NSForegroundColorAttributeName
                            value:TGColor(62, 114, 166)
                            range:NSMakeRange(0, username.length)];
            [attrStrM appendAttributedString:attrStr];
            if (i != self.top_comments.count-1){
                [attrStrM appendAttributedString: [[NSAttributedString alloc] initWithString:@"\n"]];
            }
            _commentVH += [cmtText boundingRectWithSize:textMaxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:12]} context:nil].size.height;
        }
        _attrStrM = attrStrM;
        _cellHeight += (_commentVH + 10);
    }
    _cellHeight += 35 ;//工具条
    _cellHeightWithoutComment += 35;
    //TGLog(@"_cellHeight,%@,%@,%f",self.type, self.text, _cellHeight)
    _defaultHeight = _cellHeight;
    return _cellHeight;
}

- (NSString *)passtime{
    NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
    fmt.dateFormat = @"yyyy-MM-dd HH:mm:ss";
    NSDate *create = [fmt dateFromString:_passtime];
    if (create.isThisYear) { // 今年
        if (create.isToday) { // 今天
            NSDateComponents *cmps = [[NSDate date] deltaFrom:create];
            if (cmps.hour >= 1) { // 时间差距 >= 1小时
                return [NSString stringWithFormat:@"%zd小时前", cmps.hour];
            } else if (cmps.minute >= 1) { // 1小时 > 时间差距 >= 1分钟
                return [NSString stringWithFormat:@"%zd分钟前", cmps.minute];
            } else { // 1分钟 > 时间差距
                return @"刚刚";
            }
        } else if (create.isYesterday) { // 昨天
            fmt.dateFormat = @"昨天 HH:mm:ss";
            return [fmt stringFromDate:create];
        } else { // 其他
            fmt.dateFormat = @"MM-dd HH:mm:ss";
            return [fmt stringFromDate:create];
        }
    } else { // 非今年
        return _passtime;
    }
}
@end


在Controller层,设置一个回调Block,或者用代理实现也合理,用于在cell展开或收缩后,能对此cell进行局部刷新

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    TGTopicNewCell *cell = [tableView dequeueReusableCellWithIdentifier:TGTopicCellID];
    cell.topic = self.topics[indexPath.row];
    //传入block,用于展开收缩
    __weak typeof(self)weakSelf = self;
    __weak typeof(cell)weakCell = cell;
    cell.block = ^ {
        weakCell.topic = weakSelf.topics[indexPath.row];
        [weakSelf.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath,nil] withRowAnimation:UITableViewRowAnimationNone];
    };
    return cell;
}


在Cell层(View),进行按钮的点击,由收缩变展开,由展开变收缩

- (IBAction)spreadBtnClick:(UIButton *)sender {
    [self.spreadBtn setTitle: ([self.spreadBtn.currentTitle isEqualToString:@"展开"] ? @"收缩" : @"展开" ) forState:UIControlStateNormal];
    
    self.topic.middleFrame = CGRectMake(self.topic.middleFrame.origin.x,
                                        [self.spreadBtn.currentTitle isEqualToString:@"收缩"] ?  self.topic.middleY + (self.topic.textHeight + 19.f - TextHeightConstraint) : self.topic.middleY,
                                        self.topic.middleFrame.size.width,
                                        self.topic.middleFrame.size.height);
    
    [self.topic setValue:@( [self.spreadBtn.currentTitle isEqualToString:@"收缩"] ? self.topic.cellHeight + (self.topic.textHeight + 19.f - TextHeightConstraint) : self.topic.defaultHeight) forKey:@"cellHeight" ];
    !(self.block) ? : self.block();
}

完整代码见 https://github.com/targetcloud/baisibudejie
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值