为同一视图控制器中的两个表实现单个“numberOfRowsInSection”方法

Posted

技术标签:

【中文标题】为同一视图控制器中的两个表实现单个“numberOfRowsInSection”方法【英文标题】:implement single `numberOfRowsInSection` method for two tables in same view controller 【发布时间】:2018-03-28 16:37:03 【问题描述】:

我有一个带有分段控件的视图控制器,用于控制用户看到的两个UITables 中的哪一个。当您在分段控件的索引之间切换时,会发出不同的提取请求并且表会重新加载数据。一个UITable(段索引1)允许用户添加和删除行。我在 Storyboard 和 viewDidLoad 中设置了表的委托和数据源。

我已经意识到,如果我转到这个视图控制器,切换到段索引 1,然后切换回索引 0,我会得到一个无效的更新错误。我相信这是因为我实现了 numberofRowsInSection。目前,我通过首先检查分段控制索引来评估行数。但是,当我尝试更新 seg index 1 中的表时,该方法失败了,但我退出了 seg index 0 上的视图控制器。

如何确定调用 numberOfRowsInSection 的内容,以便无论分段控件的当前索引如何,我都可以评估行数?

-[UITableView 中的断言失败 _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/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中的Core Data:

- (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];
    

【问题讨论】:

你的标题好像错了? @Koen 这个更好吗? 【参考方案1】:

我不确定我是否完全遵循事件链,但该错误消息是常见错误的标志。这意味着表视图数据源必须与您对表执行的任何插入或删除操作同步。如果你告诉表视图插入一行,你的数据源必须显示表视图比以前多了一行。表格视图将调用tableView(_:numberOfRowsInSection:) 进行查找。所以:

更新后现有节中包含的行数 (1) 必须等于更新前该节中包含的行数 (1),加上或减去从该节中插入或删除的行数(插入 1,删除 0)加上或减去移入或移出该部分的行数(0 移入,0 移出)。

在您更新之前,表格只有一行。然后你告诉表视图插入一行。但是在那次更新之后,表格视图数据源仍然说表格中有一行。如果您从一行开始并告诉表格视图插入一行,那么数据源现在必须显示有两行。

【讨论】:

谢谢你!我认为你是对的 - 这是我的 numberOfRowsInSection 的问题。我更新了我的帖子 您的问题现在令人困惑。表视图调用numberOfRowsInSection 方法。这就是问题所在,它正在调用该方法,而您返回的结果不正确。 是的,这就是我的问题。我更改了问题的标题和大部分内容。我试图找出 numberOfRowsInSection 的调用者,以便无论分段控件的当前索引如何,我都可以返回正确的结果。 请让我知道是否有更多我应该更改以更好地传达我的问题。 什么意思,这是你的问题?表视图调用该方法。这就是它应该做的。

以上是关于为同一视图控制器中的两个表实现单个“numberOfRowsInSection”方法的主要内容,如果未能解决你的问题,请参考以下文章

uitableview didselectrowatindex 仅适用于第一个表视图而不适用于同一视图控制器中的第二个表视图?

同一视图中的多个图表IOS图表

您是不是在同一个 prepareForSegue 方法中为一个视图控制器的所有 segue 实现行为?

在同一个视图控制器中使用 2 个表视图(swift 3)

同一个视图控制器Objective C中的两个UI选择器?

如何将两个不同的ListAPIView转换为单个ModelViewSet