tableView

iOSCell根据文字自适应高度,实现点击收起展开

2016-04-07  本文已影响5298人  罗小耳

一、Cell点开收起

设计思路:
笔者考虑的是点击展开的操作放到tableView的头视图上,通过头视图的点击事件来控制Cell的个数显示。当然,也可以放到尾视图上,具体可灵活应用,此文只是提供一个
大体的思路。

Dem演示.gif

1、首先创建一个Model类,用来返回Cell个数

+ (NSInteger)cellNumberWith:(NSArray *)array andIsShow:(BOOL)isShow{
    // 判断传进来的数组中元素的个数是否大于设定的值
    // 大于则根据footerView的展开收起按钮,来控制cell的个数
    
    if (array.count > MAXNUMBER) {
        if (isShow) {
            // 展开状态下显示数组中的所有元素
            return array.count;
        }else{
            // 收起状态下只显示预先设定的个数
            return MAXNUMBER;
        }
    }else{
        // 小于预先设定的数值,则展示数组中的所有元素
        return array.count;
    }
    
    return 0;
}

2、设置三个属性,分别用来存放头视图、头视图展开状态和是否全部展开

// 存放头视图展开状态的字典
@property (nonatomic, strong) NSMutableDictionary *sectionIsShowAll;

// 存放头视图的字典
@property (nonatomic, strong) NSMutableDictionary *sectionHeaderView;

@property (nonatomic, copy) NSString *showAll; // YES展开全部,NO收起全部

3、在tableView的协议方法中,根据不同的展开收起状态返回Cell的个数

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSArray *rowAr = [[_orderArray objectAtIndex:section] objectForKey:@"array"];
    
    // 先判断是否存放过该secion分组对应的展开状态,若没存过则先默认为收起状态
    if ([self.sectionIsShowAll objectForKey:[NSString stringWithFormat:@"%ld", section]] == nil) {
        [self.sectionIsShowAll setObject:[NSNumber numberWithBool:NO] forKey:[NSString stringWithFormat:@"%ld", section]];
    }
    
    return [CellNumberModel cellNumberWith:rowAr andIsShow:[[self.sectionIsShowAll objectForKey:[NSString stringWithFormat:@"%ld", section]] boolValue]];
}

4、自定义头视图中要注意Section属性的设置

@property (nonatomic, assign) NSInteger section;
- (void)setCellNumber:(NSArray *)array section:(NSInteger)section
{
    self.section = section;
    [_moreBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.mas_equalTo(self.mas_centerX);
        make.width.mas_equalTo(self.mas_width);
        make.height.mas_equalTo(self.mas_height);
    }];
    
}

4、头视图中另一个重要的地方就是显示所有Cell按钮的点击事件

- (void)footerButtonAction
{
    NSLog(@"显示所有cell");
    
    self.moreBtn.selected = !self.moreBtn.selected;
    NSLog(@"selected===%zi", self.moreBtn.selected);
    
    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    [dic setObject:[NSNumber numberWithBool:self.moreBtn.selected] forKey:@"isShow"];
    [dic setObject:[NSNumber numberWithInteger:self.section] forKey:@"section"];
    
    // 笔者是通过block更改的controller中的展开收起的存储状态,实际中也可使用协议代理的方法
    self.MoreBlock (dic);
}

5、Controller中block的实现方式

    __weak FoldCellViewController *weakSelf = self;
    headerView.MoreBlock = ^ (NSDictionary *dic)
    {
        [weakSelf.sectionIsShowAll setObject:[dic objectForKey:@"isShow"] forKey:[NSString stringWithFormat:@"%@",[dic objectForKey:@"section"]]];
        
        // 为了更好的展示给用户,此处加了一个展开全部和收起全部的按钮,该按钮的显示及操作如下所示
        int showNum = 0;
        int closeNum = 0;
        for (int i = 0; i < self.sectionHeaderView.count; i++) {
            bool selected = [[weakSelf.sectionIsShowAll objectForKey:[NSString stringWithFormat:@"%d",i]] boolValue];
            if (selected == 1) {
                showNum++;
            } else if (selected == 0) {
                closeNum++;
            }
        }
        
        if (showNum == weakSelf.sectionHeaderView.count) {
            [rightBtn setTitle:@"全部收起" forState:UIControlStateNormal];
        } else if (closeNum == weakSelf.sectionHeaderView.count) {
            [rightBtn setTitle:@"全部展开" forState:UIControlStateNormal];
        }
        
        [_orderTable reloadData];
        
    };
    
