Content pushed down in a UIPageViewController with UINavigationController

17,760

Solution 1

So I'm adding another answer after further development and I finally think I figured out what's going on. Seems as though in iOS7, UIPageViewController has its own UIScrollView. Because of this, you have to set automaticallyAdjustsScrollViewInsets to false. Here's my viewDidLoad now:

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.automaticallyAdjustsScrollViewInsets = false;

    DetailViewController *detail = [[DetailViewController alloc] init];
    [self setViewControllers:@[detail]
                   direction:UIPageViewControllerNavigationDirectionForward
                    animated:false
                  completion:nil];
}

No need to put anything in viewWillLayoutSubviews (as one of my previous answers suggested).

Solution 2

This is definitely being caused by automaticallyAdjustsScrollViewInsets, as other posters (including @djibouti33). However, this property is strange in two ways:

  1. It must be set on a UINavigationController. If you set it on a child controller that's managed by a UINavigationController, it won't have any effect. 1
  2. It only applies when a scroll view is at index zero in a controller's subviews. 2

These two caveats should explain the intermittent problems experienced by others in the thread.

TLDR: A workaround that I went with is adding a dummy view to the UIPageViewController at index zero, to avoid the setting applying to the scrollView within the page controller, like this:

pageViewController.view.insertSubview(UIView(), atIndex: 0) // swift

[pageViewController.view insertSubview: [UIView new] atIndex: 0]; // obj-c

Better would be to set the contentInset on the scroll view yourself, but unfortunately the UIPageViewController doesn't expose the scroll view.

Solution 3

Just uncheck Under Top Bars for both: UIPageViewController and your custom PageContentViewController:

enter image description here

Solution 4

I had a similar problem but none of the solutions here worked. My problem was that whenever I would scroll to the next page, the content would jump down, ending in the correct position, but starting 20 pixels too high (clearly something to do with the status bar). My container VC was not a nav VC. After pulling my hair out for a while, the solution that ended up working for me was just to make sure that none of the constraints in my content VC were connected to the top layout guide. This may or may not be feasible in your case, but in mine it was, and it was the only thing that solved the content jump. Also very curiously, this problem only manifested when the transition style was set to scroll. Just changing it to page curl made the issue disappear. But I needed scroll. Hope this helps someone else.

Solution 5

My original answer solved the problem for the time being, but after a while the same problem came back to bite me.

Using the following viewController hierarchy:

-- UINavigationController
  -- MyPageViewController
    -- MyDetailViewController

Here's what I did to solve it:

In MyPageViewController.m

@interface MyPageViewController () <delegates>
  @property (strong) MyDetailViewController *initialViewController;
@end

- (void)viewDidLoad
{
    ...

    // set this once, because we're going to use it in viewWillLayoutSubviews,
    // which gets called multiple times
    self.initialViewController = [MyDetailViewController new];
}

// the problem seemed to stem from the fact that a pageViewController couldn't
// properly lay out it's child view controller initially if it contained a 
// scroll view. by the time we're in layoutSubviews, pageViewController seems to
// have gotten it's bearings and everything is laid out just fine.
- (void)viewWillLayoutSubviews
{
    [self setViewControllers:@[self.initialViewController]
                   direction:UIPageViewControllerNavigationDirectionForward
                    animated:false
                  completion:nil];
}
Share:
17,760
djibouti33
Author by

djibouti33

Updated on June 06, 2022

Comments

  • djibouti33
    djibouti33 almost 2 years

    UPDATE 2

    I've been running and testing my app in the iOS Simulator using a 4-inch device. If I run using a 3.5-inch device the label doesn't jump. In my .xib, under Simulated Metrics, I have it set as Retina 4-inch Full Screen. Any idea why I'm only seeing this problem on a 4-inch device?

    UPDATE 1

    In IB, if I choose "Navigation Bar" in Simulated Metrics, my label still jumps. The only way I can get my label to render correctly on the first screen is to not set a navigation controller as my window's root view controller.

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    My window's rootViewController is being set to a UINavigationController whose rootViewController has a UIPageViewController embedded.

    When my app loads, the initial view is presented with it's content pushed down a bit, roughly the same size as a navigation bar. When I scroll the pageViewController, the content jumps up to where it was placed in the nib, and all other viewControllers loaded by the pageViewController are fine.

    uipageviewcontroller with navigationcontroller

    In my appDelegate:

    self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[ContainerViewController new]];
    

    In ContainerViewController:

    - (void)viewDidLoad {
    
        [super viewDidLoad];
    
        self.pvc = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
                                                   navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
                                                                 options:nil];
        self.pvc.dataSource = self;
        self.pvc.delegate = self;
        DetailViewController *detail = [DetailViewController new];
        [self.pvc setViewControllers:@[detail]
                           direction:UIPageViewControllerNavigationDirectionForward
                            animated:false
                          completion:nil];
    
        [self addChildViewController:self.pvc];
        [self.view addSubview:self.pvc.view];
        [self.pvc didMoveToParentViewController:self];
    }