60

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];
}
Bartłomiej Semańczyk
  • 52,820
  • 43
  • 206
  • 318
djibouti33
  • 11,832
  • 9
  • 75
  • 111
  • while designing the interface (.xib file) in the "attribute inspector" tab select top bar as "Navigation Bar" from drop down and then put your label accordingly in this way the label will be as it's original place. – Gyanendra Singh Aug 13 '13 at 07:29
  • That's actually what I'm already doing. – djibouti33 Aug 13 '13 at 09:22

15 Answers15

83

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).

djibouti33
  • 11,832
  • 9
  • 75
  • 111
  • 5
    See my caveat below that `automaticallyAdjustsScrollViewInsets` won't do anything when applied to a child view controller, such as a view controller inside of a `UINavigationViewController` or a `UITabBarController` – John Gibb Nov 17 '14 at 20:21
  • It Worked! Was struggling a lot with the issue, Thanks! – Mago Nicolas Palacios Apr 13 '17 at 19:45
22

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.

Community
  • 1
  • 1
John Gibb
  • 10,043
  • 2
  • 35
  • 46
12

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

enter image description here

Bartłomiej Semańczyk
  • 52,820
  • 43
  • 206
  • 318
4

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];
}
djibouti33
  • 11,832
  • 9
  • 75
  • 111
  • 1
    This is the one that ended up working for me - self.automaticallyAdjustsScrollViewInsets = false; didn't seem to do anything. – Adama Sep 11 '14 at 15:53
  • @djibouti33: I think I figured out what was going on, see http://stackoverflow.com/a/26981213/99046 – John Gibb Nov 17 '14 at 20:19
  • this worked for me - especially with a hierarchy that has detail view controllers embedded in their own nav controllers – Terry Bu Jan 19 '15 at 20:25
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.

Danny
  • 399
  • 3
  • 9
  • same here, only happens when transition style is scroll – user1139921 Jul 30 '15 at 17:00
  • I ended up adapting this hack: https://knuspermagier.de/2014-fixing-uipageviewcontrollers-top-layout-guide-problems.html so that I could re-instate my top layout guide constraint (not having it was causing problems on devices with different sizes) – Danny Jul 30 '15 at 21:21
  • Perfect! That's all I needed! I had the same problem with my leading and trailing constraints to their respective layout guides. I used this to help. http://iphonedev.tv/blog/2014/2/12/auto-layout-bug-with-the-top-layout-guide-for-xcode-5 – jervine10 Aug 26 '15 at 19:10
3

I have the same problem. I solve it by putting setViewControllers for the first page in UIPageViewController's viewDidLoad instead of setting it when I make a instance of UIPageViewController. Also, I need to set automaticallyAdjustsScrollViewInsets to NO.

howa
  • 104
  • 3
3

Try unchecking these 2 options on your storyboard

enter image description here

apinho
  • 2,045
  • 3
  • 20
  • 37
1

Try to select PageViewController in storyboard and uncheck "Under Bottom Bars" and "Under Opaque Bars" in Attributes Inspector.

acidbeast
  • 43
  • 8
0

Initially my view controller hierarchy looked like this:

-- UINavigationController
  -- MyContainerViewController
    -- UIPageViewController
      -- MyDetailViewController

I set it up this way so MyContainerViewController could manage a toolbar. I narrowed my problem down to MyContainerViewController, and then it occurred to me that I don't even need it if I subclass UIPageViewController. Now my hierarchy looks like this:

-- UINavigationController
  -- MyPageViewController
    -- MyDetailViewController

MyPageViewController manages it's toolbar, and everything works as expected, both on a 4-inch and 3.5-inch device.

djibouti33
  • 11,832
  • 9
  • 75
  • 111
  • 1
    For me, adopting this hierarchy is not enough. Putting self.edgesForExtendedLayout = UIRectEdgeNone; in the viewDidLoad of MyPageViewController solved the problem. – Bo. Sep 21 '13 at 15:54
  • 1
    Hey Bo, check out my new answer, which I chose as the accepted answer. Turns out that adopting this new hierarchy didn't quite solve it for me, as the problem came back again later in development. I also wanted my scrollviews to scroll underneath the blurred navigation bar, so I couldn't use your advice regarding edgesForExtendedLayout. My new answer seems to be solid though. – djibouti33 Sep 27 '13 at 21:14
