My iOS app is using a UIPageViewcontroller with about 18 pages. Each page has a UITableView in it with 4 dynamic cells. Each cell has about 10 different images.
The problem is that each time I swipe the UIPageViewcontroller to go to the next/previous page, there's like a half-second delay that makes the user experience pretty bad.
It seems like the UITableView of the next page to be displayed after the swipe is loading all its heavy-data cells and that originates this delay.
I've read it's possible to make this loading asyncronous but I have no idea about how to do this.
Any of you guys have any idea about how to avoid this delay so the transition to pages runs smoothly?
Here's some code for the UITableView:
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 4;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([cellsStatus[indexPath.row] integerValue] == CLOSED_CELL)
return [GrintScoreTrackPlayerDynamicCell getHeightForCell];
else
return [GrintScoreTrackPlayerDynamicCell getFullHeightForCell];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if ([mainController respondsToSelector:@selector(tableViewDidScroll:)] &&
[mainController isKindOfClass:[GrintScoreTrackViewController class]])
{
[mainController tableViewDidScroll:scrollView];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier;
GrintScoreTrackPlayerDynamicCell *cell, *cellOwner;
// Cell ID
CellIdentifier = @"GrintScoreTrackPlayerDynamicCell-ID";
// Creating the cell
cell = (GrintScoreTrackPlayerDynamicCell*)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Creating the cell owner
cellOwner = [[GrintScoreTrackPlayerDynamicCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
// Getting cell from Nib, if possible.
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"GrintScoreTrackPlayerDynamicCell" owner:cellOwner options:nil];
cell = [nib objectAtIndex:0];
}
// Filling cell with the corresponding user info
[cell fillCellWithContent];
// Changing appearance based on the new status
[self setStatusOfCell:cell withStatus:[cellsStatus[indexPath.row] integerValue]];
// Setting action for cell's "collapseButton" button
[cell.collapseButton addTarget:self action:@selector(changeCellStatus:) forControlEvents:UIControlEventTouchUpInside];
// Setting action for cell's "editScoresButton" button
[cell.editScoresButton addTarget:self action:@selector(editScoresButtonWasPressed:) forControlEvents:UIControlEventTouchUpInside];
// Setting action for cell's "pickerEnterButton" button
[cell.pickerEnterButton addTarget:self action:@selector(pickerEnterButtonWasPressed:) forControlEvents:UIControlEventTouchUpInside];
// Setting cell selection style
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.layoutMargins = UIEdgeInsetsZero;
// Setting picker view delegate and data source.
[cell.pickerView setDelegate:self];
[cell.pickerView setDataSource:self];
return cell;
}
And some code from the GrintScoreTrackPlayerDynamicCell cell:
- (void) fillCellWithContent
{
// Doing some processing fixes
[self postProcessCellContent];
}
- (void) postProcessCellContent
{
CGSize textSize;
CGFloat scoreLabelTextWidth, newHazzardLabelXValue, newPuttsLabelXValue, offset;
// Setting an offset for the hazzard and putts label. This offset represents the distance between the score label text and the hazzard and putts labels.
offset = 1;
// Getting the width of the text that's inside the Score Label.
textSize = [[self.scoreLabel text] sizeWithAttributes:@{NSFontAttributeName:[self.scoreLabel font]}];
scoreLabelTextWidth = textSize.width;
// Getting new x position value for the Hazzards Label.
newHazzardLabelXValue = self.scoreLabel.frame.origin.x +
(self.scoreLabel.frame.size.width - scoreLabelTextWidth) * 0.5 -
self.hazardsLabel.frame.size.width - offset;
// Setting the new x position value for the Hazzards label
[GrintUtils moveView:self.hazardsLabel inX:newHazzardLabelXValue andY:self.hazardsLabel.frame.origin.y];
// Getting new x position value for the Putts Label.
newPuttsLabelXValue = self.scoreLabel.frame.origin.x +
(self.scoreLabel.frame.size.width - scoreLabelTextWidth) * 0.5 +
scoreLabelTextWidth + offset;
// Setting the new x position value for the Putts label
[GrintUtils moveView:self.puttsLabel inX:newPuttsLabelXValue andY:self.puttsLabel.frame.origin.y];
}
Here's some code about my UIPageViewController with the pages pre-loaded in pageContent array:
#pragma mark - UIPageViewController Data Course
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
// Getting the previous of the next controller
int index;
index = currentPageBeingDisplayed - 1;
if (index < 0)
index = [pageContent count] - 1;
return pageContent[index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
// Getting the index of the next controller
int index;
index = currentPageBeingDisplayed + 1;
index = index % [pageContent count];
return pageContent[index];
}
A screenshot of Instruments Tool Counter focused on the delay:
An finally 2 pictures (it has 2 states) of the cell:
fillCellWithContent
method. The other thing you should do is pre-load the "next" view controller in yourpageViewController
- that way it will probably have loaded all of its data before it is displayed. Also fetching the data in thecellForRowAtIndexPath
is not ideal - you should pre-fetch the data inviewDidLoad
or similar – Paulw11