#pragma 展开全部、收起全部按钮点击事件
- (void)rightBarButtonItemAction:(UIButton *)sender
{
    if ([sender.titleLabel.text isEqualToString:@"全部展开"]) {
        [sender setTitle:@"全部收起" forState:UIControlStateNormal];
        self.showAll = @"show";
    } else {
        [sender setTitle:@"全部展开" forState:UIControlStateNormal];
        self.showAll = @"close";
    }
    
    // 将存放Cell展开状态的字典中所有对象都改为展开或收起
    for (int i = 0; i < self.sectionHeaderView.count; i++) {
        OrderHeaderView *headerView = [self.sectionHeaderView objectForKey:[NSString stringWithFormat:@"%d", i]];
        if ([self.showAll isEqualToString:@"show"]) {
            NSMutableDictionary *showDic = [NSMutableDictionary dictionary];
            [showDic setObject:[NSNumber numberWithBool:1] forKey:@"isShow"];
            [showDic setObject:[NSNumber numberWithInteger:i] forKey:@"section"];
            headerView.MoreBlock(showDic);
            headerView.moreBtn.selected = 1;
        } else if ([self.showAll isEqualToString:@"close"]) {
            NSMutableDictionary *closeDic = [NSMutableDictionary dictionary];
            [closeDic setObject:[NSNumber numberWithBool:0] forKey:@"isShow"];
            [closeDic setObject:[NSNumber numberWithInteger:i] forKey:@"section"];
            headerView.MoreBlock(closeDic);
            headerView.moreBtn.selected = 0;
        }
        
    }
    
}

二、Cell中文字高度自适应

其实实现原理与上一部分是一样的,只是上面的Model中是改变数量,在这里变为改变高度

1、首先创建一个Model类,用来返回Cell高度

#import <Foundation/Foundation.h>

@interface RemarksCellHeightModel : NSObject

/*
 * contentStr:Lable内容
 * isShow:是否展开
 * width:Lable的宽度
 * font:字号
 * defaultHeight:默认高度,若大于该高度则显示展开收起按钮,低于该高度则正常显示文字高度
 * fixedHeight:其他控件固定高度
 * btnHeight:展开收起按钮高度
 */
+ (CGFloat)cellHeightWith:(NSString *)contentStr andIsShow:(BOOL)isShow andLableWidth:(CGFloat)width andFont:(CGFloat)font andDefaultHeight:(CGFloat)defaultHeight andFixedHeight:(CGFloat)fixedHeight andIsShowBtn:(CGFloat)btnHeight;

@end
______________________________________________________________________________________________________________________

#import "RemarksCellHeightModel.h"

@implementation RemarksCellHeightModel

+ (CGFloat)cellHeightWith:(NSString *)contentStr andIsShow:(BOOL)isShow andLableWidth:(CGFloat)width andFont:(CGFloat)font andDefaultHeight:(CGFloat)defaultHeight andFixedHeight:(CGFloat)fixedHeight andIsShowBtn:(CGFloat)btnHeight
{
    CGRect rect = [contentStr boundingRectWithSize:CGSizeMake(width, 1000) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:font]} context:nil];
    if (rect.size.height > defaultHeight) {
        if (isShow) {
            return fixedHeight + btnHeight + rect.size.height;
        }else{
            return fixedHeight + btnHeight + defaultHeight;
        }
    } else {
        return fixedHeight + rect.size.height;
    }
    
    return 100;
}

@end

2、设置一个属性,存放cell视图展开状态的字典

// 存放cell视图展开状态的字典
@property (nonatomic, strong) NSMutableDictionary *cellIsShowAll;

3、在tableView的协议方法中,根据不同的展开收起状态返回Cell的高度

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 返回Cell高度
    return [RemarksCellHeightModel cellHeightWith:[self.dataSource objectAtIndex:indexPath.row] andIsShow:[[self.cellIsShowAll objectForKey:[NSString stringWithFormat:@"%ld", indexPath.row]] boolValue] andLableWidth:BOUNDS.size.width-30 andFont:12 andDefaultHeight:52 andFixedHeight:42 andIsShowBtn:8];
}

4、自定义Cell中要注意控件的赋值操作

