I was trying to create an onboarding UI using UIKit and Storyboard. I had a requirement where some parts needed to be static / sticky and some parts had to scroll horizontally as the user swiped on the onboarding.
This should give you a better idea:
To achieve the above, I used the following view hierarchy set up in storyboard
UIViewController
- Skip Button
- Container View containing a UIPageViewController to swipe horizontally
- UIViewController
- UIScrollView (This is another scroll view, not the page view controller's)
- UIStackView
- UIImage (big red cross)
- UILabel
- UILabel
- UIStackView
- UIScrollView (This is another scroll view, not the page view controller's)
- UIViewController
- UIStackView
- Page Indicator
- Subscribe button
- Login button
While this works fine in a normal set up, I have the need to support dynamic / large text which the user can update from settings / control panel.
The issue I have now is that the content grows within the scrollview inside the UIPageViewController as shown
The white box above is not part of the UI but just me trying to show that the vertical scrolling is restricted to that area.
I understand that because of my set up, this is the correct behavior.
My question / objective: What would be a better layout structure so that the whole view grows when larger text fonts are activated such that the view scrolls as a whole rather than the scrolling being restricted to that small portion of the screen which would be mean the page indicator and bottom buttons going below the screen for larger text sizes.
Please bear in mind that only the image, title and description should be horizontally scrollable.
I tried to bring the scrollview and stackview out of the PageViewController to set up something like this:
UIViewController
- UIScrollView
- UIStackView
- Skip Button
- Container View containing a UIPageViewController to swipe horizontally
- UIViewController
- UIStackView
- UIImage (big red cross)
- UILabel
- UILabel
- UIStackView
- UIViewController
- UIStackView
- Page Indicator
- Subscribe button
- Login button
- UIStackView
However, since I'm using autolayout, I get an error in the storyboard that the page view controller's height couldn't be determined.
What would be the right way to set this up in storyboard or programatically if not possible via storyboard ?
I'm open to switching to tableview or collectionview if that makes life easier.


After comment discussion, I think I understand your ultimate goal.
You're creating an "Onboarding" UI, so I'll make a few assumptions...
Therefore, we shouldn't have to worry about memory issues if the "pages" are not dynamically allocated.
A
UIPageViewControlleris a nice component -- but, it does a LOT "behind-the-scenes." In particular, it sets the "page frames" to match its frame. In your case, you want its frame to grow in height to match the pages height.That probably could be done, but it will be much easier to NOT use a page view controller.
Instead, let's use a standard
UIScrollView.First, if we embed a
UILabelin aUIView, and constrain the Height of the view equal to the Height of the label (assuming we have 8-point constraints from the table to the view):the Yellow view will grow in height as the label grows:
We can do the same thing with a
UIScrollView(red-dashed outline is the scroll view frame):The scroll view frame Height grows with the label height, and we can still scroll horizontally but not vertically.
So, let's start by putting the "pages" into a horizontal stack view:
and then embed that stack view in a scroll view.
If we constrain each page Width to the Width of the scroll view, and set a Height constraint on the scroll view, we'll get this (thick black dashed outline is the scroll view frame):
So far, nothing special.
However, if the page heights change - such as using
.adjustsFontForContentSizeCategory - trueon the labels - without doing anything else - we get this:which results in vertical as well as horizontal scrolling -- as expected.
For the target layout, though, we want the scroll view's Height to grow to match the new page Height.
Let's constrain the scroll view's Height to the stack view's Height:
Now, as our "pages" grow in height, the scroll view frame will also grow:
Once we embed our
pageScrollViewin an "outer" scroll view, and constrain it relative to the other "outer" UI elements, we'll reach the target layout.Solid black outline is the "iPhone" frame; yellow long-dash outline is the "outer" scroll view frame; white dash outline is the "page" scroll view frame:
As the "page" height grows...
we'll be able to scroll the entire "outer view" vertically...
and continue to scroll the "pages" horizontally.
The end result - (scaled way down so it will post here):
And, finally, some sample code. Everything is done via code, so no complicated Storyboard designing or
@IBOutlet/@IBActionconnections needed:Simple struct for the "page" data:
Sample "page" view controller - which we'll add as children and grab the views. ImageView, Title and Description labels:
Sample "onboarding" view controller - uses everything discussed above:
Sample Dev Mode "onboarding" view controller - subclass of
OnboardingVCthe colorizes and outlines the UI elements. It will produce the screen-caps shown above (when run on an iPad) to make it easier to see what's going on:Dashed Border View - used by the "Dev Mode" class:
Important Notes
I also posted a project with all the above code here: https://github.com/DonMag/PageViewApproach
Edit - a little more explanation...
Big benefits of using a
UIPageViewControllerorUICollectionView(whether using flow, custom or compositional layout) is memory management ... you could have 100s of "pages" and not have to worry.With this Layout goal, though, the dynamic height is always going to be the issue... because both of those classes are also designed to "layout their pages / cells" based on the frame we set for the PageViewController or CollectionView.
Consider 5 "pages" that look like this:
In a page view controller or a collection view, we can't set the frame height (yellow rect) because we don't know the "max height needed" until Page 4 is instantiated.
We could calculate the max height on load, for example:
Or, if we have "cached" the page views in memory:
And because you're using Dynamic Type, we'd also need to call that on
UITraitCollection.preferredContentSizeCategorychange and update the frame height.Personally, I would go with the scrollview approach, and simply let auto-layout handle it for me.