Search code examples
iosobjective-cuitableviewuisearchdisplaycontrolleruipageviewcontroller

UIPageViewController prevents my table view from scrolling to top when tapping the status bar


I searched before posting this question, but couldn't find anything.

I'm having a huge issue. I have a scrolling type UIPageViewController as the basis of my app, with 3 view controllers.

One of the view controller's (listTableView) has a table view and a search display controller.

The problem is, I cannot scroll to the top of the table view when tapping on the status bar like a normal table view. I believe the UIPageViewController is interfering with this, but I have no idea how to go about fixing it, but I know that I need to for my app to not feel broken.

I appreciate any help offered.

I know someone will ask for code even though it's irrelvant in this case but here it is for creating the UIPageViewController:

#import "MainViewController.h"

@interface MainViewController ()

@property (nonatomic, strong) UIPageViewController *pageViewController;
@property (nonatomic, strong) NSArray *contentViewControllers;

@end

@implementation MainViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"PageView"];
    self.pageViewController.dataSource = self;

    UIViewController *settings = [self.storyboard instantiateViewControllerWithIdentifier:@"Settings"];

    UIViewController *listTableView = [self.storyboard instantiateViewControllerWithIdentifier:@"List"];

    UIViewController *first = [self.storyboard instantiateViewControllerWithIdentifier:@"First"];

    self.contentViewControllers = [NSArray arrayWithObjects:settings,listTableView,first,nil];

    [self.pageViewController setViewControllers:@[first] direction:UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];

    [self addChildViewController:self.pageViewController];
    [self.view addSubview:self.pageViewController.view];
    [self.pageViewController didMoveToParentViewController:self];

    self.view.backgroundColor = [UIColor colorWithRed:(248.0/255.0) green:(248.0/255.0) blue:(248.0/255.0) alpha:1.0];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
  viewControllerBeforeViewController:(UIViewController *)viewController {

    NSUInteger index = [self.contentViewControllers indexOfObject:viewController];

    if (index == 0) {
        return nil;
    }

    return self.contentViewControllers[index - 1];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
   viewControllerAfterViewController:(UIViewController *)viewController {

    NSUInteger index = [self.contentViewControllers indexOfObject:viewController];

    if (index >= self.contentViewControllers.count - 1) {
        return nil;
    }

    return self.contentViewControllers[index + 1];
}

Solution

  • Solution

    After chat, we discovered something interesting. The page view controller keeps other view controllers' views inside the view hierarchy, so they also capture the scrollsToTop behavior and interfere. This means that you need to disable scrollsToTop for each scrollable view inside viewWillDisappear: of the disappearing view controllers (and enable again on viewWillAppear:).


    Original Investigation

    The quick and easy way: The scrollview is the only subview of the UIPageViewController's view:

    self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
    

    Inspecting in debugger:

    (lldb) po [self.pageViewController.view recursiveDescription]
    <_UIPageViewControllerContentView: 0x8d7c390; frame = (0 0; 320 480); clipsToBounds = YES; opaque = NO; autoresize = W+H; layer = <CALayer: 0x8d7c4a0>>
       | <_UIQueuingScrollView: 0xa912800; frame = (0 0; 320 480); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x8d7cc90>; layer = <CALayer: 0x8d7c7e0>; contentOffset: {320, 0}>
       |    | <UIView: 0x8d7da00; frame = (0 0; 320 480); layer = <CALayer: 0x8d7da60>>
       |    | <UIView: 0x8d7dab0; frame = (320 0; 320 480); layer = <CALayer: 0x8d7db10>>
       |    | <UIView: 0x8d7db40; frame = (640 0; 320 480); layer = <CALayer: 0x8d7dba0>>
    

    You could use several methods to reach that scrollview. Easiest is to iterate self.pageViewController.view.subviews and find the one that is a subclass of UIScrollView. Since it is the only subview, your loop will end after one iteration.

    Is this solution optimal? No. Is it error prone? In theory, sure. Is it likely to change? Not likely, as the view hierarchy is pretty logical. It at least gives you a quick fix, instead of having to deal with changing the entire app structure due to a small oversight by Apple (providing the user with access to the scrollview).

    You should open a feature request with Apple over at https://bugreport.apple.com.