变更记录
方案名称 KVO - 使用 KVO 更新 UITableViewCell 显示
关键字 KVO \ UITableViewCell \ 观察属性
需求场景
通过 KVO 实现 UITableViewCell 自更新,避免重复 Reload Cell
参考链接
Stack Overflow - adding KVO to UITableViewCell
Nachbaur - Back to Basics: Using KVO
详细内容 1. 在 ObjectiveModel 数据模型中定义观察子属性 KeyPath , 示例表示要观察 statusDescription 子属性 1 2 3 4 5 6 7 //ObjectiveModel.h 中 #define KEY_PATH_FOR_STATUS_DESCRIPTION @"statusDescription" ... @property (nonatomic, strong) NSString *statusDescription; ...
2. 定义 UITableViewCell 的子类 HKTimelineCell , 并实现监听方法 *- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void )context 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 //HKTimelineCell.h 中 ... //需要注册的属性 @property (strong, nonatomic) ObjectiveModel *request; ... //注册和移除观察接口 - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context; - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void *)context; ... //HKTimelineCell.m 中 @interface NetworkingTestRequestCell() // 使用 ObservableKeys 保存 keyPath 观察状态,避免重复注册和重复移除(重复移除会导致 crash) @property (nonatomic, strong) NSMutableSet *ObservableKeys; @end ... // 为 HKTimelineCell 的 request 属性注册观察(观察 request 中 KeyPath 为 KEY_PATH_FOR_STATUS_DESCRIPTION 的子属性), 此处的 request 为 ObjectiveModel 类型对象注册观察 - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context { if ([_ObservableKeys containsObject:keyPath]) { return; } if (!_ObservableKeys) { _ObservableKeys = [NSMutableSet set]; } [_ObservableKeys addObject:keyPath]; [self.request addObserver:observer forKeyPath:keyPath options:options context:context]; } //移除对 request 中 KeyPath 为 KEY_PATH_FOR_STATUS_DESCRIPTION 的子属性的观察 - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context { if (![_ObservableKeys containsObject:keyPath]) { return; } [self.request removeObserver:observer forKeyPath:keyPath context:context]; [_ObservableKeys removeObject:keyPath]; } //观察 HKTimelineCell 的 request 属性中子属性(KeyPath 为 KEY_PATH_FOR_STATUS_DESCRIPTION)值的变化并更新 labelStatus 显示,此处的 request 为 ObjectiveModel 类型对象 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([KEY_PATH_FOR_STATUS_DESCRIPTION isEqualToString:keyPath]) { // NSString *oldObject = [change objectForKey:NSKeyValueChangeOldKey]; NSString *newObject = [change objectForKey:NSKeyValueChangeNewKey]; self.labelStatus.text = newObject; } }
3. 在 *- (void)tableView:(UITableView *)tableView willDisplayCell:(HKTimelineCell *)cell forRowAtIndexPath:(NSIndexPath )indexPath 中注册观察 1 2 3 4 5 6 7 8 9 10 11 //使用到 HKTimelineCell 的 UITableview DataSource - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // Don't add observers, or the app may crash later when cells are recycled } - (void)tableView:(UITableView *)tableView willDisplayCell:(HKTimelineCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { [cell addObserver:cell forKeyPath:KEY_PATH_FOR_STATUS_DESCRIPTION options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:(void *)cell]; }
4. 在 *- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(HKTimelineCell *)cell forRowAtIndexPath:(NSIndexPath )indexPath 中移除观察 1 2 3 4 - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(HKTimelineCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { [cell removeObserver:cell forKeyPath:KEY_PATH_FOR_STATUS_DESCRIPTION context:(void *)cell]; }
5. 在 - (void)viewWillDisappear:(BOOL)animated 中移除全部观察,因为页面离开时 didEndDisplayingCell 方法不会被调用 1 2 3 4 5 6 7 - (void)viewWillDisappear:(BOOL)animated { ... [self.tableViewMain.visibleCells enumerateObjectsUsingBlock:^(NetworkingTestRequestCell *cell, NSUInteger idx, BOOL * _Nonnull stop) { [cell removeObserver:cell forKeyPath:KEY_PATH_FOR_STATUS_DESCRIPTION context:(void *)cell]; }]; }
6. 在 - (void)viewWillAppear:(BOOL)animated 中 reload 当前显示的 cells , 注册被 viewWillDisappear 移除的观察,因为 cell 使用了 ObservableKeys 避免了重复注册,所以不用担心重复注册问题 1 2 3 4 5 - (void)viewWillAppear:(BOOL)animated { ... [self.tableViewMain reloadRowsAtIndexPaths:self.tableViewMain.indexPathsForVisibleRows withRowAnimation:UITableViewRowAnimationNone]; }
7. 若要实现正确的 KVO, 在对 KeyPath 子属性赋值时一定要使用 setValue:forKey: 方法 1 [self setValue:[self statusDescription] forKey:KEY_PATH_FOR_STATUS_DESCRIPTION];
效果图 (无)
备注 (无)