简体   繁体   中英

How to add a responsive UIPageViewController inside an UIStackView programmatically?

I am trying to place an UIPageViewController inside a UIView with no predetermined height. What I am trying to achieve is build a responsive In-app Message that has one or more components (buttons, texts, carousels, etc) that has its height determined by its children.

The modal window works fine with all components except the UIPageViewController. The UIView resizes based on whatever components I put into it, but not when I place an UIPageViewController. It seems like there are some constraints missing.

The general hierarchy is:

UIView (centered vertically on the screen with trailing and leading margins, no height)
|  UIStackView (inside this UIStackView I place any number of components I wish)
|  |  UILabel (example component)
|  |  UIButton (example component)
|  |  UIPageViewController (example component)
|  |  |  UIStackView (inside this UIStackView I place any other number of components)
|  |  |  |  UILabel (example component)
|  |  |  |  UIButton (example component)
…

The following log excerpt belongs to an In-app Message with these characteristics:

UIView (centered vertically on the screen with trailing and leading margins, no height)
|  UIStackView (leading and trailing anchors equal to parent, top and bottom anchors equal to children)
|  |  UIPageViewController (will display bellow the first slide content)
|  |  |  UIStackView
|  |  |  |  UIImageView
|  |  |  |  UILabel
|  |  |  |  UILabel

Logs:

