谈谈iOS开发如何写个人中心这类页面--静态tableView页面的编写(中)

西西吹雪2018-05-25 13:09

上篇:谈谈iOS开发如何写个人中心这类页面--静态tableView页面的编写(上)


方案优点分析

对比上一方案,本方案有了下面几点改进:

  • 内容以行为单位封装成 Cell,只需要对 Cell 内部的视图做好布局即可,无需关心 UITableView 中 Cell 的布局,这样就避免了视图过多时布局的麻烦。
  • 当设计师需要移动不同行的顺序或者添加新的内容时,只需要调整 UITableView 的代理方法即可,无需修改布局。
  • UITableView 代理方法可以统一处理 Cell 的点击事件,而且无需手动添加手势。

方案缺点分析

虽然该方案对比 UIScrollView 方案有了不少改进,但是仍然有些问题:

  • tableView:numberOfRowsInSection: 我们需要根据 section 的数值返回不同的 Cell 数量,当 section 较多时,就会有多个 if else 语句,降低了代码的可读性。除此方法外,tableView:cellForRowAtIndexPath: 以及 tableView:heightForRowAtIndexPath: 、tableView:didSelectRowAtIndexPath: 也存在类似的问题。
  • 过多的 if else 导致的最主要问题是降低了代码的灵活性。例如:如果需要调整各个 Cell 的顺序或者在某处添加一个 Cell, 那么几乎第一点中的所有方法都需要修改 if else 逻辑,代码基本没有任何灵活性可言。
  • 在 tableView:cellForRowAtIndexPath: 方法中,我们在获取某个 Cell 时,需要提供一个私有方法来创建该 Cell(例如 getCell2),当然也可以不提供私有方法,直接在 tableView:cellForRowAtIndexPath: 方法中创建,其实没有本质区别。
  • 当前 tableView:heightForRowAtIndexPath: 中返回的高度是根据 indexPath 固定的,不能满足动态高度Cell(Cell 的高度根据其内容动态变化),如果需要满足动态高度Cell的需求,需要引入其他处理方法。
  • 这种方案目前不支持 Cell 的复用,当然,对于这类 Cell 数量不太多的页面,Cell 不复用也可以,所以这个缺点不太重要。

三、本文的方案

针对上面方案的缺点,本文提出了一种新的方案,可以消除上述方案的缺点,下图简单展示了本方案的基本思想:


代码举例

(1) 页面中的每个 Cell 都需要继承自 MCTableBaseCell,然后在子类中添加自定义属性和方法,如 MCDemoCell1 的定义如下:

@interface MCDemoCell1 : MCTableBaseCell

@property (nonatomic, strong) UIImageView *headerIconImageView;
@property (nonatomic, strong) UILabel *contentLabel;

@end

(2) 为该页面定义一个继承自 MCTableBaseDescribeData 的 DescribeData 类,该类中需要添加页面中所有 Cell 所需的数据。

例如,上面的 MCDemoCell1 需要一个 image 和 content 字符串,那么自定义的 DescribeData 中也应该包含有这两个信息。 demo 中的 DescribeData 需要包含有 MCDemoCell1, MCDemoCell2, MCDemoCell3 所需的数据,所以定义如下:

@interface MCDemoTableDescribeData : MCTableBaseDescribeData

@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subTitle;
@property (nonatomic, assign) BOOL switchStatus;
@property (nonatomic, copy) NSString *indicateImageName;
@property (nonatomic, copy) NSString *content;
@property (nonatomic, copy) NSString *headerIconName;
@property (nonatomic, weak) id<MCDemoCell2Delegate> cell2Delegate;

@end

(3) 有了 MCDemoTableDescribeData 的定义,我们需要为 MCDemoCell 的实现重写 setDescribeData: 方法,在该方法中, 我们需要将 describeData 中与 MCDemoCell 有关的数据赋值给 MCDemoCell,代码如下:

- (void)setDescribeData:(MCTableBaseDescribeData *)describeData {
    if ([describeData isKindOfClass:MCDemoTableDescribeData.class]) {
        MCDemoTableDescribeData *data = (MCDemoTableDescribeData *)describeData;
        _headerIconImageView.image = [UIImage imageNamed:data.headerIconName];
        _contentLabel.text = data.content;
    }
}

此外,我们需要为 MCDemoCell 重写 sizeThatFits: 方法,在该方法中,我们可以根据 MCDemoCell 的数据动态计算出 Cell 高度,该方法会在后面获取 Cell 高度时调用。

- (CGSize)sizeThatFits:(CGSize)size {
    CGFloat height;
    // 根据 Cell 的实际内容计算出 Cell 的高度。
    //例如根据 contentLabel 的内容,计算出 Cell 需要展示 content 所需的高度。
    return CGSizeMake(0, height);
}

(4) 下面就可以编写 Controller 了。首先要定义一个属性,用来保存 tableView 所有 Cell 需要的 DescribeData:

@property (nonatomic, strong) NSArray<NSArray<MCDemoTableDescribeData *> *> *cellDescriptionDatas;

(5) 然后创建 tableView 并配置 tableView 的 delegate、dataSource 等属性:

- (void)loadSubviews {
    _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
    _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    _tableView.backgroundColor = [UIColor lightTextColor];
    _tableView.delegate = self;
    _tableView.dataSource = self;
    [self.view addSubview:_tableView];

    // register cell class, use UITableView+MCRegisterCellClass
    [_tableView registerCellClasses:@[[MCDemoCell1 class],
                                      [MCDemoCell2 class],
                                      [MCDemoCell3 class]]];
}

注意: 最后一句代码使用到的方法是本文定义的 UITableView+MCRegisterCellClass 这个 category 提供的,是为了方便注册 Cell 的 Class 和 dequeue 出相应的 Cell,详细请看 demo 中的代码。


本文来自网易实践者社区,经作者白天宇授权发布。    

本文未结束,敬请期待下篇。