UITableView介紹 之 複雜cell的高度計算

複雜cell的高度計算

  • 實現了多行文字的自適應
  • 實現了圖文混排的自適應

  在個人平常開發中常常會遇到cell內容比較複雜的狀況,複雜的cell勢必會有cell高度不相同的狀況,這種需求每每是比較蛋疼的。可是不要緊只要掌握了其中的原理剩下的就只是體力活了。本文采用傳統方式純代碼佈局來計算cell的高度值,若是精力容許下篇介紹AutoLayout自動佈局下的cell高度的計算。app

ViewController中的主要代碼實現

爲了方便起見本ViewController繼承自UITableViewController具體實現代碼以下:異步

//
//  MTableViewController.m
//  cell計算
//
//  Created by code_xq on 16/3/12.
//  Copyright © 2016年 code_xq. All rights reserved.
//

#import "MTableViewController.h"
#import "MTableViewCell.h"
#import "DataModel.h"

static NSString *ID = @"cell";

@interface MTableViewController ()

@property (nonatomic, strong) NSMutableArray *dataSource;

@end

@implementation MTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.title = @"cell的高度計算";
    // 去除tableView的默認下劃線
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    self.tableView.backgroundColor = [UIColor colorWithWhite:0.9 alpha:0.9];
    
    // 註冊cell
    [self.tableView registerClass:[MTableViewCell class] forCellReuseIdentifier:ID];
    
    // 異步獲取數據
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSString *path = [[NSBundle mainBundle] pathForResource:@"cellList.plist" ofType:nil];
        NSArray *array = [NSArray arrayWithContentsOfFile:path];
        
        for (NSDictionary *dict in array) {
            DataModel *dm = [DataModel initWith:dict];
            [self.dataSource addObject:dm];
        }
        // 造數據
        [self.dataSource addObjectsFromArray:self.dataSource];
        // 在主線程中刷新數據
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.tableView reloadData];
        });
    });
}

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.dataSource.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID forIndexPath:indexPath];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    cell.dataModel = self.dataSource[indexPath.row];
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    DataModel *dm = self.dataSource[indexPath.row];
    return dm.cellHeight;
}

/**
 *  給出cell的估計高度,主要目的是優化cell高度的計算次數
 */
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 200;
}

/**
 * 初始化數據
 */
- (NSMutableArray *)dataSource {
    if (_dataSource == nil) {
        _dataSource = [NSMutableArray array];
    }
    
    return _dataSource;
}

@end

這段代碼是初始化tableView的常規作法,值得注意的是UITableView的
estimatedHeightForRowAtIndexPath 這個方法給出cell的預估值,若是沒有實現這個方法,tableView內部會一次性將全部的cell的高度所有計算出來,有了這個方法會對tableView的性能有所提升,這個方法的返回值理論上能夠是任意值。async

Model數據的代碼實現

//
//  DataModel.h
//  cell計算
//
//  Created by code_xq on 16/3/12.
//  Copyright © 2016年 code_xq. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface DataModel : NSObject

/** 文字內容 */
@property (nonatomic, copy) NSString *text;

/** 圖標*/
@property (nonatomic, copy) NSString *icon;

/** 圖片*/
@property (nonatomic, copy) NSString *picture;

/** 用戶名*/
@property (nonatomic, strong) NSString *name;

/** cell的高度*/
@property (nonatomic, assign) CGFloat cellHeight;

+ (instancetype)initWith:(NSDictionary *)dict;

@end

/***********************類的實現*********************/

#import "DataModel.h"

@implementation DataModel

+ (instancetype)initWith:(NSDictionary *)dict {
    DataModel *dm = [[self alloc] init];
    [dm setValuesForKeysWithDictionary:dict];
    return dm;
}
@end

數據是從plist中獲取直接轉換成DataModel對象的,這裏多了一個cellHeight的屬性用來存放cell的高度。佈局

重頭戲自定義cell的代碼實現

