jmoukel
jmoukel

Reputation: 794

UIPageViewController Swipe Delay

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:

enter image description here

An finally 2 pictures (it has 2 states) of the cell:

enter image description here

enter image description here

Upvotes: 2

Views: 2296

Answers (1)

matt
matt

Reputation: 535222

This is going to be a wild guess - I haven't tried it. But from what you've so brilliantly deduced, and from what you've said in your comments, I take it that this is how the images get into the cells: they are in the cell-defining .xib.

So, for every row of the table, the .xib is loaded and the images must all be loaded freshly from the .xib. This is what we believe is slowing you down.

So here's a suggestion: don't do that. Instead, just have empty image views in the cell, and populate them with images in cellForRowAtIndexPath:.

Why would this help? Because if you fetch an image by calling imageNamed:, the image is cached by the system. Thus each image will be fetched once and will be cached forever after.

One more suggestion - make sure the images are small, i.e. the same size as they will be displayed. It is a huge waste of time to start with a large image and display it in a small image view, because the version of the image to be displayed must be calculated every single time.

Upvotes: 1

Related Questions