11-自定義cell(2種方法)
第一種方法 :使用程式碼建立
分析:
(1)重寫initWithStyle
方法,在這個方法裡建立控制元件或者尺寸約束(其實控制元件約束一般寫在layoutSubviews
裡)。
(2)在layoutSubviews
方法裡設定尺寸
(3)重寫模型的set方法
重寫模型的set方法:
先宣告模型型別,接受模型資料
@class XMGTopic;
@interface XMGTopicCell : UITableViewCell
/** 模型資料 */
@property (nonatomic, strong) XMGTopic *topic;
@end
程式碼:
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
// 增加頂部的控制元件,並且設定約束
// ...
// 增加底部的控制元件,並且設定約束
// ...
}
return self;
}
/*
- (void)layoutSubviews
{
[super layoutSubviews];
// 設定頂部和底部控制元件的frame
}*/
- (void)setTopic:(XMGTopic *)topic
{
_topic = topic;
// 設定頂部和底部控制元件的具體資料(比如文字資料、圖片資料)
}
@end
在Controller裡建立cell並匯入資料也有2種方法:
第一種:不常用(最基本的建立cell方法)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"cell";
// 快取池去取
XMGTopicCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) { // 快取池沒有,建立
cell = [[XMGTopicCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
// 匯入資料
cell.topic = self.topics[indexPath.row];
return cell;
}
第二種:通過註冊,常用
註冊的功能是:去快取池取cell,若沒有建立cell
/* cell的重用標識 */
static NSString * const XMGTopicCellId = @"XMGTopicCellId";
// 註冊cell, registerClass:程式碼註冊,registerNib:xib註冊
[self.tableView registerClass:[XMGTopicCell class] forCellReuseIdentifier:XMGTopicCellId];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
XMGTopicCell *cell = [tableView dequeueReusableCellWithIdentifier:XMGTopicCellId];
// 匯入資料
cell.topic = self.topics[indexPath.row];
return cell;
}
第二種方法:使用xib建立
分析:
(1)在- (void)awakeFromNib
中進行控制元件設定
(2)重寫模型的set方法
程式碼:
- (void)awakeFromNib
{
self.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"mainCellBackground"]];
}
- (void)setTopic:(XMGTopic *)topic
{
_topic = topic;
// 頂部控制元件的資料
[self.profileImageView sd_setImageWithURL:[NSURL URLWithString:topic.profile_image] placeholderImage:[UIImage imageNamed:@"defaultUserIcon"]];
self.nameLabel.text = topic.name;
self.passtimeLabel.text = topic.passtime;
self.text_label.text = topic.text;
// 底部按鈕的文字
[self setupButtonTitle:self.dingButton number:topic.ding placeholder:@"頂"];
[self setupButtonTitle:self.caiButton number:topic.cai placeholder:@"踩"];
[self setupButtonTitle:self.repostButton number:topic.repost placeholder:@"分享"];
[self setupButtonTitle:self.commentButton number:topic.comment placeholder:@"評論"];
}
在Controller裡註冊cell並匯入資料2種方法
第一種:通過mainBundle
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"cell";
// 快取池去取
XMGTopicCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) { // 快取池沒有,建立
cell = [[[NSBundle mainBundle] loadNibNamed:self owner:nil options:nil] firstObject];
}
// 匯入資料
cell.topic = self.topics[indexPath.row];
return cell;
}
第二種:通過註冊載入cell
/* cell的重用標識 */
static NSString * const XMGTopicCellId = @"XMGTopicCellId";
// 註冊cell
UINib *nib = [UINib nibWithNibName:NSStringFromClass([XMGTopicCell class]) bundle:nil];
[self.tableView registerNib:nib forCellReuseIdentifier:XMGTopicCellId];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
XMGTopicCell *cell = [tableView dequeueReusableCellWithIdentifier:XMGTopicCellId];
cell.topic = self.topics[indexPath.row];
return cell;
}
技巧:因為通過mainBundle載入資源,使用地方很多,可以將其封裝在UIView的分類裡
程式碼:
+ (instancetype)xmg_viewFromXib;
+ (instancetype)xmg_viewFromXib
{
return [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil].firstObject;
}
擴充:若有四種cell,其中有共同的部分,也有各自不同的部分,如何做?
分析:2種思路
第一種思路:父cell+子cell
分析:
- 因為xib無法繼承,所以需要用純程式碼或者頂部一個xib,底部一個xib,新增到cell上
- 在自定義子cell時,其它都相同,就是
-setTopic
時,需要先呼叫一下父控制元件的-setTopic
方法,即將共同的頂部與底部先載入進來並設定好資料。
子cell的自定義程式碼如下:
@implementation XMGVoiceCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
// 增加中間的聲音控制元件,並且設定約束
// ...
self.backgroundColor = [UIColor greenColor];
}
return self;
}
/*
- (void)layoutSubviews
{
[super layoutSubviews];
// 設定中間聲音控制元件的frame
}*/
- (void)setTopic:(XMGTopic *)topic
{
[super setTopic:topic];
// 設定中間聲音控制元件的具體資料(比如文字資料、圖片資料)
}
@end
第二種思路:只用一種cell
分析:
**(1)建立整體cell;
(2)中間內容: 自定義三種xib,分別是視訊、語音、圖片
(3)中間內容懶載入進整體cell中
**
@class XMGTopic;
@interface XMGTopicCell : UITableViewCell
/** 模型資料 */
@property (nonatomic, strong) XMGTopic *topic;
/* 中間控制元件 */
/** 圖片控制元件 */
@property (nonatomic, weak) XMGTopicPictureView *pictureView;
/** 聲音控制元件 */
@property (nonatomic, weak) XMGTopicVoiceView *voiceView;
/** 視訊控制元件 */
@property (nonatomic, weak) XMGTopicVideoView *videoView;
@end
@implementation XMGTopicCell
#pragma mark - 懶載入
- (XMGTopicPictureView *)pictureView
{
if (!_pictureView) {
XMGTopicPictureView *pictureView = [XMGTopicPictureView xmg_viewFromXib];
[self.contentView addSubview:pictureView];
_pictureView = pictureView;
}
return _pictureView;
}
- (XMGTopicVoiceView *)voiceView
{
if (!_voiceView) {
XMGTopicVoiceView *voiceView = [XMGTopicVoiceView xmg_viewFromXib];
[self.contentView addSubview:voiceView];
_voiceView = voiceView;
}
return _voiceView;
}
- (XMGTopicVideoView *)videoView
{
if (!_videoView) {
XMGTopicVideoView *videoView = [XMGTopicVideoView xmg_viewFromXib];
[self.contentView addSubview:videoView];
_videoView = videoView;
}
return _videoView;
}
#pragma mark - 初始化
- (void)awakeFromNib
{
self.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"mainCellBackground"]];
}
- (void)setTopic:(XMGTopic *)topic
{
_topic = topic;
// 頂部控制元件的資料
UIImage *placeholder = [UIImage xmg_circleImageNamed:@"defaultUserIcon"];
[self.profileImageView sd_setImageWithURL:[NSURL URLWithString:topic.profile_image] placeholderImage:placeholder options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
// 圖片下載失敗,直接返回,按照它的預設做法
if (!image) return;
self.profileImageView.image = [image xmg_circleImage];
}];
self.nameLabel.text = topic.name;
self.passtimeLabel.text = topic.passtime;
self.text_label.text = topic.text;
// 底部按鈕的文字
[self setupButtonTitle:self.dingButton number:topic.ding placeholder:@"頂"];
[self setupButtonTitle:self.caiButton number:topic.cai placeholder:@"踩"];
[self setupButtonTitle:self.repostButton number:topic.repost placeholder:@"分享"];
[self setupButtonTitle:self.commentButton number:topic.comment placeholder:@"評論"];
// 最熱評論
if (topic.top_cmt.count) { // 有最熱評論
self.topCmtView.hidden = NO;
NSDictionary *cmt = topic.top_cmt.firstObject;
NSString *content = cmt[@"content"];
if (content.length == 0) { // 語音評論
content = @"[語音評論]";
}
NSString *username = cmt[@"user"][@"username"];
self.topCmtLabel.text = [NSString stringWithFormat:@"%@ : %@", username, content];
} else { // 沒有最熱評論
self.topCmtView.hidden = YES;
}
// 中間的內容
if (topic.type == XMGTopicTypePicture) { // 圖片
self.pictureView.hidden = NO;
self.voiceView.hidden = YES;
self.videoView.hidden = YES;
} else if (topic.type == XMGTopicTypeVoice) { // 聲音
self.pictureView.hidden = YES;
self.voiceView.hidden = NO;
self.voiceView.topic = topic;
self.videoView.hidden = YES;
} else if (topic.type == XMGTopicTypeVideo) { // 視訊
self.pictureView.hidden = YES;
self.voiceView.hidden = YES;
self.videoView.hidden = NO;
} else if (topic.type == XMGTopicTypeWord) { // 段子
self.pictureView.hidden = YES;
self.voiceView.hidden = YES;
self.videoView.hidden = YES;
}
}
- (void)layoutSubviews
{
[super layoutSubviews];
if (self.topic.type == XMGTopicTypePicture) { // 圖片
self.pictureView.frame = self.topic.middleFrame;
} else if (self.topic.type == XMGTopicTypeVoice) { // 聲音
self.voiceView.frame = self.topic.middleFrame;
} else if (self.topic.type == XMGTopicTypeVideo) { // 視訊
self.videoView.frame = self.topic.middleFrame;
}
}
問題1:如何避免同一個cell的高度計算多次,即讓同一個cel的高度只計算一次?
答案1:利用模型屬性,額外增加計算cell高度的屬性,重寫cellHeight
的getter方法。在cellHeight
方法裡判斷是否計算過行高,已經計算過就返回)
流程分析:
當UITableView顯示的時候,系統開始就會呼叫
numberOfSections
和numberOfRows
方法,來知道行數和組數然後呼叫
heightForRow
方法,計算所有cell的高度,計算出來tableView的contentsize,確定tableView的滑動範圍。再呼叫
cellForRow
方法,顯示出cell當螢幕來回滑動的時候,
heightForRow
方法會呼叫的頻繁解決上述問題,就定義一個模型屬性,重寫
cellHeight
的getter方法,已經計算過就返回
擴充:
heightForRow
這個方法的特點:
- 預設情況下,每次重新整理表格的時候,有多少資料,這個方法就一次呼叫多少次 ---如:若有100條資料,每次
reloadData
,這個方法就會呼叫100次。 - 每當有cell進入螢幕範圍,就會呼叫一次這個方法。
/* 額外增加的屬性(並非伺服器返回的屬性,僅僅是為了提高開發效率) */
/** 根據當前模型計算出來的cell高度 */
@property (nonatomic, assign) CGFloat cellHeight;
/** 中間內容的frame */
@property (nonatomic, assign) CGRect middleFrame;
#import "XMGTopic.h"
@implementation XMGTopic
- (CGFloat)cellHeight
{
// 如果已經計算過,就直接返回
if (_cellHeight) return _cellHeight;
// 文字的Y值
_cellHeight += 55;
// 文字的高度
CGSize textMaxSize = CGSizeMake(XMGScreenW - 2 * XMGMarin, MAXFLOAT);
_cellHeight += [self.text boundingRectWithSize:textMaxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:15]} context:nil].size.height + XMGMarin;
// 中間的內容
if (self.type != XMGTopicTypeWord) { // 中間有內容(圖片、聲音、視訊)
CGFloat middleW = textMaxSize.width;
CGFloat middleH = middleW * self.height / self.width;
CGFloat middleY = _cellHeight;
CGFloat middleX = XMGMarin;
self.middleFrame = CGRectMake(middleX, middleY, middleW, middleH);
_cellHeight += middleH + XMGMarin;
}
// 最熱評論
if (self.top_cmt.count) { // 有最熱評論
// 標題
_cellHeight += 21;
// 內容
NSDictionary *cmt = self.top_cmt.firstObject;
NSString *content = cmt[@"content"];
if (content.length == 0) {
content = @"[語音評論]";
}
NSString *username = cmt[@"user"][@"username"];
NSString *cmtText = [NSString stringWithFormat:@"%@ : %@", username, content];
_cellHeight += [cmtText boundingRectWithSize:textMaxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:16]} context:nil].size.height + XMGMarin;
}
// 工具條
_cellHeight += 35 + XMGMarin;
return _cellHeight;
}
@end
在Controller,返回每行模型對應的行高(在模型裡,如果已經計算好,就會返回,若沒有計算行高,就會繼續執行計算行高)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
XMGTopic * topic=self.topics[indexPath.row];
return topic.cellHeight;
}
相關文章
- Java之自定義執行緒的2種方式Java執行緒
- 微信小程式使用自定義字型的三種方法微信小程式自定義字型
- 使用 GNOME 優化工具自定義 Linux 桌面的 10 種方法優化Linux
- Salesforce LWC學習(四十六) 自定義Datatable實現cell onclick功能Salesforce
- Spring Boot中從自定義Logback訪問Spring Bean三種方法Spring BootBean
- [譯] 為函式自定義屬性的八種實現方法函式
- [2]自定義Lua解析方式
- 各種 SAP 產品的自定義 UI 建立和整合方法一覽UI
- Ribbon - 幾種自定義負載均衡策略負載
- Android自定義View之requestLayout方法和invalidate方法AndroidView
- Android自定義View之invalidate方法和postInvalidate方法AndroidView
- 【朝花夕拾】Android自定義View篇之(四)自定義View的三種實現方式及自定義屬性詳解AndroidView
- Yii2自定義日誌字首
- 自定義元件-資料、方法、屬性元件
- 匯入自定義板塊的方法
- FormRequest 自定義獲取方法名字ORM
- 怎樣用apipost自定義加密方法API加密
- 建立Laravel自定義Helper輔助方法Laravel
- Flutter自定義Widget和使用方法Flutter
- 20190118-自定義實現replace方法
- IDEA自定義類註釋和方法註釋(自定義groovyScript方法實現多行引數註釋)Idea
- Javascript陣列排序sort方法和自定義排序方法JavaScript陣列排序
- Java異常處理的兩種方式以及自定義異常的使用方法Java
- 自定義View合輯(2)-餅狀圖View
- django2中自定義過濾器Django過濾器
- 11-物件導向-2-三大特性物件
- Hyperledger Fabric 2.x 自定義智慧合約
- 學習JUC原始碼(2)——自定義同步元件原始碼元件
- spring boot學習(2): SpringApplication和自定義bannerSpring BootAPP
- 自定義跳轉函式的通用unhook方法函式Hook
- H5自定義字型解決方法(mark)H5自定義字型
- linux初學者自定義IP地址的方法Linux
- 自定義u-boot啟動logo的方法bootGo
- 自定義檔案上傳功能實現方法
- Flutter 通過Clipper實現各種自定義形狀Flutter
- Go語言學習之路-11-方法與介面Go
- ROS2/C++ 自定義訊息型別ROSC++型別
- android自定義view(自定義數字鍵盤)AndroidView
- netty自定義Decoder用於自定義協議Netty協議