iOS 自定義tableView Cell、高度自適應(yīng)

1.xib方式創(chuàng)建

每個cell的顯示的內(nèi)容都是固定的,也就是cell的高度都是相同的

加載數(shù)據(jù)

有plist文件數(shù)據(jù)結(jié)構(gòu)如下


創(chuàng)建數(shù)據(jù)模型

Product.h
@interface Product : NSObject

@property (nonatomic , copy) NSString *name;
@property (nonatomic , copy) NSString *location;
@property (nonatomic , copy) NSString *count;
@property (nonatomic , copy) NSString *price;
@property (nonatomic , copy) NSString *icon;
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)goodsWithDict:(NSDictionary *)dict;
@end

Product.m
@implementation Product
- (instancetype)initWithDict:(NSDictionary *)dict{
    if (self = [super init]) {
        self.name = dict[@"name"];
        self.location = dict[@"location"];
        self.count = dict[@"minproduct"];
        self.price = dict[@"price"];
        self.icon = dict[@"icon"];
//        [self setValuesForKeysWithDictionary:dict];
    }
    return self;
}
+ (instancetype)goodsWithDict:(NSDictionary *)dict{
    return [[self alloc]initWithDict:dict];
}
@end

懶加載數(shù)據(jù)

PageFirstTableViewController.m
//用來存儲所有團購商品的數(shù)據(jù)
@property (nonatomic , strong) NSMutableArray *goods;

#pragma mark -lazyload
- (NSMutableArray *)goods{
    if (_goods ==nil) {
        NSString *path = [[NSBundle mainBundle]pathForResource:@"dataSource.plist" ofType:nil];
        NSArray *arrayDict = [NSArray arrayWithContentsOfFile:path];
       //字典轉(zhuǎn)模型
        NSMutableArray *arrayModels = [NSMutableArray array];
        for (NSDictionary *dict in arrayDict) {
            Product *model = [Product goodsWithDict:dict];
            [arrayModels addObject:model];
        }
        _goods = arrayModels;
    }
    return _goods;
}

實現(xiàn)數(shù)據(jù)源協(xié)議

通過xib方式實現(xiàn)自定義cell

創(chuàng)建以一個.xib文件。在xib中拖一個UITableViewCell,設(shè)置高寬。向UITableViewCell中拖子控件。


創(chuàng)建一個繼承自UITableViewCell的類ProductCell與xib文件的cell相關(guān)聯(lián)。通過拖線的方式將cell的子控件拖線到ProductCell的屬性上。

ProductCell.m
@property (weak, nonatomic) IBOutlet UILabel *name;
@property (weak, nonatomic) IBOutlet UILabel *price;
@property (weak, nonatomic) IBOutlet UIImageView *icon;
....略

實現(xiàn)數(shù)據(jù)源協(xié)議

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    //1.獲取模型數(shù)據(jù)
    Product *model = self.goods[indexPath.row];
    //2.創(chuàng)建單元格
    //通過xib創(chuàng)建單元格
    //由于此方法調(diào)用十分頻繁,cell的標示聲明成靜態(tài)變量有利于性能優(yōu)化
    static NSString *ID = @"goods_cell"; //要在xib中設(shè)置這個id
    ProductCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        //加載xib文件,loadNibName:方法返回的是一個數(shù)組,因為xib中可能有不止一個控件
        cell = [[[NSBundle mainBundle]loadNibNamed:@"ProductCell" owner:nil options:nil] firstObject];//不能加xib后綴
    }
    //3.把模型數(shù)據(jù)設(shè)置給單元格
    cell.name.text = model.name;
    cell.price.text = [NSString stringWithFormat:@"¥%@", model.price];
    cell.icon.image = [UIImage imageNamed: model.icon];
   ...賦值,略
    //4.返回單元格
    return cell;
}

在控制器中直接為cell重的每個子控件賦值數(shù)據(jù)造成的問題:
1.控制器強依賴于cell,一旦cell內(nèi)部子控件發(fā)生了變化,那么控制器中的代碼也得改(緊耦合)。控制器完全依賴于單元格里面的屬性。
2.cell的封裝不夠完整,凡是用到cell的地方,每次都要編寫為cell的子控件依次賦值的語句,比如:cell.xxx = model.xxx。如果有10個控制器,每個控制器里都需要用到單元格進行賦值,如果一個單元格里有10個子控件,那么上面這樣的代碼就要寫10次。

對自定義cell進行封裝,把模型數(shù)據(jù)設(shè)置給單元格,形如:cell.goods = model;由cell對象內(nèi)部自己來解析模型數(shù)據(jù),并把數(shù)據(jù)設(shè)置到對應(yīng)的子控件中。在cell中創(chuàng)建一個模型類型的屬性,重寫該屬性的set方法,在set方法中將數(shù)據(jù)賦值給控件。

ProductCell.h
#import "Product.h"
@interface ProductCell : UITableViewCell
@property (nonatomic , strong) Product *goods;
//封裝一個創(chuàng)建自定義cell的方法
+ (instancetype)productCellWithTableView:(UITableView *)tableView;
@end

