Section、row的个数不定的UITableView实现方式最佳实践

先解释下题目,比如下面这个设置页面,里面的内容会根据用户身份、权限等等看到不同的可设置内容:

  • 比如左边有3个section;右边有4个
  • 左边section[0]中有4个row;右边section[0]中有2个row
  • 左边section[0] row[0]可点击;右边则不可点击
  • 等等

我们先假设最简单的情况,造成左右两边差异的原因完全就是由于一个变量的值的不同导致,isMyHome,如果为YES,则最终看到左边的效果,如果为NO,则最终看到右边的效果。

问题来了,你会如何实现UITableView的各种delegate?

错误示范,一种很丑的实现方式

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    if (self.isMyHome) {
        return 3;
    } else {
        return 4;
    }
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (self.isMyHome) { // 有3个section
        switch (section) {
            case 0:
                return 4;
                break;
            case 1:
                return 1;
                break;
            case 2:
                return 2;
                break;
            default:
                break;
        }
    } else {
        switch (section) {
            case 0:
                return 2;
                break;
            case 1:
                return 1;
                break;
            case 2:
                return 1;
                break;
            case 3:
                return 1;
                break;
            default:
                break;
        }
    }
    return 0;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (self.isMyHome) {
        if (indexPath.section == 2 && indexPath.row == 0) { // 有一个row稍微有点高
            return 70;
        } else {
            return 40;
        }
    } else {
        return 40;
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = ni;
    // ...真的不想写了快吐了,这才一个变量的区别
    return cell;
}

以上的写法有两个大问题:

  1. 任何时候都要考虑isMyHome的值,写一堆if、else、switch。(一个row是否要显示,row的index是多少,row长什么样都是变量,都要考虑)
  2. 使用0,1,2,3等数字,没有含义,没有使用枚举,难以维护,增删一行都要改全部,还容易出bug。

这才一个变量啊,试想实际项目中,往往很多变量综合决定了哪个row显示、那个row不显示、row长什么样。想想下,如果有一天产品经理说,去掉“宝宝信息”这行,我觉得你就要疯了,要把index全部都减一,每个代理都要检查一遍是否对得上index。

所以,这两个问题,导致的后果:难实现、容易出bug、不易维护。

最佳实现

既然上面的实现这么不好,该怎么实现呢?其实就是逐一解决上面提到的两个问题。

对于1,其实造成问题的本质原因是:在任何时候,把一个row应该显示与否、以及长什么样子、row对应的index是什么, 这些问题都融合在一起了,将问题复杂程度一下提升了很多。对于2,都使用枚举就好了,避免
这里提出解决办法:

  1. 固定section的个数、每个section中row的个数,都取所有条件下,最多的那个值。(numberOfSectionsInTableView、numberOfRowsInSection只控制个数问题)
  2. heightForRowAtIndexPath中控制row的显示与否,如果不显示,只需要高度返回0就好了
  3. cellForRowAtIndexPath里,由于section、row的index都是固定不变的,只需要关心cell的内容就好了。
  4. 使用枚举

这样,“section、row的个数(index)”、“section、row是否显示”、“row长什么样”,这三个问题就分散到了4个delegate中:

  • numberOfSectionsInTableView、numberOfRowsInSection只解决“section、row的个数(index)”的问题;
  • heightForRowAtIndexPath只解决“section、row是否显示”的问题
  • cellForRowAtIndexPath只解决“row长什么样”的问题

代码长这样:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 4;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    switch (section) {
        case SectionOne:
            return 4;
            break;
        case SectionTwo:
            return 1;
            break;
        case SectionThree:
            return 2;
            break;
        case SectionFour:
            return 1;
            break;
            
        default:
            break;
    }
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0) {
        switch (indexPath.row) {
            case SectionOneRowOne:
                return 40
                break;
            case SectionOneRowTwo:
                return self.isMyHome ? 40 : 0;
                break;
            case SectionOneRowThree:
                return self.isMyHome ? 40 : 0;
                break;
            case SectionOneRowFour:
                return 40
                break;
                
            default:
                break;
        }
    } else if (indexPath.section == 1) {
        return 40;
    } else if (indexPath.section == 2) {
        switch (indexPath.row) {
            case SectionThreeRowOne:
                return self.isMyHome ? 70 : 0;
                break;
            case SectionThreeRowTwo:
                return 40;
                break;
            default:
                break;
        }
    } else {
        return self.isMyHome ? 0 : 40;
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // 对于每个cell,配置内容就好了,index都是固定的
}

如果产品经理说,删除“宝宝信息”这行吧,那么我们只要把heightForRowAtIndexPath,相应高度改为0就好了,之后产品经理反悔还可以直接改回来,就算想完全删了,也很省事,不容易出bug。

总结

核心思想,其实就是,UITableView的几个delegate,分别只解决一个问题,不要把问题都揉在一起,让代码变得复杂

其他问题

假如要去掉一整个section怎么办,比如产品经理说,不要“我在小家的称呼”这行了。

当然,没问题,我们直接把heightForRowAtIndexPath里这行,改为0就好了嘛!

等等,虽然这行没了,但是前两个section之间的间隙,有可能会变大,因为只是设置了height为0而已,seciton还在嘛。这种情况怎么办?
不用担心,只要设置section的间隙,使用类似这种方式去设置,响应的改变section header的值,就好了~
其实,这种设置section的间隙的方式,也是我们经常使用的嘛,基本都不太用系统默认的间隙。

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return section == 0 ? 0 : 8;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    if (section == 0) {
        return [UIView new];
    }
    UIView *sectionHeader = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 8)];
    sectionHeader.backgroundColor = [UIColor clearColor];
    return sectionHeader;
}
posted @ 2017-12-02 10:32  张驰小方块  阅读(1457)  评论(0编辑  收藏  举报