像這種界面,佈局會比較複雜,每一section的頭尾顯示及點擊事件都是動態的,並且裏面的內容是幾個row也不肯定,這個界面邏輯整理後是:數組
NoDataCell-相關商城-相關醫生-相關醫院-相關問答-相關資訊-微脈好物-熱門醫生-熱門醫院-熱門問答-熱門資訊
複製代碼
而後就是設置11個section,每一個section裏有三個cell,通用的titleSectionCell和moreSectionCell,還有每一個section對應的本身的內容cell,每一個cell負責本身的UI顯示及點擊事件。bash
整理後 邏輯思路是清晰了,可是怎麼寫代碼呢? 若是按照思路說的這樣每一個section都得判斷去寫對應的三個cell還包括個字賦值及點擊事件,想想cellForRow裏代碼,額。。得寫多大一坨啊?markdown
是否是能夠更有效更優雅的寫這段代碼呢? 想一下這段裏的共性,通用的頭尾好處理,內容cell的個數由各自的數組控制 cell的選擇和index有關,並且整個tableView佈局是固定的.數據結構
因此咱們能夠這樣:函數
//排版方式:相關商品>醫生>醫院>問答>疾病標籤>資訊>熱門商品>醫生>醫院>問答>資訊
NSDictionary *mallSectionDic = @{kCellTitle:@"相關商品", kCellHasMore:@(self.mallHasMore), kCellHasResult:@(self.mallHasResult), kCellClass:@"WMSearchResultMallCell", kCellTitleMoreSelector:@"relativeMallMoreClick", kCellDataArray:self.resultMallArrray, kCellMoreText:@"查看更多商品"};
NSDictionary *doctorSectionDic = @{kCellTitle:@"相關醫生", kCellHasMore:@(self.doctorHasMore), kCellHasResult:@(self.doctorHasResult), kCellClass:@"WMSearchResultDoctorCell", kCellTitleMoreSelector:@"relativeDoctorMoreClick", kCellDataArray:self.resultDoctorArrray, kCellMoreText:@"查看更多醫生"};
NSDictionary *hospitalSectionDic = @{kCellTitle:@"相關醫院", kCellHasMore:@(self.hospitalHasMore), kCellHasResult:@(self.hospitalHasResult), kCellClass:@"WMSearchResultHospitalCell", kCellTitleMoreSelector:@"relativeHospitalMoreClick", kCellDataArray:self.resultHospitalArrray, kCellMoreText:@"查看更多醫院"};
NSDictionary *qaSectionDic = @{kCellTitle:@"相關問答", kCellHasMore:@(self.QAHasMore), kCellHasResult:@(self.QAHasResult), kCellClass:@"WMSearchResultQACell", kCellTitleMoreSelector:@"relativeQAMoreClick", kCellDataArray:self.resultQAArrray,kCellMoreText:@"查看更多問答"};
// NSDictionary *sicknessSectionDic = @{kCellTitle:@"疾病", kCellHasMore:@(self.sicknessHasMore), kCellHasResult:@(self.sicknessHasResult), kCellClass:@"WMSearchResultQACell", kCellTitleMoreSelector:@"relativeSicknessMoreClick", kCellDataArray:self.resultSicknessArray,kCellMoreText:@"查看更多疾病"};
NSDictionary *newsSectionDic = @{kCellTitle:@"相關資訊", kCellHasMore:@(self.newsHasMore), kCellHasResult:@(self.newsHasResult), kCellClass:@"WMSearchResultNewsCell", kCellTitleMoreSelector:@"relativeNewsMoreClick", kCellDataArray:self.resultNewsArrray, kCellMoreText:@"查看更多資訊"};
NSDictionary *recommentMallSectionDic = @{kCellTitle:@"熱門商品", kCellHasMore:@(self.mallHasMore), kCellHasResult:@(self.mallHasResult), kCellClass:@"WMSearchResultMallCell", kCellTitleMoreSelector:@"relativeMallMoreClick", kCellDataArray:self.resultMallArrray, kCellMoreText:@"查看更多商品"};
NSDictionary *recommentDoctorSectionDic = @{kCellTitle:@"周邊熱門醫生", kCellHasMore:@(self.doctorHasMore), kCellHasResult:@(self.doctorHasResult), kCellClass:@"WMSearchResultDoctorCell", kCellTitleMoreSelector:@"recomendDoctorMoreClick", kCellDataArray:self.resultDoctorArrray, kCellMoreText:@"查看更多醫生"};
NSDictionary *recommentHospitalSectionDic = @{kCellTitle:@"周邊熱門醫院", kCellHasMore:@(self.hospitalHasMore), kCellHasResult:@(self.hospitalHasResult), kCellClass:@"WMSearchResultHospitalCell", kCellTitleMoreSelector:@"recomendHospitalMoreClick", kCellDataArray:self.resultHospitalArrray, kCellMoreText:@"查看更多醫院"};
NSDictionary *recommentQaSectionDic = @{kCellTitle:@"熱門問答", kCellHasMore:@(self.QAHasMore), kCellHasResult:@(self.QAHasResult), kCellClass:@"WMSearchResultQACell", kCellTitleMoreSelector:@"recomendQAMoreClick", kCellDataArray:self.resultQAArrray, kCellMoreText:@"查看更多問答"};
NSDictionary *recommentNewsSectionDic = @{kCellTitle:@"熱門資訊", kCellHasMore:@(self.newsHasMore), kCellHasResult:@(self.newsHasResult), kCellClass:@"WMSearchResultNewsCell", kCellTitleMoreSelector:@"recomendNewsMoreClick", kCellDataArray:self.resultNewsArrray, kCellMoreText:@"查看更多資訊"};
self.impSearchResultDataArray = @[mallSectionDic, doctorSectionDic, hospitalSectionDic, qaSectionDic, newsSectionDic, recommentMallSectionDic, recommentDoctorSectionDic, recommentHospitalSectionDic, recommentQaSectionDic, recommentNewsSectionDic];
複製代碼
用一個數組控制,裏面對應各自section的字典,字典裏面各自負責本身的titleSection和moreSection的顯示及點擊事件/結果bool/各自的自定義cell/各自的數據源。佈局
由於字典裏的數據源NSMutableArray是淺拷貝 由指針指着地址,因此後面在數據請求後能正常一直使用。優化
字典裏的cell,經過字符串轉class的方法,獲取到對應的cell,這些cell都做統一的數組賦值 再在各自的賦值方法裏用各自的model取值,用共性來解耦。ui
字典裏的點擊事件,用imp來實現。經過Runtime的消息傳遞機制,直接執行imp指向的函數實現,這樣省去了Runtime消息傳遞過程當中所作的一系列查找操做,會比直接向對象發送消息還要高效一些。spa
代碼以下:代理
/** 搜索結果 -- cellForRow **/
- (UITableViewCell *)searchResultTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// 11 section --- nodata,熱門商城,醫生,醫院,問答,資訊,相關附近商城,醫生,附近醫院,問答,資訊
if (indexPath.section == 0) {
// noData - Cell
kWeakSelf
WMSearchResultNoDataCell *cell = [tableView dequeueReusableCellWithIdentifier:[WMSearchResultNoDataCell reuseIdentifier] forIndexPath:indexPath];
[cell configSearchText:self.searchKeyWords searchNoDataType:SearchNoDataTypeTabSummary];
cell.moreBtnClickBlock = ^{
[weakSelf skipToAskViewController];
};
return cell;
}else {
NSDictionary *impDictionary = self.impSearchResultDataArray[indexPath.section-1];
if (indexPath.row == 0) {
/*** sectionTitle - cell ***/
WMSearchResultSectionTitleCell *cell = [tableView dequeueReusableCellWithIdentifier:[WMSearchResultSectionTitleCell reuseIdentifier] forIndexPath:indexPath];
[cell configSectionTitle:impDictionary[kCellTitle] hasMore:impDictionary[kCellHasMore]];
// imp方法轉換
[cell performSelector:@selector(setSectionTitleMoreBtnClickedBlock:) withObject:^(NSIndexPath *indexPath) {
SEL selector = NSSelectorFromString(impDictionary[kCellTitleMoreSelector]);
if ([self respondsToSelector:selector])]) {
IMP imp = [self methodForSelector:selector])];
void (*func)(id, SEL) = (void *)imp;
func(self, selector));
}
}];
return cell;
}else {
NSArray *dataArray = impDictionary[kCellDataArray];
BOOL hasResult = impDictionary[kCellHasResult];
BOOL hasMore = impDictionary[kCellHasMore];
NSString *moreText = impDictionary[kCellMoreText];
if (hasMore && (indexPath.row == (dataArray.count+1))) {
/*** sectionMore - cell ***/
WMSearchResultSectionMoreCell *cell = [tableView dequeueReusableCellWithIdentifier:[WMSearchResultSectionMoreCell reuseIdentifier] forIndexPath:indexPath];
[cell configMoreText:moreText];
return cell;
}else {
/*** 各自內容cell *****/
// 獲取cell類名的重用標識符
NSString *cellIndentifer = impDictionary[kCellClass];
// 經過重用標識字符串建立類
Class cellClass = NSClassFromString(cellIndentifer);
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIndentifer];
if (!cell) {
cell = [[cellClass alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIndentifer];
}
if (dataArray.count > (indexPath.row-1)) {
if ([cell respondsToSelector:@selector(configDataArray:indexItem:)]) {
// 執行賦值方法
[cell performSelector:@selector(configDataArray:indexItem:) withObject:dataArray withObject:[NSNumber numberWithInteger:(indexPath.row-1)]];
}
}
return cell;
}
}
}
}
複製代碼
performSelector是在iOS中的一種方法調用方式,是運行時系統負責去找方法的,在編譯時候不作任何校驗。
他能夠向一個對象傳遞任何消息,而不須要在編譯的時候聲明這些方法。因此這也是runtime的一種應用方式。
因此performSelector和直接調用方法的區別就在與runtime。直接調用編譯是會自動校驗。若是方法不存在,那麼直接調用在編譯時候就可以發現,編譯器會直接報錯。
可是使用performSelector的話必定是在運行時候才能發現,若是此方法不存在就會崩潰。因此若是使用performSelector時,爲了程序的健壯性,會使用檢查方法respondsToSelector。
經過Runtime的消息傳遞機制,直接執行imp指向的函數實現:
SEL selector = NSSelectorFromString(impDictionary[kCellTitleMoreSelector]);
IMP imp = [self methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
func(self, selector);
複製代碼
SEL : 類成員方法的指針,其實只是方法編號。
IMP:一個函數指針,保存了方法的地址。IMP是」implementation」的縮寫,它是objetive-C 方法(method)實現代碼塊的地址。
IMP和SEL關係:
每個繼承於NSObject的類都能自動得到runtime的支持。
在這樣的一個類中,有一個isa指針,指向該類定義的數據結構體,這個結構體是由編譯器編譯時爲類(需繼承於NSObject)建立的。
在這個結構體中有包括了指向其父類類定義的指針以及 Dispatch table。
Dispatch table是一張SEL和IMP的對應表。
也就是說方法編號SEL最後仍是要經過Dispatch table表尋找到對應的IMP,IMP就是一個函數指針,而後執行這個方法。
實現步驟:
SEL methodId=@selector(methodName);或者SEL methodId = NSSelectorFromString(methodName);
複製代碼
IMP imp = [self methodForSelector:methodId];
複製代碼
void (*func)(id, SEL, id) = (void *)imp;
func(self, methodName,param);
複製代碼
而後直接在類裏寫methodName對應的點擊事件方法就能夠了,不用在cellForRow裏經過代理或block寫事件或在didSeclectRow裏再根據index來判斷了。
到這cellForRow裏面的內容就差很少寫完了,再說說tableViewCell高度計算。
簡單說說tableView自定義Cell的寫法,我的不建議用Xib,建議手代碼用Masonry佈局。
masonry主要用三種寫法,make/remake/update,根據具體樣式的變化程度決定用哪一個,通常make就足夠了。
代碼規範: 導入頭文件、命名、UILoad、DataLoad、PrivateAction、Delegate、LazyLoad。。