TableView的動態更新操作(無需重新加載數據源)


項目中我們經常會用到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];
    
}
View Code
#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];
}
View Code

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);
    }
}
View Code
- (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;
            }
        }];
    }
}
View Code

主要用到了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);
    }];
View Code

說明: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];
}
View Code

大功告成,用起來是不是非常方便。

具體的源碼和demo請訪問   

https://github.com/appleboyaug/UITableView-DataChanged

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM