像这种界面,布局会比较复杂,每一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。。