美文网首页
折叠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