IOS多类型Cell的tableView实现

4,695 阅读3分钟

之前阅读了处理 iOS 中复杂的 Table Views 并保持优雅iOS:如何构建具有多种 Cell 类型的表视图两篇译文,对于如何处理多类型cell的tableView有不小的收获。但我发现多类型cell的tableView之间也是有区别的。比如译文中就举例实现了动态多类型cell的tableView,这种情况使用MVVM模式有很好的效果。然而我们开发过程中也会有很多静态的多类型cell需要实现,比如微信的个人信息:

这种风格的tableView内容基本是固定且简单,如果要用MVVM去实现会比较繁琐。或者像我之前我都是直接根据indexPath去一一对应生成,这在开发阶段会很快很容易,但是后期如果要修改,比如增加一列或者删除一列,这时候就需要同时修改下面代理中的代码

- tableView: numberOfRowsInSection:
- tableView: cellForRowAtIndexPath:
- tableView: didSelectRowAtIndexPath:

对于能躺着绝不坐着的个人习惯,我就想是不是可以改进下。

生成model

model的生成大同小异,基本是http获取json后转为model。这里我自己创建了json然后本地获取模拟了下。

{
    "photo":"Audrey",
    "username":"奥黛丽",
    "userCode":"formyicarus222",
    "QRCode":"ico",
    "sex":1
}

model代码:

@interface MultipeTypeModel : NSObject

@property (strong, nonatomic) NSString *photo;
@property (strong, nonatomic) NSString *username;
@property (strong, nonatomic) NSString *userCode;
@property (strong, nonatomic) NSString *qrCode;
@property (assign, nonatomic) NSInteger sex;

+ (void)requestForData:(void(^)(MultipeTypeModel *model))completion;
@end
- (instancetype)initWithDict:(NSDictionary *)dict
{
    self = [super init];
    if (self) {
        self.photo = dict[@"photo"];
        self.username = dict[@"username"];
        self.userCode = dict[@"userCode"];
        self.qrCode = dict[@"QRCode"];
    }
    return self;
}

+ (void)requestForData:(void (^)(MultipeTypeModel * _Nonnull))completion {
    NSString *path = [[NSBundle mainBundle] pathForResource:@"MultipleCell" ofType:@"json"];
    NSString *json= [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    NSDictionary *jsonDict = [json jsonValueDecoded];
    MultipeTypeModel *model = [[MultipeTypeModel alloc] initWithDict:jsonDict];
    completion(model);
}

在vc里调用

- (void)geteData {
    [MultipeTypeModel requestForData:^(MultipeTypeModel * _Nonnull model) {
        self.muModel = model;
    }];
}

model生成数组

之前我说使用indexPath一一指定的方式去实现有个问题,就是后期需要增加或者删除cell需要修改多处代码。而解决这个问题的办法就是将tableView的数据统一和一个数组关联,那么之后有需求变动,我只需要修改这个数组就可以了。

model转数组代码:

- (NSArray *)tableArrayFromModel:(MultipeTypeModel *)model {
    NSArray *arr = @[
                     @[
                         @{
                             @"title":@"头像",
                             @"image":model.photo?:@"",
                             @"type":@(MUTableCellTypeHeader),
                             @"selector":NSStringFromSelector(@selector(doHeaderPhotoAction:))
                             },
                         
                         @{
                             @"title":@"用户名",
                             @"content":model.username?:@"",
                             @"type":@(MUTableCellTypeName),
                             @"selector":NSStringFromSelector(@selector(doUsernameAction:))
                             },
//                         
//                         @{
//                             @"title":@"性别",
//                             @"content":model.sex==1?@"男":@"女",
//                             @"type":@2
//                             },
                         
                         @{
                             @"title":@"微信号",
                             @"content":model.userCode?:@"",
                             @"type":@(MUTableCellTypeNameNoAccessory)
                             },
                         
                         @{
                             @"title":@"二维码",
                             @"image":model.qrCode?:@"",
                             @"type":@(MUTableCellTypeHeader),
                             @"selector":NSStringFromSelector(@selector(doQRCodeAction:))
                             },
                         
                         @{
                             @"title":@"更多",
                             @"content":@"",
                             @"type":@(MUTableCellTypeName),
                             @"selector":NSStringFromSelector(@selector(doMoreAction:))
                             }
                         ],
                     
                     @[
                         @{
                             @"title":@"我的地址",
                             @"content":@"",
                             @"type":@(MUTableCellTypeName),
                             @"selector":NSStringFromSelector(@selector(doAddressAction:))
                             }
                         ]
                     
                     ];
    
    return arr;
}

这是一个二维数组,第一维表示section,第二维表示row。selector表示点击cell的事件,type表示cell的类型。cell的类型我是创建一个枚举来指定的:

typedef NS_ENUM(NSInteger,MUTableCellType) {
    MUTableCellTypeHeader=1,
    MUTableCellTypeName,
    MUTableCellTypeNameNoAccessory
};

一般来说这种tableView的数据是可以修改的。我按如下思路修改:
1.直接修改self.muModel实例的属性值。
2.重新将model转为数组
3.reloadData

- (void)reloadTableWithModel:(MultipeTypeModel *)model atIndexPath:(NSIndexPath *)indexPath {

    self.tableArray = [self tableArrayFromModel:model];
    if (indexPath) {
        [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    }
    else {
        [self.tableView reloadData];
    }
}

tableView根据数组展示

到这里,tableView相关数据就处理完,接下来就是将数据填充进tableView:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSArray *arr = self.tableArray[section];
    return arr.count;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.tableArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSArray *sectionArr = self.tableArray[indexPath.section];
    NSDictionary *dict = sectionArr[indexPath.row];
    NSInteger type = [dict[@"type"] integerValue];
    switch (type) {
        case MUTableCellTypeHeader:
            {
                MUPhotoCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MUPhotoCell" forIndexPath:indexPath];
                cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
                [cell bindDict:dict];
                return cell;
            }
            break;
        case MUTableCellTypeName:
        {
            MUNameCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MUNameCell" forIndexPath:indexPath];
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
            [cell bindDict:dict];
            return cell;
        }
            break;
        case MUTableCellTypeNameNoAccessory:
        {
            MUNameCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MUNameCell" forIndexPath:indexPath];
            [cell bindDict:dict];
            return cell;
        }
            break;
            
        default:
            break;
    }
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor redColor];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:true];
    
    NSArray *sectionArr = self.tableArray[indexPath.section];
    NSDictionary *dict = sectionArr[indexPath.row];
    NSString *selector = dict[@"selector"];
    SEL sel = NSSelectorFromString(selector);
    if ([self respondsToSelector:sel]) {
        [self performSelector:sel withObject:indexPath];
    }
}

这样,当我后续接到需求变动,比如上面要添加一个cell,我只需要在- (NSArray *)tableArrayFromModel:(MultipeTypeModel *)model方法添加一个元素就可以了。

@{
@"title":@"性别",
@"content":model.sex==1?@"男":@"女",
@"type":@(MUTableCellTypeName)
}