UIPageViewController with Scroll Transition Style automatically adds Safe Area insets for iPhone X
Asked Answered
C

3

6

I'm developing image gallery like slider using UIPageViewController and I'm having troubles with UIPageViewController automatic insets in Scroll transition style mode.

Here is my layout:

  1. UIViewController with UIContainerView (magenta background)
  2. UIPageViewController linked to the container (from #1)
  3. List of dynamically created view UIViewController(s) within the page controller (from #2), full width-height views (1. orange, 2. red, 3. green)

enter image description here

It used to work fine for a long time and continue to work with iOS 11 unless it's rendered on iPhone X device with safe area:

enter image description here

I've checked a lot of various options and was able to confirm that it's related specifically to the Scroll mode of the Page Controller. If I switch to PageCurl transition style - it works as expected (full height):

enter image description here

The Page Controller doesn't expose a lot of options to control this behavior for the scroll mode and I wasn't able to "hack" it as well by searching the controls tree and modifying various insets and frame and contentSize related properties. What I can clearly see is that once view controller is created, my scroll view contentSize and frame is 34px smaller then the container frame

> view.frame
{{X=0,Y=0,Width=375,Height=732}}
    Bottom: 732
    Height: 732
    IsEmpty: false
    Left: 0
    Location: {{X=0, Y=0}}
    Right: 375
    Size: {{Width=375, Height=732}}
    Top: 0
    Width: 375
    X: 0
    Y: 0

> scroll.frame
{{X=-5,Y=0,Width=385,Height=698}}
    Bottom: 698
    Height: 698
    IsEmpty: false
    Left: -5
    Location: {{X=-5, Y=0}}
    Right: 380
    Size: {{Width=385, Height=698}}
    Top: 0
    Width: 385
    X: -5
    Y: 0

> scroll.contentSize
{{Width=1155, Height=698}}
    Height: 698
    IsEmpty: false
    Width: 1155

I've also set up my autolayout constraints to be linked to superview rather than safe area:

enter image description here

Here is my code for the Home Controller and all the rest is set in a storyboard (alert: C# Xamarin syntax)

private List<UIViewController> viewControllers;

public HomePageViewController (IntPtr handle) : base ( handle)
{
}

public override void ViewDidLoad()
{
    base.ViewDidLoad();
    var child1 = new UIViewController();
    child1.View.BackgroundColor = UIColor.Orange;
    var child2 = new UIViewController();
    child2.View.BackgroundColor = UIColor.Red;
    var child3 = new UIViewController();
    child3.View.BackgroundColor = UIColor.Green;

    this.viewControllers = new List<UIViewController>
    {
        child1,
        child2,
        child3,
    };

    this.SetViewControllers(new UIViewController[] { child1 }, UIPageViewControllerNavigationDirection.Forward, false, null);

    this.GetNextViewController = (c, r) =>
    {
        var current = this.viewControllers.IndexOf(this.ViewControllers[0]);
        if (current >= this.viewControllers.Count - 1)
            return null;

        return this.viewControllers[current + 1];
    };

    this.GetPreviousViewController = (c, r) =>
    {
        var current = this.viewControllers.IndexOf(this.ViewControllers[0]);
        if (current <= 0)
            return null;

        return this.viewControllers[current - 1];
    };
}

How can I force my children view controllers to have full height (equals to the frame height of the parent container)?

Cell answered 30/1, 2018 at 3:28 Comment(0)
B
0

I think you can solve this issue using code and custom layout. I mean create your UIPageViewController and insert its view to your UIViewController in code not on storyboard. I think you should override UIViewController.viewDidLayoutSubviews() and set your rects "manually" (at least the one of the UIPageViewController.) Well, when you do it in code, sometimes you even don't need to override UIViewController.viewDidLayoutSubviews() because the template by Apple itself didn't do this. I think because any created view has translatesAutoresizingMaskIntoConstraints = true. So you can also follow this approach.

There is an example when you create a new project and state it is a page based app.

Here is the template if you want (WARNING: This is a part of a template by Apple itself)

    var pageViewController: UIPageViewController?


override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    // Configure the page view controller and add it as a child view controller.
    self.pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
    self.pageViewController!.delegate = self

    let startingViewController: DataViewController = self.modelController.viewControllerAtIndex(0, storyboard: self.storyboard!)!
    let viewControllers = [startingViewController]
    self.pageViewController!.setViewControllers(viewControllers, direction: .forward, animated: false, completion: {done in })

    self.pageViewController!.dataSource = self.modelController

    self.addChildViewController(self.pageViewController!)
    self.view.addSubview(self.pageViewController!.view)

    // Set the page view controller's bounds using an inset rect so that self's view is visible around the edges of the pages.
    var pageViewRect = self.view.bounds
    if UIDevice.current.userInterfaceIdiom == .pad {
        pageViewRect = pageViewRect.insetBy(dx: 40.0, dy: 40.0)
    }
    self.pageViewController!.view.frame = pageViewRect

    self.pageViewController!.didMove(toParentViewController: self)
}

You can extend this functionality by extending

Beaudette answered 9/2, 2018 at 4:21 Comment(0)
H
0

I had a similar issue that only happened in the X sizes and after hours of trails and errors I got it fixed.

I am not sure if it's applicable for you or not but the way I have my page view controller VCs is that each VC has an image filling its background. I have 3 pages. Scrolling for the first time looked normal but when I would reverse scroll from page 2 to 1 or from 1 to 0, page 1's image would show around 40 pixels from the side when it should be completely hidden (similar to your screenshots).

So to fix it I had to either set the images to Aspect Fit or clips to bounds = true. I used the latter because it worked better for the UI.

Hannie answered 23/9, 2018 at 12:5 Comment(0)
C
0

I achieved to have my UIPageViewController display with full screen height with the following code :

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    guard let contentScrollView = view.subviews.first(where: { $0 is UIScrollView }) else { return }
    contentScrollView.frame.size.height = view.frame.height
}
Cissie answered 1/2, 2022 at 16:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.