IOS多型別Cell的tableView實現

輕羅小扇撲流螢發表於2019-04-09

之前閱讀了處理 iOS 中複雜的 Table Views 並保持優雅iOS:如何構建具有多種 Cell 型別的表檢視兩篇譯文,對於如何處理多型別cell的tableView有不小的收穫。但我發現多型別cell的tableView之間也是有區別的。比如譯文中就舉例實現了動態多型別cell的tableView,這種情況使用MVVM模式有很好的效果。然而我們開發過程中也會有很多靜態的多型別cell需要實現,比如微信的個人資訊:

IOS多型別Cell的tableView實現
這種風格的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)
}

複製程式碼

相關文章