UI基礎-UITableView 高級

自定義cell

概要:

自定義cell就是創建一個UITableViewCell的子類。
把cell上的控件創建都封裝在子類中,簡化UIViewController中的代 碼。
子視圖控件添加到cell的contentView上。

通信

cell中聲明一個Model類型的屬性,viewController中獲取到Model對象後賦值給cell的Model屬性。
cell中重寫Model的setter方法,把Model對象中的內容重新賦值給各
個控件 M和V不直接進行通信,C負責M和V之間進行通信。

創建新工程,添加加載數據方法和創建TableView

// 加載數據
- (void)setUpData
{
    // 獲取文件路徑
    NSString *path = [[NSBundle mainBundle] pathForResource:@"StudentList" ofType:@"plist"];
    // 從該路徑 讀取文件
    NSArray *arr = [NSArray arrayWithContentsOfFile:path];

    // 字典轉化成模型  再放入字典或數組
    self.dataArr = [NSMutableArray array];

    for (NSDictionary *dic in arr) {
        CellModel *model = [[CellModel alloc] init];
        [model setValuesForKeysWithDictionary:dic];
        [self.dataArr addObject:model];
        [model release];
    }
}
- (void)addTableView
{
    UITableView *tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:(UITableViewStylePlain)];
    tableView.dataSource = self;
    tableView.delegate = self;
    [self.view addSubview:tableView];
    [tableView release];
}

自定義cell的步驟:

創建TableViewCell的子類

創建2個名爲MyTableViewCell、ManTableViewCell的繼承於TableViewCell的子類
這裏寫圖片描述

這裏寫圖片描述

重寫初始化方法

MyTableViewCell

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

ManTableViewCell

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

把要添加的控件 添加在cell的實現內容區域 contentView上面

MyTableViewCell

// 添加子視圖
- (void)addSubView
{
    self.imageV = [[UIImageView alloc] initWithFrame:CGRectMake(kMargin, kMargin, kImageWidth, kImageHeight)];
    self.imageV.backgroundColor = [UIColor redColor];
    [self.contentView addSubview:self.imageV];
    [self.imageV release];

    self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.imageV.right + kMargin, self.imageV.top, (kScreenWidth - 3*kMargin - self.imageV.width), (kImageHeight - 2*kLabelMargin)/3)];
    self.nameLabel.backgroundColor = [UIColor greenColor];
    [self.contentView addSubview:self.nameLabel];
    [self.nameLabel release];

    self.phoneLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.nameLabel.left, self.nameLabel.bottom + kLabelMargin, self.nameLabel.width, self.nameLabel.height)];
    self.phoneLabel.backgroundColor = [UIColor greenColor];
    [self.contentView addSubview:self.phoneLabel];
    [self.phoneLabel release];

    self.genderLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.phoneLabel.left, self.phoneLabel.bottom + kLabelMargin, self.phoneLabel.width, self.phoneLabel.height)];
    self.genderLabel.backgroundColor = [UIColor greenColor];
    [self.contentView addSubview:self.genderLabel];
    [self.genderLabel release];

}

ManTableViewCell

- (void)addSubview
{
    self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 40)];
    self.nameLabel.backgroundColor = [UIColor orangeColor];
    [self.contentView addSubview:self.nameLabel];
    [self.nameLabel release];
}

把系統的cell 替換成 自定義cell完成

