我有一个带有分段控件的视图控制器,可以控制用户看到的两个UITable
中的哪一个。在分段控件的索引之间切换时,会生成不同的提取请求,并且表会重新加载数据。一个UITable
(段索引1)允许用户添加和删除行。我在Storyboard和viewDidLoad
中设置了表的委托和数据源。
我已经意识到,如果我转到此视图控制器,切换到段索引1然后切换回索引0,我得到无效的更新错误。我相信这是因为我实现了numberofRowsInSection。目前,我通过首先检查分段控制索引来评估行数。但是,当我尝试更新seg索引1中的表时,该方法失败,但我在seg索引0上退出了视图控制器。
如何确定调用numberOfRowsInSection的内容,以便无论分段控件的当前索引如何,我都可以评估行数?
断言失败 - [UITableView _endCellAnimationsWithContext:],/ BuildRoot / Library / Cache / com.apple.xbs / Sources / UIKit / UIKit-3698.34.4 / UITableView.m:2011 无效更新:第0节中的行数无效。更新后现有部分中包含的行数(1) 必须等于之前该部分中包含的行数 更新(1),加上或减去插入或删除的行数 从该部分(插入1,删除0)和加号或减号 移入或移出该部分的行(0移入,0移出)。 (空)
- (IBAction)segmentChanged:(id)sender {
//first table
if (_segmentedControl.selectedSegmentIndex == 0) {
/*for switching between collection map view and browse list */
_table.hidden= NO;
[mapVC willMoveToParentViewController:nil];
[mapVC.view removeFromSuperview];
[_mapVC removeFromParentViewController];
_searchController.searchBar.hidden = NO;
if (storedDataset_ != dataset) {
speciesCurrentPredicate = [NSPredicate predicateWithFormat:@"dataset & %d > 0", dataset];
}
[self fetchResultsUsingSegmentedControlIndex];
if (currentSelectedIndexPath)
{
[[self.table cellForRowAtIndexPath:currentSelectedIndexPath] setSelected:NO animated:YES];
currentSelectedIndexPath = nil;
}
[_table reloadData];
// Define your required task...
}
/*second "tab" - My Collection
set up table view of user's collected leafs*/
else if(_segmentedControl.selectedSegmentIndex == 1) {
// _searchController.searchBar.hidden = YES;
NSError *error;
[self collectionFetchedResultsController:collectionCurrentPredicate];
[self.collectionFetchedResultsController performFetch:&error];
[_table reloadData];
collectedLeafArray = [collectionFetchedResultsController fetchedObjects];
}
}
- (NSFetchedResultsController *)collectionFetchedResultsController:(NSPredicate*)predicate
{
NSString * sectionNameKeyPath = nil;
/* if (collectionFetchedResultsController != nil)
{
return collectionFetchedResultsController;
}*/
NSManagedObjectContext* context = [(LeafletAppDelegate*)[[UIApplication sharedApplication] delegate] managedObjectContext];
// Create and configure a fetch request with the Species entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSLog(@"this a predicate %@", predicate);
if(predicate){
sectionNameKeyPath = @"selectedSpecies";
[fetchRequest setPredicate:predicate];
}
NSEntityDescription *entity = [NSEntityDescription entityForName:@"CollectedLeaf" inManagedObjectContext:context] ;
[fetchRequest setEntity:entity];
// Create the sort descriptors array.
NSSortDescriptor *idDescriptor = [[NSSortDescriptor alloc] initWithKey:@"collectedDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:idDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller.
collectionFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:sectionNameKeyPath cacheName:nil];
collectionFetchedResultsController.delegate = self;
// Memory management.
[fetchRequest release];
[idDescriptor release];
[sortDescriptors release];
return collectionFetchedResultsController;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
NSLog(@"begin updates called");
[_table beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
switch(type) {
case NSFetchedResultsChangeInsert:
NSLog(@"ns fetched results change insert at %@", [NSArray arrayWithObject:newIndexPath]);
[_table insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[_table deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
NSLog(@"ns fetched results change update");
// [_table reloadRowsAtIndexPaths:@[indexPath]
// withRowAnimation:UITableViewRowAnimationAutomatic];
[self configureCell:[_table cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[_table deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[_table insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[_table insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[_table deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
//nothing
break;
case NSFetchedResultsChangeUpdate:
//
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
NSLog(@"end updates called");
[_table endUpdates];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
/*Only allow deletion for collection table */
if(_segmentedControl.selectedSegmentIndex == 1) {
if (editingStyle == UITableViewCellEditingStyleDelete)
{
NSLog(@"index path at commitediting style %@", indexPath);
//id result = nil;
CollectedLeaf * collectedLeaf = nil;
if ([[collectionFetchedResultsController sections] count] > [indexPath section]){
id <NSFetchedResultsSectionInfo> sectionInfo = [[collectionFetchedResultsController sections] objectAtIndex:[indexPath section]];
if ([sectionInfo numberOfObjects] > [indexPath row]){
collectedLeaf = [collectionFetchedResultsController objectAtIndexPath:indexPath];
LeafletPhotoUploader * leafletPhotoUploader = [[LeafletPhotoUploader alloc] init];
leafletPhotoUploader.collectedLeaf = collectedLeaf;
if([LeafletUserRegistration isUserRegistered]) {
[leafletPhotoUploader deleteCollectedLeaf:collectedLeaf delegate:self];
}else{
NSLog(@"I am not registered and want to delete leaf");
}
NSManagedObjectContext *context = [collectionFetchedResultsController managedObjectContext];
[context deleteObject:collectedLeaf];
NSError *error;
if (![context save:&error])
{
NSLog(@"Failed to save to data store: %@", [error localizedDescription]);
NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors != nil && [detailedErrors count] > 0)
{
for(NSError* detailedError in detailedErrors)
{
NSLog(@" DetailedError: %@", [detailedError userInfo]);
}
}
else
{
NSLog(@" %@", [error userInfo]);
}
}
}
}
}
}
}
对象将保存到PhotoUploader.m
中的核心数据:
- (void)requestFinished:(ASIHTTPRequest *)request
{
// Use when fetching text data
NSString *responseString = [request responseString];
//NSLog(@"Photo uploader response: %@", responseString);
// Use when fetching binary data
//NSData *responseData = [request responseData];
NSError *error;
SBJSON *json = [[SBJSON new] autorelease];
NSDictionary *jsonDictionary = [json objectWithString:responseString error:&error];
//NSLog(@"Photo uploader response: %@", jsonDictionary);
if (jsonDictionary == nil)
{
error = [NSError errorWithDomain:kLeafletErrorDomain code:kErrorResultNotReady userInfo:nil];
if(![self isALabelRequest:request] && [delegate respondsToSelector:@selector(leafletPhotoUploader:didFailWithError:)])
{
[delegate leafletPhotoUploader:self didFailWithError:error];
}
}
else if (collectedLeaf)
{
// If this is the first time uploading
if(collectedLeaf.leafID == nil) {
NSManagedObjectContext* managedObjectContext = [(LeafletAppDelegate*)[[UIApplication sharedApplication] delegate] managedObjectContext];
collectedLeaf.leafID = [jsonDictionary objectForKey:@"id"];
LeafletURL* segmentedImageURL = (LeafletURL*)[NSEntityDescription insertNewObjectForEntityForName:@"LeafletURL" inManagedObjectContext:managedObjectContext];
segmentedImageURL.dataSource = @"SegmentedImage";
segmentedImageURL.type = @"SegmentedImage";
segmentedImageURL.rawURL = [NSString stringWithFormat:@"/%@/segmented.png", collectedLeaf.leafID];
segmentedImageURL.hiResImageLocation = kLocationServer;
segmentedImageURL.thumbnailLocation = kLocationServer;
collectedLeaf.segmentedImageURL = segmentedImageURL;
LeafletURL* originalImageURL = (LeafletURL*)[NSEntityDescription insertNewObjectForEntityForName:@"LeafletURL" inManagedObjectContext:managedObjectContext];
originalImageURL.dataSource = @"OriginalImage";
originalImageURL.type = @"OriginalImage";
originalImageURL.rawURL = [NSString stringWithFormat:@"/%@/original.jpg", collectedLeaf.leafID];
originalImageURL.hiResImageLocation = kLocationServer;
originalImageURL.thumbnailLocation = kLocationServer;
collectedLeaf.originalImageURL = originalImageURL;
NSError *error = nil;
BOOL success = [managedObjectContext save:&error];
if(!success){
NSLog(@"Unresolved error saving %@, %@", error, [error userInfo]);
[self showAlert];
}else{
/*If successfully saved uploaded leaf, delete image stored in documents directory from collecting w/o internet connection */
if(collectedLeaf.localImageFileName){
[self removeImage:collectedLeaf.localImageFileName];
}
}
} else {
// This means that the labeling has changed
NSManagedObjectContext* managedObjectContext = [(LeafletAppDelegate*)[[UIApplication sharedApplication] delegate] managedObjectContext];
collectedLeaf.syncStatus = kSyncStatusSame;
[managedObjectContext save:&error];
}
}
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
if(![self isALabelRequest:request] && [delegate respondsToSelector:@selector(leafletPhotoUploader:didUploadImage:)])
{
[delegate leafletPhotoUploader:self didUploadImage:collectedLeaf];
}
}
答案 0 :(得分:0)
我不确定我是否完全遵循事件链,但该错误消息是常见错误的标志。这意味着表视图数据源必须与您在表上执行的任何插入或删除操作同步。如果告诉表视图插入一行,则数据源必须显示表视图的行数比以前多一行。表格视图将调用void func(const char* str)
{
void *s = &str;
std::cout << *(char**)s << std::endl;
}
int main()
{
char *str = "hello world";
func(str);
char *str_1 = "Hello world";
void *ptr_1 = str;
std::cout << (char*)ptr_1 << std::endl;
// std::cout << *(char**)ptr_1 << std::endl;
return 0;
}
来查找。所以:
更新(1)后现有部分中包含的行数必须等于更新(1)之前该部分中包含的行数,加上或减去从该部分插入或删除的行数(1个已插入,0个已删除)并加上或减去移入或移出该部分的行数(0移入,0移出)。
在您更新之前,该表有一行。然后你告诉表视图插入一行。但在更新之后,表视图数据源仍然表示表中有一行。如果从一行开始并告诉表视图插入一行,则数据源现在必须显示有两行。