ios 8.1 - UITableView Freezing from Asynchronous Reload

799 Views Asked by At

Bear with the noob...

There are several similar questions on this issue, mainly dealing with pulling asynchronous data from a given source and populating a UITableView.

In reviewing these and trying several listed solutions, I'm still showing the same issue:

  1. My main app is controlled with a tab bar, the main window being a google map view, and the second tab for some more advanced options. I bring this up in case what I'm seeing is related to memory usage. One of the "advanced options" is giving me trouble.

  2. On the second tab, one of the buttons launches a view controller, "Places". This is the trouble spot. This class pulls data from the factual ios api: https://github.com/Factual/factual-ios-sdk. This is a list of places in the area with phone numbers, etc.

  3. The code for "Places.h" and "Places.m" are below. This view controller is populating a UITableView. On an iPhone 5, there is no issue with scrolling. On an iPhone 6 and 4s, the scrolling works ok for few rows, and then freezes. It will sometimes unfreeze after a long time.

  4. I have isolated this to the asynchronous "[tableView reloadData]" call. The same freezing issue appears when the call to the Factual API is removed, cells are populated with the word "Test". In both cases, in this app, the reloading data freezes the app.

  5. Several other posts have pointed to the "reloadData" causing sluggish performance, but not freezing.

  6. After trying the solutions listed on the other posts, I still have the same issue with freezing while scrolling down a few rows.

  7. The only way I've gotten the table view to not freeze while scrolling is to hard code a number of asynchronous reloads. Too high a number of reloads, and I get it into a frozen state. It goes without saying that I am asking for trouble with this solution, and at a minimum, it seems terrible practice.

  8. Included in the code is a call to [PlacesFactQuery startTheActions]. This only launches the factual api, and makes enough calls to enough coordinates to populate a global array of places.

  9. Places.m will check for when the global array contains more than zero points. At which time, it will start to populate the cells.

Here is the Places code. Any recommendations on how to reload this data without hard coding a set number of async counts and without freezing will help me tremendously.

Places.h

#import <UIKit/UIKit.h>
#import <FactualSDK/FactualAPI.h>
#import <FactualSDK/FactualQuery.h>




@interface Places : UIViewController <UITableViewDataSource, UITableViewDelegate> {
    NSTimeInterval *requestStartTime;
}

@property (nonatomic, retain) IBOutlet UITableView *tableView;

@property (nonatomic, retain) NSMutableDictionary* prefs;
@property (strong,retain)  FactualQueryResult* queryResult;
@property (nonatomic, retain) FactualAPIRequest* activeRequest;
@property (strong, retain) NSMutableArray *tokenAndSourceAndNorthAndSouthBounds;
@property (strong, retain) NSMutableArray *placesInCorridor;
@property (assign, nonatomic) bool diagMode;
@property (nonatomic, assign) int numLoads;


@end

Places.m

#import "Places.h"
#import "Storage.h"
#import "QueryPreferences.h"
#import "PlacesFactQuery.h"
#import "PlacesDiag.h"




@implementation Places

-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nil bundle:nil];

    if (self)
    {
        self.title = @"Places in Corridor";
        self.diagMode = false;
    }

    self.numLoads = 0;

    return self;
}


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

    if ([self respondsToSelector:@selector(edgesForExtendedLayout)]) {

        [self setEdgesForExtendedLayout:UIRectEdgeBottom];

    }

    //[self loadDiagnostic];

}


- (void) viewDidLoad
{

    PlacesFactQuery *placesFactQuery = [[PlacesFactQuery alloc] init];
    [placesFactQuery startTheActions];

}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView
 numberOfRowsInSection:(NSInteger)section
{
    if (self.diagMode)
    {
        return 5;
    } else {

        if ([globalPlacesInCorridor count] > 0)
        {
            return [globalPlacesInCorridor count];
        }else
        {
            return 1;
        }

    }

}

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    tableView.dataSource = self;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];


    if(cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:@"Cell"];
    }

    if (self.diagMode)
    {
        cell.detailTextLabel.text = @"test";
        cell.detailTextLabel.numberOfLines = 1;
    } else {

        if ([globalPlacesInCorridor count] > 0)
        {
            NSArray *arrayForInfo = [[NSArray alloc] initWithArray:globalPlacesInCorridor[indexPath.row]];


            cell.detailTextLabel.text = [NSString stringWithFormat:@"%@\n%@\n%@\n%@",
                                         arrayForInfo[0], arrayForInfo[4], arrayForInfo[5],
                                         arrayForInfo[6]];
            cell.detailTextLabel.numberOfLines = 5;


        } else
        {
            cell.detailTextLabel.text = @"Please Wait...";
            cell.detailTextLabel.numberOfLines = 1;
        }


        if (self.numLoads < 500)
        {
            dispatch_async(dispatch_get_main_queue(), ^{
                [tableView reloadData];
            });
            self.numLoads++;
        }

    }

    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{

    return 100.0f;
}


-(void) loadDiagnostic
{
    PlacesDiag *placesDiag = [[PlacesDiag alloc] initWithNibName:nil bundle:nil];

    if ([globalPlacesInCorridor count] > 0)
    {
        placesDiag.placesList = globalPlacesInCorridor;
    }
    if ([globalPlacesAll count] > 0)
    {
        placesDiag.placesAll = globalPlacesAll;
    }

    [self.navigationController pushViewController:placesDiag animated:YES];

}

@end
0

There are 0 best solutions below