//
//  MTableViewCell.h
//  cell計算
//
//  Created by code_xq on 16/3/12.
//  Copyright © 2016年 code_xq. All rights reserved.
//

#import <UIKit/UIKit.h>
@class DataModel;
@interface MTableViewCell : UITableViewCell

/** 數據模型*/
@property (nonatomic, strong) DataModel *dataModel;

@end

/***********************類的實現*********************/


#import "MTableViewCell.h"
#import "UIView+Expand.h"
#import "DataModel.h"

#define SCWIDTH [UIScreen mainScreen].bounds.size.width
#define SCHEIGHT [UIScreen mainScreen].bounds.size.height

static CGFloat const margin = 10;

@interface MTableViewCell()

@property (nonatomic, weak) UIImageView *imageIcon;
@property (nonatomic, weak) UILabel *labelName;
@property (nonatomic, weak) UILabel *labelContent;
@property (nonatomic, weak) UIImageView *picView;
@end

@implementation MTableViewCell


- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self setUpView];
    }
    return self;
}


- (void)setUpView {
    // 用戶頭像
    UIImageView *imageIcon = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
    imageIcon.left = margin;
    imageIcon.top = margin;
    self.imageIcon = imageIcon;
    [self.contentView addSubview:imageIcon];
    
    // 用戶名
    CGFloat nameW = SCWIDTH - imageIcon.width - 3 * margin;
    UILabel *labelName = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, nameW, 30)];
    labelName.left = imageIcon.right + margin;
    labelName.top = margin;
    labelName.font = [UIFont systemFontOfSize:20];
    self.labelName = labelName;
    [self.contentView addSubview:labelName];
    
    // 文字內容
    UILabel *labelContent = [[UILabel alloc] initWithFrame:CGRectMake(margin, 0, SCWIDTH - 20 , 30)];
    labelContent.top = imageIcon.bottom + margin;
    // 設置顯示多行文字
    labelContent.lineBreakMode = NSLineBreakByCharWrapping;
    labelContent.numberOfLines = 0;
    labelContent.font = [UIFont systemFontOfSize:15];
    self.labelContent = labelContent;
    [self.contentView addSubview:labelContent];
    
    // 圖片
    UIImageView *picView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 120, 90)];
    picView.left = margin;
    self.picView = picView;
    [self.contentView addSubview:picView];
}


- (void)setFrame:(CGRect)frame {
    frame = CGRectMake(frame.origin.x, frame.origin.y + 10, frame.size.width, frame.size.height - 10);
    [super setFrame:frame];
}


- (void)setDataModel:(DataModel *)dataModel {
    _dataModel = dataModel;
    // 設置用戶頭像
    self.imageIcon.image = [UIImage imageNamed: dataModel.icon];
    // 設置用戶名
    self.labelName.text = dataModel.name;
    
    // 計算文字內容的高度
    CGFloat height =  [dataModel.text boundingRectWithSize:CGSizeMake(SCWIDTH - 2 * margin, CGFLOAT_MAX)
                                                   options:NSStringDrawingUsesLineFragmentOrigin
                                                attributes:@{NSFontAttributeName : self.labelContent.font}
                                                   context:nil].size.height;
    self.labelContent.height = height;
    self.labelContent.text = dataModel.text;
    
    // 設置圖片內容
    if (dataModel.picture) {
        self.picView.hidden = NO;
        self.picView.top = self.labelContent.bottom + margin;
        self.picView.image = [UIImage imageNamed:dataModel.picture];
        dataModel.cellHeight = self.picView.bottom + 2 * margin;
    } else {
        self.picView.hidden = YES;
        dataModel.cellHeight = self.labelContent.bottom + 2 * margin;
    }
    
}

@end

爲了佈局方便本身給UIView寫了個分類能夠很容易獲取view的left、top、right、bottom的值,好了到此複雜cell的高度計算就講完了,若是有機會繼續講述AutoLayout自動佈局下的cell高度的計算。性能

相關文章
相關標籤/搜索