一、tableViewStyle的选择
tableview有两种style供我们选择,UITableViewStylePlain(默认)和UITableViewStyleGrouped。
1.grouped的sectionHeader不会悬浮。plain反之。
2.plain会出现多余的分割线,充满整个tableview,grouped不会。
具体选择那一种style,根据需求具体分析。个人比较喜欢UITableViewStylePlain,因为这样就不用再去专门做一个悬浮的sectionHeader,不会悬浮的header作为一个cell显示。
二、tableViewHeader
一般的页面都会有一个比较固定的头部,它们通常比较复杂,不用经常更新,UI数据也不用后台返回。这时候就需要我们的tableViewHeader。
tableViewHeader会随着tableView一起滑动。
tableViewHeader默认为nil,需要重新去赋值一个view;
需要注意的点:
通常我们习惯用masonry或者其他的一些自动化布局,但是tableViewHeader的高度,在layoutsubViews方法执行之前,默认为0。
所以我们需要在layoutsubViews之后,进行masonry布局,不然会报UI警告。(个人洁癖,其实无伤大雅)
另外一点,头部的图片,如果size超过了imageView的大小,记得设置maskToBounds,不然在ipad上面,会出现图片的溢出,遮挡住其他控件。
三、sectionHeader和cell
这两个一起说,其实在我看来,他们本质上是一致的:可以放到缓存池中被重用的view。
而tableView呢,是他们的一种布局方式,是collectionView的一种特殊布局,这个后面再说。
我只聊一下对cell的看法,sectionHeader可以比照着理解。
1、注册cell
比如注册一个LyTableViewCell,重用ID为LyTableViewCellId:
[tableView registerClass:[LyTableViewCell class] forCellReuseIdentifier:@"LyTableViewCellId"]
其实这是一个把LyTableViewCell注册到缓存池的操作,同样的,滑动tableView时释放cell,也会执行这个操作。
所以,你可以找到一个替代注册cell的方式:
LyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"LyTableViewCellId"];
if (!cell) {
cell = [[LyTableViewCell alloc] init];
}
先从缓存池中取id为LyTableViewCellId的cell,如果为空,则初始化该cell。
2、cell的重用
每一个消失在屏幕上面的cell都会被放到缓存池中,等待被重用。
而重用这一操作,对我们原生开发来说,就是对一个cell重新赋值的过程。
比如对于一个cell,tabLabel可以是“半价”,可以是“精选”,也可以是隐藏状态,在我们重用时,就要充分考虑到所有cell的可能情况,对该cell进去相应的操作。
初学者出现的,cell的各种UI小问题,大部分都是这个原因。
重用时执行的方法:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
大部分cell是需要重用的,这也是tableView在数据庞大时仍能保持高性能的原因。但是有些特殊的cell,比如"状态选择类"的,每次重新赋值,会导致状态的丢失,这时候就需要做一些特殊的操作,保证不被复用。
我的方式也很简单,在cellModel中,加一个singleIdentifier的属性,如果检测到,则不进行赋值,直接返回。(我习惯一个cell对应一个cellModel,如果大家没有这种习惯,其他类似方式也可以解决)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
{...}
NSString *identifier = cellModel.singleIdentifier;
BOOL forceRefresh = cellModel.forceRefresh;
if (identifier && identifier.length > 0 && !forceRefresh)
{
return [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
}
{...}
}
这种写法会导致任何情况下都不会赋值,所以需要多加一个forceRefresh属性控制,是否强制刷新。
3、cell的点击
实现两个代理方法就可以了:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(3_0);
设置cell的selected为true之后,下次点击就会执行didDeselectRowAtIndexPath方法。
点击操作就在这里面执行。
如果要设置高亮状态的UI,则在cell的设置方法里面进行操作:
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated;
4、cell的高度
cell的高度,API给了我们两个属性进行设置
rowHeight和estimatedRowHeight,这两个都是默认UITableViewAutomaticDimension,自适应高度,我们可以根据需求去配置相应的属性。
rowHeight:顾名思义,用在已经确定了高度的cell,设置之后就会固定cell的高度。
estimatedRowHeight:估算高度,用得好的话可以提高tableView的性能。
这个值不可以乱设,tableView在刷新页面时,会先将cell默认为estimatedRowHeight的高度去布局,如果和实际高度相差过大,tableView会出现闪动、改变偏移量的现象。
所以,估算高度,和实际高度不能有太大的误差。
API还提供了两个方法,根据cell去设置高度。方法优先级高于统一设置的属性。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
四、代理方法
UITableViewDataSource:数据
传tableView的一些基础数据,如个数,cell数据;
负责cell的移动,删减,增加。
UITableViewDelegate:UI显示
遵循UIScrollViewDelegate;
负责如cell的显示,高度,预估高度,高亮状态,点击状态等等的回调;
为什么单拎出来说呢,因为这样我们可以明显的看到,苹果有意把tableView分成两个部分,数据和UI。
对应我们原生的工作,就是UI搭建和建模。
所以我们也可以把这两块分开来做,不要纠缠在一起。
数据由后台传递,是动态变化的。UI是我们自己搭建,相对格式化的,然后在tableView中对应起来。
这些大概就是小弟的一些浅见。
总有人觉得,tableView这么简单的东西,为什么还要专门去研究呢。但在我看来,见微知著,如果能把这个东西搞透彻,就有了去研究更深一级的collectionView的门槛,collectionView也琢磨透了,UI的展示类布局基本就算是可以出师了。
当然了,学无止境。