我正在尝试找出保持UITableViewController
和UISearchDisplayController
之间行同步的最优雅方式。
在UITableViewController
中选择行时,我希望UISearchDisplayController
为active
时,同一行显示为已选中,反之亦然。
tableView
个对象都allowsMultipleSelection
设置为YES
。
答案 0 :(得分:2)
这项技术的基础来自Erica Sadun的非常有用的书" The Core iOS 6 Developer's Cookbook"第四版,由Addison Wesley出版。我从Erica的书中提出了许多想法和代码的解决方案。
备注
UISearchDisplayController
,而是UISearchController
。self.tableView
和self.searchDisplayController.searchResultsTableView
表格视图所需的大量代码。NSFetchedResultsController
。基本上,我们使用NSMutableDictionary
来维护self.tableView
和self.searchDisplayController.searchResultsTableView
使用的所选单元格的记录。
此技术可用于表视图和控制器,用于注册和跟踪一个或多个选定单元格。
自从我实施此解决方案以来,我可能会错过一些步骤,所以请告诉我,我会检查它。
准备一组公共属性,包括......
@property (nonatomic, retain) NSMutableArray *searchResults;
@property (nonatomic, strong) NSManagedObjectID *managedObjectID;
@property (nonatomic, strong) NSArray *arrayObjects;
@property (nonatomic) BOOL isArray;
@property (nonatomic, strong) void (^blockSelectManagedObjectID)(NSManagedObjectID *objectID);
@property (nonatomic, strong) void (^blockSelectManagedObjects)(NSArray *objects);
使用父TVC中包含的managedObjectID
方法设置属性arrayObjects
和prepareForSegue
。只设置了一个或另一个,具体取决于您是传递一个NSManagedObjectID
(单个选择),还是NSArray
多个NSManagedObject
s(多个选择)。
可以删除属性isArray
,但我将其包含在内,以方便编码和代码可读性。它也在上述父TVC中的相同prepareForSegue
方法中设置。
这些块在父TVC中定义,并在退出此TVC时更新父TVC中的数据。
总结一下,除searchResults
外,这些公共属性由家长TVC设定。
准备一组私人财产,包括......
@property (nonatomic, strong) NSMutableDictionary *dictionaryTableRowCheckedState; //our primary dictionary!
@property (nonatomic, strong) NSMutableArray *arrayObjectsSelected;
@property (nonatomic, strong) NSIndexPath *indexPathSelected;
@property (nonatomic, strong) NSIndexPath *indexPathObjectFromArray;
@property (nonatomic, strong) NSManagedObjectID *cellObjectID;
在TVC生命周期方法viewWillAppear
中设置私有属性。
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self setDictionaryTableRowCheckedState:[NSMutableDictionary dictionary]];
[self setArrayObjectsSelected:[NSMutableArray arrayWithArray:self.arrayObjects]];
[self setIndexPathSelected:nil];
[self setIndexPathObjectFromArray:nil];
[self.tableView reloadData];
//<<_YOUR_OTHER_CODE_>>
}
准备使用TVC数据源方法UITableViewCell
填充cellForRowAtIndexPath
,如下所示...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//<<_YOUR_OTHER_CODE_>>
//<<including...>>
if (tableView == self.tableView) {
rowEntity = [self.fetchedResultsController objectAtIndexPath:indexPath];
} else {
rowEntity = [self.searchResults objectAtIndex:indexPath.row];
}
[self setCellObjectID:[rowEntity objectID]];
//<<_YOUR_OTHER_CODE_>>
[cell setAccessoryType:UITableViewCellAccessoryNone];
NSIndexPath *indexPathLastManagedObject = nil;
// If there exists 'checked' value/s, manage row checked state
if (self.managedObjectID || self.arrayObjects.count) {
BOOL isChecked = NO;
if (!self.isArray) {
if (self.cellObjectID == self.managedObjectID) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
isChecked = YES;
self.indexPathSelected = indexPath;
NSManagedObject *localManagedObject = nil;
if (tableView == self.tableView) {
localManagedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
} else {
localManagedObject = [self.searchResults objectAtIndex:indexPath.row];
}
indexPathLastManagedObject = indexPath;
self.managedObjectID = localManagedObject.objectID;
}
} else if (self.isArray) {
if (self.arrayObjectsSelected.count) {
for (NSManagedObject *localManagedObject in self.arrayObjectsSelected) {
if (self.cellObjectID == localManagedObject.objectID) {
isChecked = YES;
indexPathLastManagedObject = indexPath;
break;
}
}
}
self.dictionaryTableRowCheckedState[indexPath] = @(isChecked);
cell.accessoryType = isChecked ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
} else {
NSLog(@"%@ - %@ - has (possible undefined) E~R~R~O~R attempting to set UITableViewCellAccessory at indexPath: %@_", NSStringFromClass(self.class), NSStringFromSelector(_cmd), indexPath);
}
}
return cell;
}
当然,我们需要一个相当粗略的TVC委托方法didSelectRowAtIndexPath
来处理用户对单元格的选择和取消选择。
请注意,我在代码中强调了块回调的使用,尽管没有详细提及 - 这是我在父TVC中更新数据的首选方法。如果您希望我更新代码以合并块回调,请告诉我(这里已经有很多代码)。
另请注意,当我们处于单选模式时,我会弹出TVC。如果我们处于多小区选择模式,那么返回父TVC显然必须是手动的。
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (!self.isArray) {
if (self.indexPathSelected) {
if ((indexPath.section == self.indexPathSelected.section)
&& (indexPath.row == self.indexPathSelected.row)) {
[cell setAccessoryType:UITableViewCellAccessoryNone];
[self setIndexPathSelected:nil];
} else {
NSIndexPath *oldIndexPath = self.indexPathSelected;
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:oldIndexPath];
[oldCell setAccessoryType:UITableViewCellAccessoryNone];
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
[self setIndexPathSelected:indexPath];
NSManagedObject *localManagedObject = nil;
if (tableView == self.tableView) {
localManagedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
} else {
localManagedObject = [self.searchResults objectAtIndex:indexPath.row];
}
NSManagedObjectID *localObjectID = localManagedObject.objectID;
[self blockSelectManagedObjectID](localObjectID); //block callback to update parent TVC
}
} else {
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
[self setIndexPathSelected:indexPath];
NSManagedObject *localManagedObject = nil;
if (tableView == self.tableView) {
localManagedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
} else {
localManagedObject = [self.searchResults objectAtIndex:indexPath.row];
}
NSManagedObjectID *localObjectID = localManagedObject.objectID;
[self blockSelectManagedObjectID](localObjectID); //block callback to update parent TVC
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[self.navigationController popViewControllerAnimated:YES];
} else if (self.isArray) {
NSManagedObject *localManagedObject = nil;
if (tableView == self.tableView) {
localManagedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
} else {
localManagedObject = [self.searchResults objectAtIndex:indexPath.row];
}
// Toggle the cell checked state
__block BOOL isChecked = !((NSNumber *)self.dictionaryTableRowCheckedState[indexPath]).boolValue;
self.dictionaryTableRowCheckedState[indexPath] = @(isChecked);
[cell setAccessoryType:isChecked ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone];
if (isChecked) {
[self.arrayObjectsSelected addObject:localManagedObject];
} else {
[self.arrayObjectsSelected removeObject:localManagedObject];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
} else {
NSLog(@"%@ - %@ - has (possible undefined) E~R~R~O~R at indexPath: %@_", NSStringFromClass(self.class), NSStringFromSelector(_cmd), indexPath);
}
}
TVC生命周期方法viewWillDisappear
中的此代码更新父TVC中的数据,当它是正在返回的NSArray
个托管对象时,或者在单个托管对象ID行的情况下只是取消选择,没有选择其他行(如果在进入表视图/ TVC时选择了行(选中标记)。
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
NSLog(@"%@ - %@ - values for:\n indexPathSelected: %@\n indexPathObjectFromArray: %@\n\n", NSStringFromClass(self.class), NSStringFromSelector(_cmd), self.indexPathSelected, self.indexPathObjectFromArray);
if (self.passBackManagedObjects) {
if (self.isArray) {
// Return an array of selected NSManagedObjects
[self blockSelectManagedObjects](self.arrayObjectsSelected);
} else if (self.indexPathSelected == nil) {
// Return nil where a previously selected (optional) entity is deactivated
[self blockSelectManagedObjectID](nil);
}
}
}
希望您喜欢通过这个小小的享受!任何问题随时都可以问。
答案 1 :(得分:1)
您使用相同的数据源,因此从技术上讲,您不会同步。使用一组对象进行检查。
UITableView
时,任何UITableViewDataSource
子类都需要调用tableView:cellForRowAtIndexPath:
reloadData
方法。这就是你做
if (![selectionSet containsObject:object]) {
[tableView selectRowAtIndexPath:indexPath animated:NO];
[cell setSelected:YES animated:NO];
}
else {
[tableView deselectRowAtIndexPath:indexPath];
[cell setSelected:NO animated:NO];
}
最好在IMO中覆盖UITableViewCell
setSelected:animated:
方法,以便您可以更好地控制UITableViewDelegate
方法tableView:didSelectRowAtIndexPath:
。