0

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?

vendettacore
  • 1,439
  • 1
  • 13
  • 28

2 Answers2

0

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.

Community
  • 1
  • 1
vendettacore
  • 1,439
  • 1
  • 13
  • 28
0

I was getting the same error but found that it had to do with a different class having a mirrored copy of the array, and that class accessing a deleted index.

You can add an Exception Breakpoint to see exactly where the exception is coming from. Useful for differentiating errors in your code with errors coming from the iOS SDK: NSRangeException when deleting last UICollectionViewCell

Community
  • 1
  • 1
Cbas
  • 6,003
  • 11
  • 56
  • 87