Reputation: 42690
Currently, I have a UIViewController
, with its top component consists of a horizontal UICollectionView
(MenuTabsView.swift)
Now, I would like to add a UIPageViewController
, just below the MenuTabsView.
I have tried the following few approaches.
func presentPageVCOnView() {
self.pageController = storyboard?.instantiateViewController(withIdentifier: "PageControllerVC") as! PageControllerVC
self.pageController.view.frame = CGRect.init(x: 0, y: menuBarView.frame.maxY, width: self.view.frame.width, height: self.view.frame.height - menuBarView.frame.maxY)
self.addChildViewController(self.pageController)
self.view.addSubview(self.pageController.view)
self.pageController.didMove(toParentViewController: self)
}
Here's the outcome.
From 1st glance, it seems that UIPageViewController
's view need to offset by Y status bar distance. (But why?)
func presentPageVCOnView() {
let statusBarHeight = CGFloat(20.0)
self.pageController = storyboard?.instantiateViewController(withIdentifier: "PageControllerVC") as! PageControllerVC
self.pageController.view.frame = CGRect.init(x: 0, y: menuBarView.frame.maxY + statusBarHeight, width: self.view.frame.width, height: self.view.frame.height - menuBarView.frame.maxY - statusBarHeight)
self.addChildViewController(self.pageController)
self.view.addSubview(self.pageController.view)
self.pageController.didMove(toParentViewController: self)
}
Now, it looks way better.
But, I don't feel comfortable, on why we need to manually consider status bar height, during programatically way. I was thinking, maybe I can add a ContainerView
to UIViewController
, and "attach" the UIPageViewController
's view to it?
(I am not sure why during adding Container View to storyboard, an additional UIViewController
will be added along. Anyhow, I just manually delete the additional UIViewController
)
Then, I use the following code to "attach" the UIPageViewController
's view to new container view.
func presentPageVCOnView() {
self.pageController = storyboard?.instantiateViewController(withIdentifier: "PageControllerVC") as! PageControllerVC
self.pageController.view.frame = containerView.frame
self.addChildViewController(self.pageController)
self.view.addSubview(self.pageController.view)
self.pageController.didMove(toParentViewController: self)
}
But, the outcome is not what as expected. Y offset still happen!!!
I try to make sure, there are space of 20, between the top component MenuTabsViews
and UIPageViewController
's view.
I was wondering, is there any good practice/ solution, to ensure we can add UIPageViewController
's view below another component, without affecting by status bar height?
Upvotes: 0
Views: 398
Reputation: 77442
You can do this all without any code -- it just takes an understanding of how UIContainerView
works.
There's no real UIContainerView
class... it is an automated way of adding a child view controller via Storyboard / Interface Builder. When you add a UIContainerView
, IB automatically creates a "default" view controller connected to the container view with an Embed
segue. You can change that default controller.
Here's step-by-step (images are large, so you'll probably want to click them to see the details)...
Start with a fresh UIViewController
:
Add your "Menu Bar View" - I have it constrained Top/Leading/Trailing to safe-area, Height of 60:
Drag a UIContainerView
onto the view - note that it creates a default view controller at the current size of the container view. Also note that it shows a segue. If you inspect that segue, you'll see it is an Embed
segue:
Constrain the Top of the container view to the Bottom of your Menu Bar View, and Leading/Trailing/Bottom to safe-area. Notice that the size of the embedded view controller automatically takes the new size of the container view:
Select that default controller... and delete it:
Drag a new UIPageViewController
onto your Storyboard and set its Custom Class to PageControllerVC
:
Now, Ctrl-Click-Drag from the Container view to the newly added page view controller. When you release the mouse button, select Embed
from the popup:
You now have an Embed
segue from the container view to your page view controller. Notice that it automatically adjusted its size to match the container view size:
Since the Menu Bar View top is constrained to the safe-area, it will behave as expected.
Since the container view top is constrained to the bottom of the Menu Bar View, it will stay there, and should give you what you want.
No Code Needed :)
Edit
The most likely reason you ran into trouble with loading via code is with you frame setting.
If you try to set frames in viewDidLoad()
, for example, auto-layout has not configured the rest of the view hierarchy... so framing will not be what you expect.
You're much better off using auto-layout / constraints, rather than setting explicit frames anyway.
Here is how I would do it from code (assumes you have your "Menu Bar View" connected via @IBOutlet
):
class ViewController: UIViewController {
@IBOutlet var menuBarView: UIView!
var pageControllerVC: PageControllerVC?
override func viewDidLoad() {
super.viewDidLoad()
guard let vc = storyboard?.instantiateViewController(withIdentifier: "PageControllerVC") as? PageControllerVC else {
fatalError("Could not instantiate PageControllerVC!!!")
}
guard let v = vc.view else {
fatalError("loaded PageControllerVC had no view ????")
}
addChild(vc)
view.addSubview(v)
v.translatesAutoresizingMaskIntoConstraints = false
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
v.topAnchor.constraint(equalTo: menuBarView.bottomAnchor),
v.leadingAnchor.constraint(equalTo: g.leadingAnchor),
v.trailingAnchor.constraint(equalTo: g.trailingAnchor),
v.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
vc.didMove(toParent: self)
self.pageControllerVC = vc
}
}
Upvotes: 1
Reputation: 422
You should remove safeArea pinning for pageVC. Safe area includes status bar and iPhone 11+ top space
tabBar.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor)
// to this
tabBar.topAnchor.constraint(equalTo: self.view.topAnchor)
And in storyboards change Safe Area to SuperView
Upvotes: 0