項目中我們經常會用到TableView展示一個Cell,Cell的數據來源於我們自定義的一個Model類,那么對於TableView我們有以下幾種場景。
1. 添加操作: 在該列表頁面頂部有一個按鈕叫做新建,點擊后進入一個新的添加頁面,添加完成之后,返回到列表頁更新數據。
2. 更新操作:點擊列表中cell進入編輯頁面,編輯頁面其實就是這個Model類中屬性的一個展示,對其中某些屬性進行更改后,返回到列表頁更新數據。
3. 刪除操作:點擊列表中cell進入編輯頁面,該頁面有個刪除按鈕,點擊刪除按鈕,操作成功后,返回列表頁面更新數據。
對於以上場景,一般比較笨的方法是回到列表頁面后重新從服務器加載一遍數據,但太耗費資源 pass。
另一種辦法就是對數據源(數組 datasource)進行更新:
- 添加操作執行后,在數據源 datasource中insertObject:obj 然后reload列表。
- 更新操作后,在數據源datasource找到該item進行更新然后reload列表。
- 刪除操作后,在數據源datasource找到該item進行刪除操作然后reload列表。
要達到這種效果,我們需要將數據源datasource傳入下一個頁面或者通過消息通知機制通知datasource(當操作執行時),如果項目中該功能列表非常多的時候,每次都進行重復的操作着實有些麻煩,所以在這里,我封裝了一個tableview的category用於解決以上的問題,工作原理如下:
1. 給tableview增加注冊監聽消息(添加,更新,刪除,刷新)

- (void)addDataChangedObserver:(NSMutableArray *)datasource primaryKey:(NSString *)primaryKey changeBlock:(DataChangeBlock)changeBlock { //給類別關聯數據源屬性,存儲tableview數據源 objc_setAssociatedObject(self, (__bridge const void *)(kDatasource), datasource, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //記錄數據源中model的唯一標識 objc_setAssociatedObject(self, (__bridge const void *)(kPrimaryKey), primaryKey, OBJC_ASSOCIATION_COPY_NONATOMIC); //記錄回調的block objc_setAssociatedObject(self, (__bridge const void *)(kChangeBlock), changeBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); //添加監聽方法 [self removeObserver]; [self addObserver]; }

#pragma mark - Observer Operation - (void)addObserver { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(add:) name:TableViewOperationAddNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(delete:) name:TableViewOperationDeleteNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(update:) name:TableViewOperationUpdateNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refresh:) name:TableViewOperationRefreshNotification object:nil]; }
2. 收到消息通知后調用的方法

#pragma mark - Actions - (void)add:(NSNotification *)notification { NSLog(@"調用了tableview的add消息事件"); id obj = notification.object; //獲取數據源數據進行對象添加 NSMutableArray *datasource = [self getPropertyKey:kDatasource]; if (datasource && obj) { if (datasource.count > 0) { if ([obj isKindOfClass:[datasource[0] class]]) { [datasource insertObject:obj atIndex:0]; [self reloadData]; } } } [self callback:TableViewOperationAdd obj:obj]; } - (void)delete:(NSNotification *)notification { NSLog(@"調用了tableview的delete消息事件"); id objNotify = notification.object; //從數據源中刪除一個對象並刷新tableview [self changeDataSourceWithObj:objNotify operationType:TableViewOperationDelete]; [self callback:TableViewOperationDelete obj:objNotify]; } - (void)update:(NSNotification *)notification { NSLog(@"調用了tableview的update消息事件"); id objNotify = notification.object; //從數據源更新一個對象並刷新tableview [self changeDataSourceWithObj:objNotify operationType:TableViewOperationUpdate]; [self callback:TableViewOperationUpdate obj:objNotify]; } - (void)refresh:(NSNotification *)notification { NSLog(@"調用了tableview的refresh消息事件"); id obj = notification.object; //刷新tableview [self reloadData]; [self callback:TableViewOperationRefresh obj:obj]; } - (void)callback:(TableViewOperationType)operationType obj:(id)obj { DataChangeBlock block = objc_getAssociatedObject(self, (__bridge const void*)kChangeBlock); NSIndexPath *indexPath = objc_getAssociatedObject(self, (__bridge const void*)kIndexPath); if (block) { block(operationType, indexPath, obj); } }

- (void)changeDataSourceWithObj:(id)objNotify operationType:(TableViewOperationType)operationType { //取出數據源 NSMutableArray *datasource = [self getPropertyKey:kDatasource]; //取出對象主鍵字段名 NSString *primaryKey = [self getPropertyKey:kPrimaryKey]; //取出對象主鍵字段對應的value值 NSString *valueNotify = [self getObjPropertyValueByKey:primaryKey obj:objNotify]; if (objNotify) { [datasource enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSString *value = [self getObjPropertyValueByKey:primaryKey obj:obj]; if ([valueNotify isEqualToString:value]) { if (operationType == TableViewOperationDelete) { [datasource removeObject:objNotify]; NSLog(@"對象刪除成功,刷新數據"); } else if (operationType == TableViewOperationUpdate) { [datasource replaceObjectAtIndex:idx withObject:objNotify]; NSLog(@"對象更新成功,刷新數據"); } [self reloadData]; *stop = YES; } }]; } }
主要用到了runtime的相關知識,動態的為tableview關聯了屬性,當程序運行到執行相應的操作時,根據關聯的屬性來操作數據源,達到效果。
如何使用??
1. 非常簡單,在需要的viewcontroller中添加如下方法:

[self.tableview addDataChangedObserver:self.datasource primaryKey:@"pid" changeBlock:^(TableViewOperationType operationType, NSIndexPath *indexPath, id obj) { NSLog(@"%@", indexPath); NSLog(@"%ld", operationType); NSLog(@"%@", obj); }];
說明:self.datasource一個初始化了數組,存放數據源;@“pid”數據源中model對象中的標識屬性,此處是Person類的中pid屬性;changBlock操作時回調的block, operationType操作類型(增、刪、改、刷新),indexPath操作的行, obj操作的類,此處是person對象
2. 在需要執行操作的地方發出通知消息

- (IBAction)delete:(id)sender { [[NSNotificationCenter defaultCenter] postNotificationName:TableViewOperationDeleteNotification object:self.p]; } - (IBAction)update:(id)sender { self.p.name = [NSString stringWithFormat:@"change_%@", self.p.name]; [[NSNotificationCenter defaultCenter] postNotificationName:TableViewOperationUpdateNotification object:self.p]; }
大功告成,用起來是不是非常方便。
具體的源碼和demo請訪問
https://github.com/appleboyaug/UITableView-DataChanged