- (void)setCellContent:(NSString *)contentStr andIsShow:(BOOL)isShow andCellIndexPath:(NSIndexPath *)indexPath
{
    _textsLabel.text = contentStr;
    _cellIndexPath = indexPath;
    
// 获取文字高度
   CGRect rect = [_textsLabel.text boundingRectWithSize:CGSizeMake(BOUNDS.size.width-30, 1000) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12]} context:nil];
    if (rect.size.height > 52) {
        // 文字大于三行,显示展开收起按钮
        self.moreBtn.hidden = NO;
        if (isShow) {
            _textsLabel.numberOfLines = 0;
            [_textsLabel mas_remakeConstraints:^(MASConstraintMaker *make){
                make.left.mas_equalTo(self.contentView.mas_left).offset(15);
                make.top.mas_equalTo(_infolable.mas_bottom).offset(5);
                make.width.mas_equalTo(BOUNDS.size.width-30);
                make.height.mas_equalTo(rect.size.height);
            }];
        } else {
            [_textsLabel mas_remakeConstraints:^(MASConstraintMaker *make){
                make.left.mas_equalTo(self.contentView.mas_left).offset(15);
                make.top.mas_equalTo(_infolable.mas_bottom).offset(5);
                make.width.mas_equalTo(BOUNDS.size.width-30);
                make.height.mas_equalTo(52);
            }];
        }
    } else {
        // 文字小于三行,隐藏展开收起按钮
        _textsLabel.numberOfLines = 3;
        self.moreBtn.hidden = YES;
        [_textsLabel mas_remakeConstraints:^(MASConstraintMaker *make){
            make.left.mas_equalTo(self.contentView.mas_left).offset(15);
            make.top.mas_equalTo(_infolable.mas_bottom).offset(5);
            make.width.mas_equalTo(BOUNDS.size.width-30);
            make.height.mas_equalTo(rect.size.height+1);  // 由于系统计算的那个高度有时候会有1像素到2像素的误差,所以这里把高度+1
        }];
    }
}

5、显示更多按钮点击事件

#pragma mark - 显示更多按钮点击事件
- (void)moreButtonClicked
{
    self.moreBtn.selected = !self.moreBtn.selected;
    if (self.moreBtn.selected) {
        [self.moreBtn setImage:[UIImage imageNamed:@"xiala"] forState:UIControlStateNormal];
    }else{
        [self.moreBtn setImage:[UIImage imageNamed:@"shq"] forState:UIControlStateNormal];
    }
    
    // 记录当前按钮的选中状态,并传递给Controller
    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    [dic setObject:[NSNumber numberWithInteger:_cellIndexPath.row] forKey:@"row"];
    [dic setObject:[NSNumber numberWithBool:self.moreBtn.selected] forKey:@"isShow"];
    
    
    // 协议回调,改变Controller中存放Cell展开收起状态的字典
    if (self.delegate && [self.delegate respondsToSelector:@selector(remarksCellShowContrntWithDic:andCellIndexPath:)]) {
        [self.delegate remarksCellShowContrntWithDic:dic andCellIndexPath:_cellIndexPath];
    }
}

6、Controller中代理方法实现

#pragma mark -- Dalegate
- (void)remarksCellShowContrntWithDic:(NSDictionary *)dic andCellIndexPath:(NSIndexPath *)indexPath
{    
    [self.cellIsShowAll setObject:[dic objectForKey:@"isShow"] forKey:[NSString stringWithFormat:@"%@",[dic objectForKey:@"row"]]];
    
    [_tableView reloadData];
}

三、整个TableView中只有一个Cell有展开收起功能

实现原理同二,只是本部分更简单,不用字典来存放那么多的Cell的展开收起状态,只需一个全局的属性来存放Cell的展开收起状态

核心代码如下

{
    BOOL _isShow; // 是否展开
    NSString *_cellContentStr; // 备注消息
}
______________________________________________________________________________________________________________________
______________________________________________________________________________________________________________________
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section == 0) {
        return 174;
    } else if (indexPath.section == 1) {
        return [RemarksCellHeightModel cellHeightWith:_cellContentStr andIsShow:_isShow andLableWidth:BOUNDS.size.width-30 andFont:12 andDefaultHeight:52 andFixedHeight:42 andIsShowBtn:8];
    } else {
        return 38;
    }

}

______________________________________________________________________________________________________________________
______________________________________________________________________________________________________________________
        static NSString *cellName = @"meTableViewCell";
        RemarksTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellName];
        if (!cell) {
            cell = [[RemarksTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellName];
            cell.delegate = self;
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
        }
        [cell setCellContent:_cellContentStr andIsShow:_isShow  andCellIndexPath:indexPath];
        
        return cell;

______________________________________________________________________________________________________________________
______________________________________________________________________________________________________________________
#pragma mark -- Dalegate
- (void)remarksCellShowContrntWithDic:(NSDictionary *)dic andCellIndexPath:(NSIndexPath *)indexPath
{
    _isShow = [[NSString stringWithFormat:@"%@",[dic objectForKey:@"isShow"]] boolValue];
    
    [_tableView reloadData];
}


GitHub下载地址

Demo中的代码约束使用的是Masonry三方约束,Masonry使用详解

上一篇下一篇

猜你喜欢

热点阅读