0

I'm trying to retrieve uploaded thumbnailPhotos from Parse to display them in UITableViewCells, but I get an exception thrown in everytime. The error code is as follows: "Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'"
This is my code:

- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];

PFQuery *query = [PFQuery queryWithClassName:@"Events"];
[query orderByDescending:@"date"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
    if (error) {
        NSLog(@"Error: %@ %@", error, [error userInfo]);
    } else {
        self.events = (NSMutableArray *) objects;
        [self.tableView reloadData];

        for (PFObject *event in self.events) {

            NSInteger index = [self.events indexOfObject:event];

            PFFile *imageFile = [event objectForKey:@"thumbnailImage"];

            [imageFile getDataInBackgroundWithBlock:^(NSData *result, NSError *error) {

                if (error) {
                    //Handle Error
                } else {

                    UIImage *image = [UIImage imageWithData:result];

                    if (self.thumbnailPhotos == nil) {

                        self.thumbnailPhotos = [NSMutableArray array];
                        self.thumbnailPhotos[index] = image;
                    } else {

                    self.thumbnailPhotos[index] = image;
                    }

                    [self.tableView reloadData];
                }

            }];
        }
    }
}];

}

CellForRowAtIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *reuseIdentifier = @"Cell";
EventsTableViewCell *cell = (EventsTableViewCell *)[tableView dequeueReusableCellWithIdentifier:reuseIdentifier forIndexPath:indexPath];

PFObject *event = [self.events objectAtIndex:indexPath.row];

NSDate *date = [event objectForKey:@"date"];

NSString *dateString = [self.dateFormat stringFromDate:date];
NSString *timeString = [self.timeFormat stringFromDate:date];
NSLog(@"IndexPath.row = %ld", (long)indexPath.row);

if ([self.thumbnailPhotos objectAtIndex:indexPath.row] != nil) {

    cell.imageView.image = self.thumbnailPhotos[indexPath.row];
} else {
    NSLog(@"Nil, Application will crash!");
}

cell.eventNameLabel.text = [event objectForKey:@"title"];
cell.dateLabel.text = dateString;
cell.timeLabel.text = timeString;
[cell.timeLabel sizeToFit];

return cell;

}`

I had to add the index value of self.events because the thumbnailPhotos were downloaded in different speed, so my Cells always showed the wrong photo for the wrong event. I hope this was enough details to figure the problem out.

4
  • Does the crash occur on the line self.thumbnailPhotos[index] = image;? Commented Aug 20, 2015 at 21:06
  • The root cause is that reload data must happen after all of the image fetches have occurred (under your current design). The way it's coded, the reload happens before any of them complete. The solution is to either (a) wait to reload until the list of image data fetches is done or (b) load the images lazily, as the cells are configured. I can show you how to do either (so can many others). Its up to you. Commented Aug 20, 2015 at 21:06
  • 1
    @MikeAtNobel, the question should have stated this, but I'm 99% certain the failing line is if ([self.thumbnailPhotos objectAtIndex:indexPath.row] != nil) {. See my prior comment to see why. Commented Aug 20, 2015 at 21:07
  • Yes, it is in the line stated by danh. I am actually not that good at coding, but I am practicing and trying to become better, so it would be nice if you could show me how I could implement both solutions, or maybe suggest me even a better way to code this functionality. Commented Aug 20, 2015 at 21:21

1 Answer 1

1

Application is crashing because thumbnailPhotos doesn't have any object at index for assignment. Please use following code.

Updated Code which will support Dictionary to hold thumbnail images

 /*

     Define events as NSMutableArray which holds all events 
     Define thumbnailPhotos as NSMutableDictionary which holds thumbnail image for index as key

     */

        //Create a weak Reference of self for accessing self within block

        __weak __typeof(self)weakSelf = self;
        PFQuery *query = [PFQuery queryWithClassName:@"Events"];
        [query orderByDescending:@"date"];
        [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
            if (error) {
                NSLog(@"Error: %@ %@", error, [error userInfo]);
            } else {
                //Create a strong reference for updating the UI
                __strong __typeof(weakSelf)strongSelf = weakSelf;

                //Assign the events to instance object
                strongSelf.events = [NSMutableArray arrayWithArray:objects];

                //Alloced thumbnail dictionary
                strongSelf.thumbnailPhotos = [NSMutableDictionary dictionary];

                //Reload tableView so that data will be visible
                [strongSelf.tableView reloadData];

                for (PFObject *event in strongSelf.events) {

                    //Define index as block type because we have to use this instance within block
                   __block NSInteger index = [strongSelf.events indexOfObject:event];

                    PFFile *imageFile = [event objectForKey:@"thumbnailImage"];
                    [imageFile getDataInBackgroundWithBlock:^(NSData *result, NSError *error) {

                        if (error) {
                            //Handle Error
                        } else {

                            UIImage *image = [UIImage imageWithData:result];

                            //Set the image against index
                            [strongSelf.thumbnailPhotos setObject:@"" forKey:@(index)];

                            //Reload only cell for which image is just downloaded
                            [strongSelf.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
                        }

                    }];
                }
            }
        }];

Updated: Modify your cellForRowAtIndexPath as below

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *reuseIdentifier = @"Cell";
    EventsTableViewCell *cell = (EventsTableViewCell *)[tableView dequeueReusableCellWithIdentifier:reuseIdentifier forIndexPath:indexPath];

    PFObject *event = [self.events objectAtIndex:indexPath.row];

    NSDate *date = [event objectForKey:@"date"];

    NSString *dateString = [self.dateFormat stringFromDate:date];
    NSString *timeString = [self.timeFormat stringFromDate:date];
    NSLog(@"IndexPath.row = %ld", (long)indexPath.row);

    if ([self.thumbnailPhotos valueForKey:@(indexPath.row)]) {

        cell.imageView.image = [self.thumbnailPhotos valueForKey:@(indexPath.row)];
    } else {
        NSLog(@"Nil, Application will crash!");
    }

    cell.eventNameLabel.text = [event objectForKey:@"title"];
    cell.dateLabel.text = dateString;
    cell.timeLabel.text = timeString;
    [cell.timeLabel sizeToFit];

    return cell;
}

In this implementation thumbnail Image is getting saved in to dictionary and second reload of tableView is per cell basis instead of reloading complete tableView for single thumbnail download.

Sign up to request clarification or add additional context in comments.

9 Comments

this one will crash while its testing to avoid the crash.
This one is actually working perfectly so far, thank you. :) So the only problem with my code was that I didn't add "placeholder" objects to my thumbnails array? I would like to know what I did wrong so I can probably understand a bit more about coding.
@danh I modified the source code have more stable solution. Please give a try
@Strikecounter2 you did not added placeholder images into array and trying to access the object from array, however your array didn't had any object at all.
But I dont quite understand why it still tried to access the missing objects in the array. I have checked if there is an object at the given indexPath.row so if there is no object, nothing will happen.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.