如何以编程方式在 UIStackView 中添加响应式 UIPageViewController?
How to add a responsive UIPageViewController inside an UIStackView programmatically?
我正在尝试将 UIPageViewController 放置在没有预定高度的 UIView 中。我想要实现的是构建一个具有一个或多个组件(按钮、文本、旋转木马等)的响应式应用内消息,其高度由其子项决定。
模态 window 适用于除 UIPageViewController 之外的所有组件。 UIView 会根据我放入其中的任何组件调整大小,但不会在我放置 UIPageViewController 时调整。似乎缺少一些约束。
一般层次结构是:
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)
…
以下日志摘录属于具有以下特征的应用内消息:
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
日志:
(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>>
这里有两个例子。第一个是没有 UIPageViewController 的应用内消息。第二个是我想要实现的,但作为 Android.
上的一个工作示例
Working In-app Message without UIPageViewController
Working example of carousel on Android
根据这些日志,任何人都可以指出我纠正约束的正确方向吗?发生的情况是层次结构中较高的视图高度为 0,尽管它们的子视图没有。这会导致高度为 0 的应用内消息。
编辑
我将模态视图作为子视图添加到主视图,并且当 UIPageViewController 出现在层次结构中时,我将它添加到顶层控制器:
一切都是通过代码完成的。我没有添加视图控制器,只是向我的主视图添加了一个子视图:
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
}
}
根据我们的评论提供答案...
A UIPageViewController
加载并设置其“页面”控制器的视图大小。因此,尝试以相反的方式进行 - 设置页面视图控制器的大小以匹配“页面”大小有点棘手。
此外,如果我们尝试为每个“页面”执行此操作,框架将不断变化并且(可能)会是糟糕的用户体验。
我们可以,但是,pre-load 页面并计算最大页面高度来设置我们的控制器。
概览代码为:
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
我试了一下,在这里放了一个工作示例项目:https://github.com/DonMag/DynamicPGVC
虽然我将“parentView”(我称之为 OverlayView)作为一个单独的视图子类,以便将很多 activity 保留在主视图控制器之外,但您展示的内容的基本概念。
外观如下:
点击“标签”按钮(细矩形显示“carouselView”的框架):
点击“图像”按钮:
点击“页面视图控制器”按钮 - 第一个“页面”(“页面”很简单 - 带有标题和内容标签的垂直堆栈视图):
第二页,显示内容的最大高度:
我正在尝试将 UIPageViewController 放置在没有预定高度的 UIView 中。我想要实现的是构建一个具有一个或多个组件(按钮、文本、旋转木马等)的响应式应用内消息,其高度由其子项决定。
模态 window 适用于除 UIPageViewController 之外的所有组件。 UIView 会根据我放入其中的任何组件调整大小,但不会在我放置 UIPageViewController 时调整。似乎缺少一些约束。
一般层次结构是:
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)
…
以下日志摘录属于具有以下特征的应用内消息:
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
日志:
(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>>
这里有两个例子。第一个是没有 UIPageViewController 的应用内消息。第二个是我想要实现的,但作为 Android.
上的一个工作示例Working In-app Message without UIPageViewController Working example of carousel on Android
根据这些日志,任何人都可以指出我纠正约束的正确方向吗?发生的情况是层次结构中较高的视图高度为 0,尽管它们的子视图没有。这会导致高度为 0 的应用内消息。
编辑
我将模态视图作为子视图添加到主视图,并且当 UIPageViewController 出现在层次结构中时,我将它添加到顶层控制器:
一切都是通过代码完成的。我没有添加视图控制器,只是向我的主视图添加了一个子视图:
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
}
}
根据我们的评论提供答案...
A UIPageViewController
加载并设置其“页面”控制器的视图大小。因此,尝试以相反的方式进行 - 设置页面视图控制器的大小以匹配“页面”大小有点棘手。
此外,如果我们尝试为每个“页面”执行此操作,框架将不断变化并且(可能)会是糟糕的用户体验。
我们可以,但是,pre-load 页面并计算最大页面高度来设置我们的控制器。
概览代码为:
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
我试了一下,在这里放了一个工作示例项目:https://github.com/DonMag/DynamicPGVC
虽然我将“parentView”(我称之为 OverlayView)作为一个单独的视图子类,以便将很多 activity 保留在主视图控制器之外,但您展示的内容的基本概念。
外观如下:
点击“标签”按钮(细矩形显示“carouselView”的框架):
点击“图像”按钮:
点击“页面视图控制器”按钮 - 第一个“页面”(“页面”很简单 - 带有标题和内容标签的垂直堆栈视图):
第二页,显示内容的最大高度: