objective-cuicollectionviewnsrangeexception

NSRangeException while deleting UICollectionView cells


I'm trying to remove cells from UICollectionView in for.. in loop and every time I get NSRangeException. I can't understand why does it happen because firstly I sort my array and then trying to remove. So the problem is that I firstly try to send request to the server and only if response is succes my UICollectionView cells and array elements are removes. Here is my code:

Pass elements through the loop:

- (IBAction)deletePictures:(id)sender {
int i = 0;
if (selectedPhotosURL.count>0){
    loadCount = (int)selectedPhotosURL.count;
//sorting an array (it works fine)
    NSArray *indexPaths = sortMediaCollection.indexPathsForSelectedItems;
    NSMutableArray *pathes = [NSMutableArray arrayWithArray:indexPaths];
    NSSortDescriptor *highestToLowest = [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:NO];
    [pathes sortUsingDescriptors:[NSArray arrayWithObject:highestToLowest]];
    [selecedCellsArray sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) {
        return [str2 compare:str1 options:(NSNumericSearch)];
    }];


    NSLog(@"selectedCElls %@",selecedCellsArray);
    for(NSIndexPath *indexPath in pathes) {
        NSLog(@"indexPath in pathes is %ld",(long)indexPath.row);
        AVMSMCell *cell = (AVMSMCell *)[sortMediaCollection cellForItemAtIndexPath:indexPath];
        AVMDataStore *oneItem = [smArray objectAtIndex:indexPath.row];
        NSString *contentId = oneItem.fileId;
        if (i<selectedPhotosURL.count){
            NSLog(@"indexPath second loop is %ld",(long)indexPath.row);
            [self deleteUserPhotos:contentId : indexPath.row : cell]; // send request to the server it's ok too.
            i++;
        }
    }

} else {
    [self selectAtLeastOneFirst];
  }

}

For example here I select 6 cells and my array sort with right order from up to down (5,4,3,2,1,0). Then I pass this elements in method with this order.

Request send method:

-(void)deleteUserPhotos : (NSString *)contentId : (NSInteger )pathRow : (AVMSMCell *) cell{
NSNumber *rowNsNum = [NSNumber numberWithUnsignedInt:(unsigned int)pathRow];
if (([selecedCellsArray containsObject:[NSString stringWithFormat:@"%@",rowNsNum]]) ) 
{
    cell.selectedBG.backgroundColor = DANGER;
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    NSString *token = [defaults objectForKey:@"token"];
    NSString *header = [NSString stringWithFormat:@"Bearer %@",token];
    NSDictionary *params = @{@"lang": @"en",@"content_id":contentId,@"project_id":[defaults objectForKey:@"project_id"]}; 
    manager.responseSerializer = [AFJSONResponseSerializer serializer];
    [manager.requestSerializer setValue:header forHTTPHeaderField:@"Authorization"];
    [manager POST:@"http://example.com/api/project/delete-content" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
        NSLog(@"JSON: %@", responseObject);

        if ([[responseObject objectForKey:@"result"] isEqualToString:@"success"]){
            @try{
                NSLog(@"pathRow in TRY %ld",(long)pathRow); // HERE I get wrong number after two or three elements already passed
                [smArray removeObjectAtIndex:(unsigned int)pathRow];

                [selecedCellsArray removeObject:[NSString stringWithFormat:@"%ld",(long)pathRow]];

                cell.selectedBG.hidden = YES;
                [sortMediaCollection reloadSections:[NSIndexSet indexSetWithIndex:0]];
                loadCount--;

                }
            } @catch (NSException *e){
                NSLog(@"something is bad %@",e);
                [SVProgressHUD dismiss];
                if (smArray.count<pathRow-1){
                [smArray removeObjectAtIndex:(unsigned int)pathRow-1];
                } 
            } @finally {
                cell.selectedBG.hidden = YES;
                [sortMediaCollection reloadSections:[NSIndexSet indexSetWithIndex:0]];
            }
        } else {
            NSLog(@"can't delete photo!");
        }
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
        NSLog(@"Error: %@", error);
        errorEndSpinner
    }];
    }
}

So in method above I get wrong element number after two or three elements already passed i.e frist element is 5,then 4, then 2, 3,1,0. And at this moment my @catch handle exception and trying to remove element with [smArray removeObjectAtIndex:(unsigned int)pathRow-1]; and then I get NSRangeException and my app crashing. What I do wrong?


Solution

  • I solved my problem with this answer and little bit modified my code. I've got an NSRangeException because I removed my UICollectionView items through the loop. Instead I should better use multiple deleting instantly like this:

        // Delete the items from the data source.
                        [self deleteItemsFromDataSourceAtIndexPaths:selectedItemsIndexPaths];
    
                            // Now delete the items from the collection view.
                        [sortMediaCollection deleteItemsAtIndexPaths:selectedItemsIndexPaths];
    

    So now my code looks like:

    Pass through the loop:

    - (IBAction)deletePictures:(id)sender {
    int i = 0;
    if (selectedPhotosURL.count>0){
        [SVProgressHUD showWithStatus:@"Deleting" maskType:SVProgressHUDMaskTypeBlack];
        loadCount = (int)selectedPhotosURL.count;
        NSArray *indexPaths = sortMediaCollection.indexPathsForSelectedItems;
        NSMutableArray *pathes = [NSMutableArray arrayWithArray:indexPaths];
        NSSortDescriptor *highestToLowest = [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:NO];
        [pathes sortUsingDescriptors:[NSArray arrayWithObject:highestToLowest]];
        [selecedCellsArray sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) {
            return [str2 compare:str1 options:(NSNumericSearch)];
        }];
    
        for(NSIndexPath *indexPath in pathes) {
            AVMSMCell *cell = (AVMSMCell *)[sortMediaCollection cellForItemAtIndexPath:indexPath];
            AVMDataStore *oneItem = [smArray objectAtIndex:indexPath.row];
            NSString *contentId = oneItem.fileId;
            if (i<selectedPhotosURL.count){
                [self deleteUserPhotos:contentId : indexPath.row : cell pathes:pathes]; //pass array with pathes into 'deleteUserPhotos'
                i++;
            }
        }
    
    } else {
        [self selectAtLeastOneFirst];
       }
    
    }
    

    Main method:

    -(void)deleteItemsFromDataSourceAtIndexPaths:(NSArray  *)itemPaths {
    NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
    for (NSIndexPath *itemPath  in itemPaths) {
        [indexSet addIndex:itemPath.row];
    }
    [smArray removeObjectsAtIndexes:indexSet];
    }
    
    -(void)deleteUserPhotos : (NSString *)contentId : (NSInteger )pathRow : (AVMSMCell *) cell pathes:(NSMutableArray*)selectedItemsIndexPaths{
    NSNumber *rowNsNum = [NSNumber numberWithUnsignedInt:(unsigned int)pathRow];
    if (([selecedCellsArray containsObject:[NSString stringWithFormat:@"%@",rowNsNum]]))
    {
        cell.selectedBG.backgroundColor = DANGER;
        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
        NSString *token = [defaults objectForKey:@"token"];
        NSString *header = [NSString stringWithFormat:@"Bearer %@",token];
        NSDictionary *params = @{@"lang": @"en",@"content_id":contentId,@"project_id":[defaults objectForKey:@"project_id"]}; 
        manager.responseSerializer = [AFJSONResponseSerializer serializer];
        [manager.requestSerializer setValue:header forHTTPHeaderField:@"Authorization"];
        [manager POST:@"http://example.com/api/project/delete-content" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
            [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
            NSLog(@"JSON: %@", responseObject);
    
            if ([[responseObject objectForKey:@"result"] isEqualToString:@"success"]){
                @try{
    
                    [selecedCellsArray removeObject:[NSString stringWithFormat:@"%ld",(long)pathRow]];
    
                    loadCount--;
    
                    if (loadCount==0){ //only there remove items from collectionview
    
                            // Delete the items from the data source.
                        [self deleteItemsFromDataSourceAtIndexPaths:selectedItemsIndexPaths];
                            // Now delete the items from the collection view.
                        [sortMediaCollection deleteItemsAtIndexPaths:selectedItemsIndexPaths];
    
                        [selectedPhotosURL removeAllObjects];
                        }
                } @catch (NSException *e){
                    NSLog(@"something is bad %@",e);
                    [SVProgressHUD dismiss];
                    if (smArray.count<pathRow-1){
                    [smArray removeObjectAtIndex:(unsigned int)pathRow-1];
                    }
                } @finally {
                    cell.selectedBG.hidden = YES;
                    [sortMediaCollection reloadSections:[NSIndexSet indexSetWithIndex:0]];
                }
            } else {
                NSLog(@"can't delete photo!");
            }
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
            NSLog(@"Error: %@", error);
            errorEndSpinner
        }];
      }
    }
    

    Hope this will be helpful for somebody.