0

I have a UITableView for an Instagram feed. I have implemented UIRefreshControl for pull to refresh functionality.

After drag and release the refresh control, and while the spinner is going, I'm able to drag the tableview down without the app crashing. However, if I scroll the tableview upwards, the app crashes (maybe because of cell 2, 3 etc?)

Here's a video showing the problem: http://www.screenmailer.com/v/DukT4lt2aUGm8c5MLRlGMg/2586/3a23tXo7uXs.mp4

Why is this happening?

Code for the .m file:

#import "InstagramViewController.h"
#import "InstagramCell.h"
#import <InstagramKit/InstagramKit.h>
#import "UIImageView+AFNetworking.h"

@interface InstagramViewController ()
{
    NSMutableArray *mediaArray;
}

@property (nonatomic, strong) InstagramPaginationInfo *currentPaginationInfo;

@end

@implementation InstagramViewController

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        //mediaArray = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)viewDidLoad
{
    mediaArray = [[NSMutableArray alloc] init];

    [super viewDidLoad];

    [self loadMedia];

    self.refreshControl = [[UIRefreshControl alloc] init];
    [self.refreshControl addTarget:self action:@selector(reloadMedia) forControlEvents:UIControlEventValueChanged];

    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
}

-(void)reloadMedia
{
    self.currentPaginationInfo = nil;

    [mediaArray removeAllObjects];

    [self loadMedia];
}

-(IBAction)loadMedia
{
    // start network indicator
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];

    [[InstagramEngine sharedEngine] getMediaWithTagName:@"AUFsommer" count:10 maxId:self.currentPaginationInfo.nextMaxId withSuccess:^(NSArray *media, InstagramPaginationInfo *paginationInfo) {

        if (paginationInfo)
        {
            self.currentPaginationInfo = paginationInfo;
        }

        [mediaArray addObjectsFromArray:media];

        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];

        [self reloadData];

    } failure:^(NSError *error) {

        NSLog(@"Search Media Failed");

        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];

    }];


}

-(void)reloadData
{
    [self.refreshControl endRefreshing];
    [self.tableView reloadData];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Table view data source

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    UIView *headerView = [[UIView alloc] init];
    headerView.backgroundColor = [UIColor colorWithWhite:1.0f alpha:0.9f];

    InstagramMedia *media = mediaArray[section];

    // create imageview for profile photo
    AsyncImageView *profilePhoto = [[AsyncImageView alloc] initWithFrame:CGRectMake(8, 8, 32, 32)];
    profilePhoto.layer.borderColor = [[UIColor colorWithRed:204.0/255.0f green:204.0/255.0f blue:204.0/255.0f alpha:1.0f] CGColor];
    profilePhoto.layer.borderWidth = 1;
    profilePhoto.layer.masksToBounds = YES;
    profilePhoto.layer.cornerRadius = 16.0;
    [profilePhoto loadImageFromURL:[media.user.profilePictureURL absoluteString]];

    // uifont settings
    UIFont *labelFont = [UIFont boldSystemFontOfSize:13.0];

    // create label for username
    UILabel *usernameLabel = [[UILabel alloc] initWithFrame:CGRectMake(48, 0, 210, 48)];
    usernameLabel.text = media.user.username;
    usernameLabel.font = labelFont;
    usernameLabel.textColor = [UIColor colorWithRed:235.0/255.0 green:24.0/255.0 blue:22.0/255.0 alpha:1.0f];

    // create label for timestamp
    UILabel *timestampLabel = [[UILabel alloc] initWithFrame:CGRectMake(250, 0, 54, 48)];
    timestampLabel.textAlignment = NSTextAlignmentRight;
    timestampLabel.font = labelFont;
    // timestampLabel.text = [self stringForDisplayFromDate:media.createdDate];

    // add to view
    [headerView addSubview:profilePhoto];
    [headerView addSubview:usernameLabel];
    [headerView addSubview:timestampLabel];

    return headerView;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return mediaArray.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return 1;
}

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

    if (cell == nil)
    {
        cell = [[InstagramCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    // clear photo
    [cell.igPhoto setImage:nil];

    if (mediaArray.count >= indexPath.section+1)
    {
        InstagramMedia *media = mediaArray[indexPath.section];

        cell.title.text = media.caption.text;

        [cell.igPhoto loadImageFromURL:[media.standardResolutionImageURL absoluteString]];
    }

    return cell;
}


- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    InstagramMedia *media = mediaArray[indexPath.section];

    CGSize maximumLabelSize = CGSizeMake(304.0f, 20000.0f);
    CGSize expectedLabelSize = [media.caption.text sizeWithFont:[UIFont systemFontOfSize:13.0f] constrainedToSize:maximumLabelSize lineBreakMode:NSLineBreakByWordWrapping];

    return (320.0f + expectedLabelSize.height + 20.0f);
}

-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    CGFloat currentOffset = scrollView.contentOffset.y;
    CGFloat maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height;

    if (maximumOffset - currentOffset < 10.0)
    {
        [self loadMedia];
    }
}

@end
CAMOBAP
  • 5,523
  • 8
  • 58
  • 93
rebellion
  • 6,628
  • 11
  • 48
  • 79
  • 3
    do you have a crash log? that would help a lot. – Volker May 28 '14 at 12:14
  • Sorry. index out of bounds. `Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds for empty array` – rebellion May 28 '14 at 12:25

1 Answers1

6

The first thing you should do is to add an exception breakpoint in your project, so you will know why and where it crashes.

The reason why your app is crashing is that you try to read in an empty array. In the method reloadMedia, you do this :

self.currentPaginationInfo = nil;    
[mediaArray removeAllObjects];

So at this point, your array is empty, but your UITableView is not aware of that. By scrolling before your data is reloaded, the methods cellForRowAtIndexPath will get called and try to access an index in your empty array.

To fix this, you can call [self.tableView reloadData]; after [mediaArray removeAllObjects];. This way, the UITableView will makes its call to know how many rows and sections it should have and will be aware there is no more rows and sections.

Or, if you want the old information to still be in your UITableView during the loading, just don't nil the currentPaginationInfo or empty mediaArray in reloadMedia,

Emilie
  • 2,413
  • 13
  • 12
  • Thanks, this worked. I have to set `currentPaginationInfo = nil`, but commenting out `[mediaArray removeAllObjects]` was a good idea. – rebellion May 28 '14 at 12:55