0

As stated by "Bo:": Putting self.edgesForExtendedLayout = UIRectEdgeNone; in the viewDidLoad of MyPageViewController solved the problem. – Bo

osxdirk
  • 548
  • 6
  • 11
0

this is my first time posting on stack overflow, but I have searching for a solution to this problem for over a week now.

Here is a solution I came up with, I hope this works for anyone else with the same issue.

I'm not sure how you are initializing your frame for your detail view controller, but I am going to assume you might use: self.view.frame.size.height;

try using: self.view.frame.size.height -= self.navigationController.navigationBar.bounds.size.height;

Hope this helps

CalebDavis
  • 31
  • 4
0

I'm seeing the same issue as described by @Danny on iOS 9. I tried updating all my constraints to that they are not constrained to the margins, but it didn't fix the issue. I ended up having to adopt a hack similar to this one as follows;

  • For each content page to be displayed in the UIPageViewController, find the top-most constraint, the one between the Top of a view and the bottom of the top layout guide, and add an outlet for it to the view controller.
  • In each view controller with such an outlet, add another property for the preferred top distance. The two outlets look like this (in Swift):

    @IBOutlet weak var topGuideConstraint: NSLayoutConstraint!
    var topDistance: CGFloat!
    
  • In viewDidLoad(), set topDistance to the value assigned to the constraint in the storyboard:

    override func viewDidLoad() {
        super.viewDidLoad()
        topDistance = topGuideConstraint.constant
    }
    
  • In viewWillLayoutSubviews(), make sure the constraint has the proper value, adjusting for the height of the status bar when the topLayoutGuide.length is zero, which seems to be the case during the transition, but not once it's complete:

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        topGuideConstraint.constant = topDistance + (
            topLayoutGuide.length == 0
                ? UIApplication.sharedApplication().statusBarFrame.size.height
                : 0
        )
    }
    

Repeat for every content view controller displayed in the UIPageViewController. Adjust the offset as appropriate if you're also displaying a UINavigation bar.

This is an unfortunate hack, and I hate having to do it, but after many hours trying different things, I'm at least happy to have something that works so I can move on.

Community
  • 1
  • 1
theory
  • 8,210
  • 8
  • 50
  • 115
0

As @djibouti33 already posted:

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

By waiting for layoutSubViews to load before setting any viewControllers to the UIPageViewController was the only thing that worked for me.

override func viewDidLoad() {
    super.viewDidLoad()
    self.pageViewController = self.storyboard?.instantiateViewController(withIdentifier: "yourPageViewController") as? UIPageViewController       
    self.pageViewController?.dataSource = self

    pageViewController?.automaticallyAdjustsScrollViewInsets = false    
    self.pageViewController?.view.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.height)       
    self.addChildViewController(self.pageViewController!)        
    self.view.addSubview((self.pageViewController?.view)!)       
    self.pageViewController?.didMove(toParentViewController: self)
}


override func viewDidLayoutSubviews() {
    let startVC = self.viewControllerAtIndex(index: 0) as infoDataViewController     
    let viewControllers = NSArray(object: startVC)    
    self.pageViewController?.setViewControllers(viewControllers as? [UIViewController], direction: .forward, animated: true, completion: nil)
}
QitVision
  • 61
  • 7
0

None of above worked for me

Here I found the solution

var pageMenu : CAPSPageMenu?

Instead of adding like this

self.view.addSubview(pageMenu!.view)

Add your CAPSPageMenu like below

addChildViewController(pageMenu!) 
self.view.addSubview(pageMenu!.view) 
pageMenu!.didMove(toParentViewController: self)

Reference: iOS Swift : SlidingMenuView wrong Position after presenting imagePicker

Happy Coding!

Community
  • 1
  • 1
Sanju
  • 1,108
  • 11
  • 25
0

Swift's version of djibouti33's answer (excluding the line that is not part of the problem resolution)

Swift 3.0

override func viewDidLoad() {
    super.viewDidLoad()
    self.automaticallyAdjustsScrollViewInsets = false
}
Kqtr
  • 5,397
  • 3
  • 17
  • 27