Основа этой методики была взята из очень полезной книги Эрики Садун «The Core iOS 6 Developer’s Cookbook», четвертое издание, опубликованной Addison Wesley. Я разработал множество решений на основе идей и кода, представленных в книге Эрики.
Примечания
- Это не элегантно, но это работает.
- Это решение предназначено для цели под управлением iOS 7 и ниже.
UISearchDisplayController
устарел в iOS 8 в пользу UISearchController
.
- Чтобы попытаться сделать ответ как можно короче, это решение не включает фрагменты кода, необходимые для правильной подготовки табличных представлений
self.tableView
и self.searchDisplayController.searchResultsTableView
.
- Это решение предполагает использование функционирующего Core Data Stack и
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);
Свойства managedObjectID
и arrayObjects
устанавливаются с помощью метода prepareForSegue
, содержащегося в родительском TVC. Устанавливается только одно или другое, в зависимости от того, передаете ли вы один NSManagedObjectID
(одиночный выбор) или NSArray
из нескольких NSManagedObject
(множественный выбор).
Свойство isArray
можно было бы удалить, но я включаю его для простоты кодирования и удобочитаемости кода. Он также устанавливается тем же методом prepareForSegue
в родительском TVC, упомянутом выше.
Блоки определяются в родительском 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_>>
}
Шаг четвертый
Подготовьтесь к заполнению ваших UITableViewCell
с помощью метода источника данных TVC 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);
}
}
Шаг шестой
Этот код в методе viewWillDisappear
жизненного цикла TVC обновляет данные в родительском TVC, когда возвращается NSArray
управляемых объектов, или в случае, когда одна строка идентификатора управляемого объекта просто отменяется, а никакая другая строка не выбрана (в случай, когда строка была выбрана (отмечена галочкой) при входе в табличное представление / 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);
}
}
}
Надеюсь, вам понравится работать над этим маленьким удовольствием! Любые вопросы, не стесняйтесь спрашивать.
person
andrewbuilder
schedule
26.08.2014