ProductCell.m
//重寫set方法
- (void)setGoods:(Product *)goods{
    _goods =goods;
    self.name.text = goods.name;
    self.price.text = [NSString stringWithFormat:@"¥%@",goods.price];
    self.icon.image = [UIImage imageNamed:goods.icon];
    self.location.text = goods.location;
    self.count.text = [NSString stringWithFormat:@"最低批發(fā)量:%@",goods.count];
}
+ (instancetype)productCellWithTableView:(UITableView *)tableView{
    static NSString *ID = @"goods_cell";
    ProductCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        cell = [[[NSBundle mainBundle]loadNibNamed:@"ProductCell" owner:nil options:nil] firstObject];//不能加xib后綴
    }
    return cell;
}

修改后的數(shù)據(jù)源方法

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    Product *model = self.goods[indexPath.row];
    ProductCell *cell = [ProductCell productCellWithTableView:tableView];
    cell.goods = model;
    return cell;
}

注意:要設(shè)置tableView.rowHeight = xib中的cell的高度。不然會報警告

2.純代碼方式創(chuàng)建(frameLayout,自適應(yīng)高度)

每個cell顯示的內(nèi)容不固定,cell的高度需要根據(jù)內(nèi)容的多少自適應(yīng)高度(例如微博,朋友圈):
iOS開發(fā)UI篇—使用純代碼自定義UItableviewcell實現(xiàn)一個簡單的微博界面布局

使用純代碼自定義一個tableview的步驟
1.新建一個繼承自UITableViewCell的類
2.重寫initWithStyle:reuseIdentifier:方法
添加所有需要顯示的子控件(不需要設(shè)置子控件的數(shù)據(jù)和frame, 子控件要添加到contentView中)
進行子控件一次性的屬性設(shè)置(有些屬性只需要設(shè)置一次, 比如字體\固定的圖片)
3.提供2個模型
數(shù)據(jù)模型: 存放文字數(shù)據(jù)\圖片數(shù)據(jù)
frame模型: 存放數(shù)據(jù)模型\所有子控件的frame\cell的高度
4.cell擁有一個frame模型(不要直接擁有數(shù)據(jù)模型)
5.重寫frame模型屬性的setter方法: 在這個方法中設(shè)置子控件的顯示數(shù)據(jù)和frame
6.frame模型數(shù)據(jù)的初始化已經(jīng)采取懶加載的方式(每一個cell對應(yīng)的frame模型數(shù)據(jù)只加載一次)

原文例子里自定義cell自適應(yīng)高度是通過加減乘除運算來計算出來的,沒有使用autolayout

結(jié)合鏈接原文里面的例子,講一下自己的個人理解。

  • 步驟2:NJWeiboCell.m文件,在重寫的initWithStyle:reuseIdentifier:方法里創(chuàng)建并添加子控件到contentView上,注意創(chuàng)建的子控件屬性要聲明為weak。另外,像微博vip皇冠圖標這種固定的內(nèi)容的控件,進行一次性數(shù)據(jù)設(shè)置即可。

  • 步驟3、4:除了數(shù)據(jù)模型(NJWeibo.m)以外還需要frame模型,自定義cell持有frame模型,frame模型持有數(shù)據(jù)模型。
    為什么還需要frame模型?如果沒有frame模型,那么自定義cell中直接持有數(shù)據(jù)模型,即- (void)setWeiboFrame:(NJWeiboFrame *)weiboFrame替換為- (void)setWeibo:(NJWeibo *)weibo,然后在- (void)settingFrame方法中詳細設(shè)置控件frame,得出cell的高度。每個cell的高度是隨子控件內(nèi)容而變化的。
    但是在tableView中,- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath設(shè)置行高的代理方法要比- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath方法先調(diào)用。如果沒有提供frame模型,那么要直到執(zhí)行數(shù)據(jù)源方法,把模型數(shù)據(jù)設(shè)置給單元格(形如:cell.weibo = model;)時,才能獲取得到cell的行高。
    如果獨立出frame模型,就可以在frame模型中詳細設(shè)置控件frame,得出cell高度,然后在設(shè)置行高的代理方法中取出對應(yīng)的frame模型中的行高。

  • 獲取文本lable的寬和高:- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context ;參數(shù)1是計算大小的指定范圍;如果將來計算的文字的范圍超出了指定的范圍,返回的就是指定的范圍;如果沒有超出那么返回的就是真實的范圍;不限制范圍的話設(shè)置CGSizeMake(MAXFLOAT, MAXFLOAT)就可以了。要注意的是:如果是獲取多行文本的話,在此之前需要設(shè)置文本控件的numberOfLines屬性為0。

另外,如果是獲取單行文本的size :可以用sizeWithAttributes:方法

更新:使用xib創(chuàng)建自適應(yīng)高度的tableViewCell
UITableViewCell高度自適應(yīng)探索這篇文章寫得挺細致的,沒有其他要補充,大致上和用代碼創(chuàng)建自適應(yīng)高度cell的實現(xiàn)原理上是一樣的。比起使用frameLayout創(chuàng)建cell要簡單一點,不需要另外設(shè)置frame模型來存放所有子控件的frame、cell的高度。

現(xiàn)在有個第三方框架可以很方便地創(chuàng)建高度自適應(yīng)的tableView cell:
優(yōu)化UITableViewCell高度計算的那些事
UITableView+FDTemplateLayoutCell 源碼閱讀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內(nèi)容