MyTableViewCell

 static NSString *identifier = @"GirlCell";
 MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
     if (cell == nil) {
         cell = [[[MyTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];

ManTableViewCell

static NSString *identifier = @"ManCell";
ManTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
     if (cell == nil) {
         cell = [[[ManTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];

多種類型的cell混合使用

注意:

通常我們會在tableView:cellForRowAtIndexPath:方法中根據不同的。
Model來決定使用什麼類型的cell 每種類型的cell要定義不同的重用標識符 。
cell重用的時候會根據重用標識從重用隊列中取用哪種類型的cell。

新建一個cellModel繼承於NSObject,聲明它的屬性

// 名字
@property (nonatomic, retain) NSString *name;
// 性別
@property (nonatomic, retain) NSString *gender;
// 手機號
@property (nonatomic, retain) NSString *phoneNumber;

把它聲明成tableViewcell子類的屬性,見上文截圖中。

用model在- (UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath方法中判斷用哪種cell

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 根據model的值進行判斷 顯示不同的cell
    CellModel *model = self.dataArr[indexPath.row];
    if ([model.gender isEqualToString:@"女"]) {
        static NSString *identifier = @"GirlCell";
        MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
        if (cell == nil) {
            cell = [[[MyTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
        }
        // 在給model賦值的同時 咱們希望  也給cell上控件完成賦值
        cell.model = model;
        return cell;
    } else {
        static NSString *identifier = @"ManCell";
        ManTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
        if (cell == nil) {
            cell = [[[ManTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
        }
        cell.model = model;
        return cell;
    }

}

這樣就能達到下面的效果了
這裏寫圖片描述

cell自適應高度

獲取文本高度:(iOS7)

- (CGRect)boundingRectWithSize:(CGSize)size options:
(NSStringDrawingOptions)options attributes:(NSDictionary
*)attributes context:(NSStringDrawingContext *)context

注意
計算文本高度是所用的字體要和label顯示時用的字體一致。
label的寬度要和計算時使用的限定寬度一致。
這樣才能保證文本顯示在label中時,label高度恰巧夠。

例題:實現如下效果,label正好是適應文字大小

這裏寫圖片描述

新建工程,創建一個UIlabel

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 300, 100)];
label.backgroundColor = [UIColor orangeColor];
label.font = [UIFont systemFontOfSize:16];
label.text = @" 我崇拜高尚的生命的祕密。我崇拜這生命在降生、成長、戰鬥、傷殘、犧牲時迸濺出的鋼花焰火。我崇拜一個活靈靈的生命在崇山大河,在海洋和大陸上飄蕩的自由。是的,生命就是希望。它飄蕩無定,自由自在,它使人類中總有一支血脈不甘於失敗,九死不悔地追尋着自己的金牧場。";
[self.window addSubview:label];
[label release];

構建字體大小的字典 UIFont,NSFontAttributeName UIFont 在系統中的屬性的名字(key)

    // 參數size
    // 寬度 跟你的label一邊寬
    // 高度 填一個這段字最大高度
    // CGFLOAT_MAX 最大的float數
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:[UIFont systemFontOfSize:16], NSFontAttributeName, nil];
    CGRect frame = [label.text boundingRectWithSize:CGSizeMake(300, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin) attributes:dic context:nil];
    NSLog(@"%f", frame.size.height);

    // 更改label的高度
    CGRect temp = label.frame;
    temp.size.height = frame.size.height;
    label.frame = temp;

    label.numberOfLines = 0;
    [self.window addSubview:label];
    [label release];

這樣就能簡單實現label適應文字的大小了
那怎麼讓cell自適應高度呢?怎麼解決cell的複用問題呢?

例題:

新建工程,添加加載數據方法,添加表視圖

- (void)setUpData
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"NewsData" ofType:@"plist"];
    NSDictionary *dicData = [NSDictionary dictionaryWithContentsOfFile:path];

    self.dataArr = [NSMutableArray array];
    NSArray *newsArr = dicData[@"news"];
    for (NSDictionary *dic in newsArr) {
        NewsModel *model = [[NewsModel alloc] init];
        [model setValuesForKeysWithDictionary:dic];
        // 給點擊狀態賦值初值
        model.isSelect = NO;
        [self.dataArr addObject:model];
        [model release];
    }
}

- (void)addTableView
{
    UITableView *tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:(UITableViewStylePlain)];
    tableView.dataSource = self;
    tableView.delegate = self;
    [self.view addSubview:tableView];
    [tableView release];
}

創建NewsModel,聲明屬性

@property (nonatomic, retain) NSString *title;
@property (nonatomic ,retain) NSString *summary;

// 標識選中的狀態
@property (nonatomic, assign) BOOL isSelect;

創建NewsTableViewCell,重寫初始化方法,添加視圖

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

- (void)addSubview
{
    self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(kMargin, kMargin, kLabelWidth, kLabelHeight)];
    self.titleLabel.backgroundColor = [UIColor cyanColor];
    [self.contentView addSubview:self.titleLabel];
    [self.titleLabel release];

    self.summaryLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.titleLabel.left, self.titleLabel.bottom + kMargin, self.titleLabel.width, self.titleLabel.height)];
    self.summaryLabel.backgroundColor = [UIColor orangeColor];
    // 多行顯示
    self.summaryLabel.numberOfLines = 0;
    // 設置字體大小
    self.summaryLabel.font = [UIFont systemFontOfSize:16];
    [self.contentView addSubview:self.summaryLabel];
    [self.summaryLabel release];

    self.imageV = [[UIImageView alloc] initWithFrame:CGRectMake(self.titleLabel.right + kMargin, kMargin, 3 * kMargin, 3 * kMargin)];
    [self.contentView addSubview:self.imageV];
    [self.imageV release];
}

實現協議中的兩個方法

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"MyCell";
    NewsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (cell == nil) {
        cell = [[[NewsTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
    }
    NewsModel *model = self.dataArr[indexPath.row];
    cell.model = model;
    return cell;
}

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

實現cell的點擊方法

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 取出點擊 cell對應的model
    NewsModel *model = self.dataArr[indexPath.row];

    // 取出被點擊的cell
    NewsTableViewCell *cell = (NewsTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];

    // 更改點擊的狀態
    model.isSelect = !model.isSelect;
    if (model.isSelect == YES) {
        // 更改cell上的圖片
        cell.imageV.image = [UIImage imageNamed:@"select"];
    } else {
        // 更改cell上的圖片
        cell.imageV.image = [UIImage imageNamed:@"cancel"];
    }

}

重寫model的setter方法

- (void)setModel:(NewsModel *)model
{
    if (_model != model) {
        [_model release];
        _model = [model retain];
    }
    self.titleLabel.text = model.title;
    self.summaryLabel.text = model.summary;

    // 利用model中的點選狀態 解決cell複用問題
    // 需要每次被複用的cell 再進行一次與狀態對應的賦值
    if (model.isSelect == YES) {
        self.imageV.image = [UIImage imageNamed:@"select"];
    } else {
        self.imageV.image = [UIImage imageNamed:@"cancel"];
    }

    // 獲取字符串的高度
    CGFloat summaryHeight = [NewsTableViewCell cellHeightForModel:model];
    // 改變一下label的高度
    CGRect frame = self.summaryLabel.frame;
    frame.size.height = summaryHeight;
    self.summaryLabel.frame = frame;

}

計算高度的類方法

+ (CGFloat)cellHeightForModel:(NewsModel *)model
{
  // 創建字體大小的字典
    NSDictionary *fontDic = @{NSFontAttributeName:[UIFont systemFontOfSize:16]};
    // 計算字符串高度
    CGRect textRect = [model.summary boundingRectWithSize:CGSizeMake(kLabelWidth, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin) attributes:fontDic context:nil];
    return textRect.size.height;
}

返回cell的動態高度

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NewsModel *model = self.dataArr[indexPath.row];
    CGFloat labelHeight = [NewsTableViewCell cellHeightForModel:model];

    // 上邊距 + topLabel + 中間間距 + 動態label高度 + 下邊距
    return 20 + labelHeight + 40 + 10;
}

這裏寫圖片描述
這樣就解決了cell的自適應高度和cell的複用問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章