美文网首页
折叠Cell

折叠Cell

作者: Silence_xl | 来源:发表于2020-11-28 19:35 被阅读0次
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface FoldingModel : NSObject
    
    @property (copy, nonatomic) NSString *content;
    @property (assign, nonatomic) BOOL expanded; // 是否已经展开
    @property (assign, nonatomic) CGFloat cellHeight;// Cache
    
    @end
    
    NS_ASSUME_NONNULL_END
    //==============================================================
    #import "FoldingModel.h"
    
    @implementation FoldingModel
    
    @end
    
    #import <UIKit/UIKit.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @class FoldingModel;
    @class FoldingCell;
    
    @protocol FoldingCellDelegate <NSObject>
    
    - (void)foldingCell:(FoldingCell *)cell switchExpandedStateWithIndexPath:(NSIndexPath *)index;
    
    @end
    
    @interface FoldingCell : UITableViewCell
    
    @property (weak, nonatomic) id <FoldingCellDelegate> delegate;
    
    - (void)setFoldingModel:(FoldingModel *)entity indexPath:(NSIndexPath *)indexPath;
    
    @end
    
    NS_ASSUME_NONNULL_END
    //==============================================================
    #import "FoldingCell.h"
    #import "FoldingModel.h"
    #import <Masonry/Masonry.h>
    
    @interface FoldingCell ()
    
    @property (strong, nonatomic) UILabel *titleLabel;
    @property (strong, nonatomic) UILabel *contentLabel;
    @property (strong, nonatomic) UIButton *moreButton;
    @property (strong, nonatomic) MASConstraint *contentHeightConstraint;
    @property (strong, nonatomic) FoldingModel *foldingModel;
    @property (strong, nonatomic) NSIndexPath *indexPath;
    
    @end
    
    @implementation FoldingCell
    
    - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
        self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
        if (self) {
            self.selectionStyle = UITableViewCellSelectionStyleNone;
            [self initView];
        }
        return self;
    }
    
    - (void)setFoldingModel:(FoldingModel *)foldingModel indexPath:(NSIndexPath *)indexPath {
        _foldingModel = foldingModel;
        _indexPath = indexPath;
        _titleLabel.text = [NSString stringWithFormat:@"index: %ld, contentView: %p", (long) indexPath.row, (__bridge void *) self.contentView];
        _contentLabel.text = _foldingModel.content;
    
        if (_foldingModel.expanded) {
            [_contentHeightConstraint uninstall];
        } else {
            [_contentHeightConstraint install];
        }
    }
    
    - (void)switchExpandedState:(UIButton *)button {
        [_delegate foldingCell:self switchExpandedStateWithIndexPath:_indexPath];
    }
    
    - (void)initView {
    
        // Title
        _titleLabel = [[UILabel alloc] init];
        [self.contentView addSubview:_titleLabel];
        [_titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            make.height.mas_equalTo(21);
            make.left.right.top.mas_equalTo(self.contentView).insets(UIEdgeInsetsMake(4, 8, 4, 8));
        }];
    
        // More button
        _moreButton = [UIButton buttonWithType:UIButtonTypeSystem];
        [_moreButton setTitle:@"More" forState:UIControlStateNormal];
        [_moreButton addTarget:self action:@selector(switchExpandedState:) forControlEvents:UIControlEventTouchUpInside];
        [self.contentView addSubview:_moreButton];
        [_moreButton mas_makeConstraints:^(MASConstraintMaker *make) {
            make.height.mas_equalTo(32);
            make.left.right.bottom.mas_equalTo(self.contentView);
        }];
    
        // Content
        // 计算UILabel的preferredMaxLayoutWidth值,多行时必须设置这个值,否则系统无法决定Label的宽度
        CGFloat preferredMaxWidth = [UIScreen mainScreen].bounds.size.width - 16;
        _contentLabel = [[UILabel alloc] init];
        _contentLabel.numberOfLines = 0;
        _contentLabel.lineBreakMode = NSLineBreakByCharWrapping;
        _contentLabel.clipsToBounds = YES;
        _contentLabel.preferredMaxLayoutWidth = preferredMaxWidth; // 多行时必须设置
        [self.contentView addSubview:_contentLabel];
        [_contentLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.right.equalTo(self.contentView).insets(UIEdgeInsetsMake(4, 8, 4, 8));
            make.top.mas_equalTo(_titleLabel.mas_bottom).mas_offset(4);
            make.bottom.mas_equalTo(_moreButton.mas_top).mas_offset(-4);
            // 先加上高度的限制
            _contentHeightConstraint = make.height.mas_equalTo(@64).priorityHigh(); // 优先级只设置成High,比正常的高度约束低一些,防止冲突
        }];
    }
    
    @end
    
    #import <UIKit/UIKit.h>
    
    @interface ViewController : UIViewController
    
    @end
    //==============================================================
    #import "ViewController.h"
    #import "FoldingCell.h"
    #import "FoldingModel.h"
    
    @interface ViewController ()<UITableViewDelegate, UITableViewDataSource, FoldingCellDelegate>
    
    @property (strong, nonatomic) UITableView *tableView;
    @property (strong, nonatomic) FoldingCell *templateCell;
    @property (strong, nonatomic) NSArray *data;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [self.view addSubview:self.tableView];
        [self generateData];
    //    [_tableView reloadData];
    }
    
    - (UITableView *)tableView {
        if (!_tableView) {
            _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
            _tableView.delegate = self;
            _tableView.dataSource = self;
            _tableView.estimatedRowHeight = 0;
            _tableView.sectionHeaderHeight = 0.0;
            _tableView.sectionFooterHeight = 0.0;
            _tableView.estimatedSectionHeaderHeight = 0.0;
            _tableView.estimatedSectionFooterHeight = 0.0;
            [_tableView registerClass:[FoldingCell class] forCellReuseIdentifier:NSStringFromClass([FoldingCell class])];
        }
        return _tableView;
    }
    
    - (void)foldingCell:(FoldingCell *)cell switchExpandedStateWithIndexPath:(NSIndexPath *)index {
        // 改变数据
        FoldingModel *foldingModel = self.data[index.row];
        foldingModel.expanded = !foldingModel.expanded; // 切换展开还是收回
        foldingModel.cellHeight = 0; // 重置高度缓存
        
        // **********************************
        // 下面两种方法均可实现高度更新,都尝试下吧
        // **********************************
        
        // 刷新方法1:只会重新计算高度,不会reload cell,所以只是把原来的cell撑大了而已,还是同一个cell实例
            [_tableView beginUpdates];
            [_tableView endUpdates];
            [_tableView scrollToRowAtIndexPath:index atScrollPosition:UITableViewScrollPositionMiddle animated:YES];// 让展开/收回的Cell居中,酌情加,看效果决定
        
        // 刷新方法2:先重新计算高度,然后reload,不是原来的cell实例
    //    [_tableView reloadRowsAtIndexPaths:@[index] withRowAnimation:UITableViewRowAnimationFade];
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        return self.data.count;
    }
    
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        if (!_templateCell) {
            _templateCell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([FoldingCell class])];
        }
        // 获取对应的数据
        FoldingModel *foldingModel = _data[indexPath.row];
        
        // 判断高度是否已经计算过
        if (foldingModel.cellHeight <= 0) {
            // 填充数据
            [_templateCell setFoldingModel:foldingModel indexPath:[NSIndexPath indexPathForRow:-1 inSection:-1]]; // 设置-1只是为了方便调试,在log里面可以分辨出哪个cell被调用
            // 根据当前数据,计算Cell的高度,注意+1
            foldingModel.cellHeight = [_templateCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 0.5f;
            NSLog(@"Calculate height: %ld", (long) indexPath.row);
        } else {
            NSLog(@"Get cache %ld", (long) indexPath.row);
        }
        return foldingModel.cellHeight;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        FoldingCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([FoldingCell class]) forIndexPath:indexPath];
        [cell setFoldingModel:self.data[indexPath.row] indexPath:indexPath];
        cell.delegate = self;
        return cell;
    }
    
    - (void)generateData {
        NSMutableArray *tmpData = [NSMutableArray new];
        for (NSInteger i = 0; i < 20; i++) {
            FoldingModel *foldingModel = [[FoldingModel alloc] init];
            foldingModel.content = [NSString stringWithFormat:@"第%ld条数据 == %@",i,[self getRandomLengthStr]];
            [tmpData addObject:foldingModel];
        }
        self.data = tmpData;
    }
    
    - (NSString *)getRandomLengthStr {
        NSMutableString *str = [NSMutableString string];
        for (NSInteger i = 0; i < (arc4random() % 50) + 10; i++) {
            [str appendString:@"case 8 content."];
        }
        return [str copy];
    }
    
    @end
    
    
    Simulator Screen Shot - iPhone 11 Pro Max - 2020-11-28 at 19.35.01.png

    相关文章

      网友评论

          本文标题:折叠Cell

          本文链接:https://www.haomeiwen.com/subject/yfecwktx.html