【iOS開發黑科技】與tableViewDelegate,tableViewDataSource說再見,Objective-C,Swift雙版本...
CBTableViewSecret
前言
tableView可以說是iOS開發中使用頻率最高的容器檢視了。正是因為它的使用頻率高,所以時常引人思索。大家都知道,tableView資料的呈現和控制是依靠UITableViewDataSource和UITableViewDelegate來實現的。反反覆覆,冷冷清清,對於我們來說,也許按照蘋果官方提供的編碼方式非常簡單易懂,但是,當顯示的檢視型別繁多,資料複雜的時候,可能在代理函式中就需要去處理大量的計算,還有邏輯判斷。這給我們的開發帶來了難度。
縱觀世面上對待tableView普遍抱著不太熱情卻還能容忍的態度,所以大多數人還是把蘋果提供給我們的代理函式作為編寫tableView頁面的預設選擇。
問題來了,有沒有更好的開發方式呢,當然有,本人在長期與tableView“打交道”的基礎上,總結出了一套開發方式。這就是基於官方代理封裝的TableViewSecret。先讓我們來看一段程式碼,如下:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
if (indexPath.row % 2 == 0) {
return UITableViewAutomaticDimension;
} else {
return 70.0;
}
} else if (indexPath.section == 1) {
return 100.0;
} else {
if (indexPath.row == 0) {
return self.tableView.bounds.size.width / 16.0 * 9.0;
} else {
return 80.0;
}
}
}
該程式碼是tableView的代理函式設定cell高度的方法。當cell型別繁多的時候,是不是可閱讀性變得非常糟糕。維護起來也非常吃力。這裡的表格操作都是代理函式集中處理的。而現在,我們可以對這種寫法說再見。
讓它以組或行為單位,即一個section中包含若干的row,每個section或row都可以設定對應的引數,然後再通過資料模型組裝的方式把要呈現的資料拼接起來。
優勢和特點
- 更少程式碼量
- 不用再計算需要多少分組多少行,程式碼中累加
- cell型別過多的時候tableView分組和分行免去大量判斷,更好控制
- 處理事件更加清晰,把事件的持有物件由tableView轉移到display,即行列資料本身持有者
實現
CBTableViewSecret
專案中引入類 CBTableViewDisplay,旨在代替tableView所屬的controller成為tableView的dataSource和delegate。然後一系列的代理函式都由secret來實現。
此時secret是通用類,不提倡開發者直接更改,除非新增tableView的代理功能的實現藉口,所以此處藉助CBTableViewDisplay類,在tableView所在的controller中建立一個secret,secret與tableView相互關聯。
建立CBTableViewSecret例項並關聯tableView,關聯display
+ (instancetype)secretWithTableView:(UITableView *)tableView display:(CBTableViewDisplay *)display;
這樣一來,實際上tableView真正的資料來源和代理變成了display,我們可以用display來對tableView的呈現實現控制。
各層display
display自內向外包含三個層級,分別用3個類來定義,如下:
類名 | 用途 |
---|---|
CBTableViewRowDisplay | tableView行呈現類。用於確定cell的呈現資料和代理方法 |
CBTableViewSectionDisplay | tableView分組呈現類。用於確定每組header和footer的呈現資料和代理方法;同時也是CBTableViewRowDisplay資訊的容器類 |
CBTableViewDisplay | 外層類。用作CBTableViewSectionDisplay的外部呼叫容器類 |
通過上面層層巢狀實現了tableView的資料來源和代理已經為display類控制,此處display類也是設計的通用類,實際使用中儘量不讓開發者更改原始碼,所以這裡用block回撥的方式,實現了呼叫呈現方法的時候實現正反向雙向傳值,讓開發者在controller中呼叫這些決定tableView呈現的資料。方法宣告如下:
CBTableViewDisplay
+ (instancetype)displayWithSectionsBlock:(void(^)(NSMutableArray<CBTableViewSectionDisplay *>* sections))sectionsBlock;
CBTableViewSectionDisplay
+ (instancetype)displayWithHeaderHeight:(CGFloat)headerHeight autoHeaderHeight:(BOOL)autoHeaderHeight footerHeight:(CGFloat)footerHeight autoFooterHeight:(BOOL)autoFooterHeight rowsBlock:(void (^)(NSMutableArray<CBTableViewRowDisplay *> * rows))rowsBlock;
CBTableViewRowDisplay
+ (instancetype)displayWithCellHeight:(CGFloat)cellHeight autoCellHeight:(BOOL)autoCellHeight cellForRowAtIndexPath:(UITableViewCell *(^)(UITableView *, NSIndexPath *))cellForRowAtIndexPath;
使用
controller中的display方法
- (void)display {
CBTableViewDisplay *display = [CBTableViewDisplay displayWithSectionsBlock:^(NSMutableArray<CBTableViewSectionDisplay *> *sections) {
// 1.News
CBTableViewSectionDisplay *sec0 = [CBTableViewSectionDisplay displayWithHeaderHeight:45.0 autoHeaderHeight:NO footerHeight:50.0 autoFooterHeight:NO rowsBlock:^(NSMutableArray<CBTableViewRowDisplay *> *rows) {
for (NSInteger i = 0; i < self.newsModel.newslist.count; i++) {
CBTableViewRowDisplay * row = [CBTableViewRowDisplay displayWithCellHeight:60.0 autoCellHeight:YES cellForRowAtIndexPath:^UITableViewCell *(UITableView *tableView, NSIndexPath *indexPath) {
NewsListTableViewCell *cell = [tableView cellWithClass:[NewsListTableViewCell class] fileType:FileTypeNib];
cell.lblTitle.text = self.newsModel.newslist[i].title;
cell.lblSubTitle.text = self.newsModel.newslist[i].source;
return cell;
}];
row.didSelectRowAtIndexPath = ^(UITableView *tableView, NSIndexPath *indexPath) {
NSLog(@"新聞列表點選 - %ld - %ld -", indexPath.section, indexPath.row);
};
[rows addObject:row];
}
}];
sec0.viewForHeader = ^UIView *(UITableView *tableView, NSInteger section) {
NewsListTableHeaderView *header = [tableView headerFooterFromNib:[NewsListTableHeaderView class]];
header.lblTitle.text = @"新聞列表";
return header;
};
sec0.viewForFooter = ^UIView *(UITableView *tableView, NSInteger section) {
NewsListTableFooterView * footer = [tableView headerFooterFromNib:[NewsListTableFooterView class]];
footer.lblDesc.text = @"上面是新聞";
return footer;
};
// 2.Appliances
CBTableViewSectionDisplay *sec1 = [CBTableViewSectionDisplay displayWithHeaderHeight:90.0 autoHeaderHeight:NO footerHeight:CGFLOAT_MIN autoFooterHeight:NO rowsBlock:^(NSMutableArray<CBTableViewRowDisplay *> *rows) {
CBTableViewRowDisplay *row = [CBTableViewRowDisplay displayWithCellHeight:100.0 autoCellHeight:NO cellForRowAtIndexPath:^UITableViewCell *(UITableView *tableView, NSIndexPath *indexPath) {
AppliancesTableViewCell *cell = [tableView cellWithClass:[AppliancesTableViewCell class] fileType:FileTypeNib];
AppliancesModel *md = self.appliancesModel;
cell.lblName.text = md.name;
cell.lblColor.text = md.color;
cell.lblPrice.text = [NSString stringWithFormat:@"%.2f",md.price];
return cell;
}];
[rows addObject:row];
}];
sec1.viewForHeader = ^UIView *(UITableView *tableView, NSInteger section) {
AppliancesTableHeaderView *header = [tableView headerFooterFromNib:[AppliancesTableHeaderView class]];
header.lblName.text = @"這裡的電器";
return header;
};
// 3. Animal & Person
CBTableViewSectionDisplay *sec2 = [CBTableViewSectionDisplay displayWithHeaderHeight:50.0 autoHeaderHeight:NO footerHeight:CGFLOAT_MIN autoFooterHeight:NO rowsBlock:^(NSMutableArray<CBTableViewRowDisplay *> *rows) {
CBTableViewRowDisplay *row0 = [CBTableViewRowDisplay displayWithCellHeight:self.tableView.bounds.size.width / 16.0 * 9.0 autoCellHeight:NO cellForRowAtIndexPath:^UITableViewCell *(UITableView *tableView, NSIndexPath *indexPath) {
AnimalTCell *cell = [tableView cellWithClass:[AnimalTCell class] fileType:FileTypeNib];
return cell;
}];
CBTableViewRowDisplay *row1 = [CBTableViewRowDisplay displayWithCellHeight:80.0 autoCellHeight:NO cellForRowAtIndexPath:^UITableViewCell *(UITableView *tableView, NSIndexPath *indexPath) {
PersonTCell * cell = [tableView cellWithClass:[PersonTCell class] fileType:FileTypeClass];
cell.lblName.text = @"張三";
return cell;
}];
[rows addObject:row0];
[rows addObject:row1];
}];
[sections addObject:sec0];
[sections addObject:sec1];
[sections addObject:sec2];
}];
_tvSecret = [CBTableViewSecret secretWithTableView:self.tableView display:display];
_tvSecret.didSelectRowAtIndexPath = ^(UITableView *tableView, NSIndexPath *indexPath) {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
};
}
如上述,通過display的block回撥,我們可以對tableView的具體實現控制。
需要注意的是,程式碼裡的“_tvSecret”的生命週期需要和tableView的生命週期相同。這裡建立的全域性變數,防止tableView重新整理時它的dataSource和delegate已經不存在,從而導致頁面異常,具體的詳情不做更多解讀,歡迎去GitHub獲取程式碼一探究竟,OC版本: https://github.com/cba023/CBTableViewSecret , Swift版本: https://github.com/cba023/CBTableViewSecretSwifty 。
總結
雖然這是一個良好的開始,但是我仍然意識到專案中還有很多的不足,比如針對UITableViewDelegate和UITableViewDataSource的方法呼叫還不夠徹底,這個估計後期我會慢慢優化,期待CBTableViewSecret能夠更加完善;同時程式碼中可能還有值得改善的地方,希望正在閱讀此文的你能提出寶貴意見。
其實程式碼總體上很簡單。藝術源於生活,而封裝源於工作。不積跬步,無以至千里,祝願彼此都能進步!
相關文章
- 再說swift namespaceSwiftnamespace
- iOS 開發--Objective-C 反射機制iOSObject反射
- iOS 藍芽開發 - swift版iOS藍芽Swift
- 黑科技開戶神器黑科技開戶神器黑科技開戶神器黑科技開戶神器黑科技開戶神器
- Objective-C iOS 開發 建立 合成 一張LivePhotoObjectiOS
- iOS 開發選擇OC還是Swift?iOSSwift
- Swift與Cocoa框架開發Swift框架
- Objective-C和Swift混編ObjectSwift
- 共享單車,不想說再見
- ios應用開發+swift語言入門iOSSwift
- 史丹佛iOS Swift開發公開課總結(一)iOSSwift
- iOS開發中使用OC和swift的對比iOSSwift
- iOS冰與火之歌 – Objective-C Pwn and iOS arm64 ROPiOSObject
- openssh版本更新與說明 openssl版本更新與說明
- OA管理軟體新體驗:與舊時代說再見
- 用 Swift 模仿 Vue + Vuex 進行 iOS 開發(二):CoordinatorSwiftVueiOS
- Fastjson到了說再見的時候了ASTJSON
- 十年後,E胖與《以撒的結合》說再見
- 虛幻5降臨!再談談它的“黑科技”
- Swift iOS:KVOSwiftiOS
- Swift iOS : RichTextSwiftiOS
- Swift iOS : ArchiveSwiftiOSHive
- 對亂糟糟的日誌說再見
- 來一次有側重點的區分Swift與Objective-CSwiftObject
- iOS開發中常見定位座標轉換iOS
- [譯]iOS開發者在Swift中應避免過度使用@objciOSSwiftOBJ
- 用AI說再見!「辣眼睛」的買家秀AI
- 用AI說再見!“辣眼睛”的買家秀AI
- SpringBoot Jar包瘦身 - 跟大檔案說再見!Spring BootJAR
- Swift iOS : 解析jsonSwiftiOSJSON
- 說說iOS中的常用的關鍵字static ,class(僅限Swift關鍵字)iOSSwift
- 10個讓你相見恨晚的iOS Swift動畫框架!iOSSwift動畫框架
- Swift 開發視訊 iOS 開發視訊教程完整版下載 (共四季)SwiftiOS
- Swift 呼叫 Objective-C 的可變引數函式SwiftObject函式
- 特斯拉自研AI晶片,要和英偉達說再見?AI晶片
- 是時候優雅的和NullPointException說再見了NullException
- 無密碼身份認證,跟密碼說再見!密碼
- 足球黑科技之AI與足球智慧分析AI