(lldb) po [[0x7feeb7a78e30 superview ] recursiveDescription]
<UIView: 0x7feeb76c44f0; frame = (0 0; 334 0); layer = <CALayer: 0x600002afd1c0>>
   | <_UIPageViewControllerContentView: 0x7feeb7a78e30; frame = (0 0; 334 0); clipsToBounds = YES; opaque = NO; autoresize = W+H; layer = <CALayer: 0x600002ae1500>>
   |    | <_UIQueuingScrollView: 0x7feeafbc6800; frame = (0 0; 334 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x600002773600>; layer = <CALayer: 0x600002ae0e80>; contentOffset: {334, 0}; contentSize: {1002, 0}; adjustedContentInset: {0, -334, 0, -334}>
   |    |    | <UIView: 0x7feeb7a79b30; frame = (0 0; 334 0); layer = <CALayer: 0x600002ae04a0>>
   |    |    | <UIView: 0x7feeb7a79ca0; frame = (334 0; 334 0); layer = <CALayer: 0x600002ae0880>>
   |    |    |    | <UIView: 0x7feeb6f25990; frame = (0 0; 757 219.667); autoresize = W+H; layer = <CALayer: 0x600002aed700>>
   |    |    |    |    | <UIStackView: 0x7feeb6fb7a10; frame = (0 0; 757 219.667); layer = <CALayer: 0x600002aed8c0>>
   |    |    |    |    |    | <UIImageView: 0x7feeb76ceb70; frame = (0 0; 757 131.667); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600002aff560>>
   |    |    |    |    |    | <UILabel: 0x7feeb76ced40; frame = (0 141.667; 757 29.6667); text = 'This is the first slide t...'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600000bddb30>>
   |    |    |    |    |    | <UILabel: 0x7feeb6fb7c60; frame = (0 181.333; 757 18.3333); text = 'This is the description f...'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600000bca0d0>>
   |    |    | <UIView: 0x7feeb7a79e10; frame = (668 0; 334 0); layer = <CALayer: 0x600002ae0840>>
   |    | <UIPageControl: 0x7feeb76cb9b0; frame = (10 -25; 314 20); autoresize = W; userInteractionEnabled = NO; gestureRecognizers = <NSArray: 0x600002693180>; layer = <CALayer: 0x600002afdac0>>
   |    |    | <_UIPageControlContentView: 0x7feeb6fb4b10; frame = (111.667 -3; 91 26); layer = <CALayer: 0x600002aed0c0>>
   |    |    |    | <_UIPageControlIndicatorContentView: 0x7feeb6fb73b0; frame = (14.3333 0; 62.6667 26); autoresize = H; layer = <CALayer: 0x600002aec080>>
   |    |    |    |    | <_UIPageIndicatorView: 0x7feeb6f27570; frame = (0.166667 8.16667; 9.66667 9.66667); opaque = NO; userInteractionEnabled = NO; tintColor = UIExtendedGrayColorSpace 0.333333 1; layer = <CALayer: 0x600002ae5b00>>
   |    |    |    |    | <_UIPageIndicatorView: 0x7feeb0809630; frame = (17.5 8.16667; 9.66667 9.66667); opaque = NO; userInteractionEnabled = NO; tintColor = UIExtendedGrayColorSpace 0.666667 1; layer = <CALayer: 0x600002a01000>>
   |    |    |    |    | <_UIPageIndicatorView: 0x7feeb6cd34c0; frame = (35.5 8.16667; 9.66667 9.66667); opaque = NO; userInteractionEnabled = NO; tintColor = UIExtendedGrayColorSpace 0.666667 1; layer = <CALayer: 0x600002a01080>>
   |    |    |    |    | <_UIPageIndicatorView: 0x7feeb6fc6100; frame = (53.1667 8.16667; 9.66667 9.66667); opaque = NO; userInteractionEnabled = NO; tintColor = UIExtendedGrayColorSpace 0.666667 1; layer = <CALayer: 0x600002ae0140>>

Here are two examples. The first is a working In-app Message with no UIPageViewController. The second is what I am trying to achieve, but as a working example on Android.

Working In-app Message without UIPageViewController Working example of carousel on Android

Based on these logs, can anyone point me in the right direction of correcting the constraints? What happens is that the views higher in the hierarchy have 0 height, though their children have not. This results in a 0 height In-app Message.

Edit

I am adding the modal view as a subview to the main view and I add the UIPageViewController to the top controller when it is present in the hierarchy:

All is done via code. I am not adding a view controller, only a subview to my main view:

UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
if (keyWindow != nil) {
    [keyWindow addSubview:self.parentView]; // self.parentView = blackish background + vertically centered UIView (the actual modal view) + UIStackView for the components
    if (hasCarousel){
        UIViewController *topController = [UIApplication getPresentedViewController];
        if (topController != nil)
            [topController embedViewController:pageViewController inView:carouselWrapper]; // carouselWrapper = UIView containing the pageViewController that is added to the UIStackView above

    }
}

To provide an answer based on our comments...

A UIPageViewController loads and sets the view size of its "page" controllers. So, trying to do it the other way around - setting the Page View Controller's size to match the "page" size is a little tricky.

In addition, if we tried to do that for each "page," the frame would keep changing and would (likely) be a bad user experience.

We can , however, pre-load the pages and calculate the maximum page height to setup our controller.

Overview code would be:

MutableArray *pages = [NSMutableArray new];
// however we load and initialize our page controllers
[pages addObject:FirstPageVC()];
[pages addObject:SecondPageVC()];
// etc

CGFloat maxHeight = 0;

CGSize fitSize = CGSizeMake(_carouselWrapper.frame.size.width, UILayoutFittingCompressedSize.height);

for (UIViewController *vc in pages) {
    // get size of page view
    CGSize sz = [vc.view systemLayoutSizeFittingSize:fitSize withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityDefaultLow];
    maxHeight = MAX(sz.height, maxHeight);
}

// if using the built-in PageControl
//  add height of PageControl + 1.5 (control is shown 1.5-pts below the view)
UIPageControl *c = [UIPageControl new];
maxHeight += [c sizeForNumberOfPages:1].height + 1.5;

// here we can set the height of the view frame for our PageViewController

I played around with it a bit and put a working example project here: https://github.com/DonMag/DynamicPGVC

Basic idea of what you showed, although I made the "parentView" (I call it an OverlayView) a separate view subclass to keep a lot of the activity out of the main view controller.

Here's how it looks:

在此处输入图片说明

Tapping "Label" button (the thin rectangle shows the frame of the "carouselView"):

在此处输入图片说明

Tapping "Image" button:

在此处输入图片说明

Tapping "Page View Controller" button - first "page" (The "pages" are simple - vertical stack view with title and content labels):

在此处输入图片说明

and second page, showing the max height of the content:

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM