子视图不会将后沿与其父 UIScrollView 对齐

Posted

技术标签:

【中文标题】子视图不会将后沿与其父 UIScrollView 对齐【英文标题】:Subview won't align trailing edge to its parent UIScrollView 【发布时间】:2013-07-16 02:10:05 【问题描述】:

我有一个带有 2 个子视图的 UIScrollView。我希望一个子视图是“前导对齐”(左对齐),其前缘与滚动视图的前缘对齐。我希望另一个子视图是“尾随对齐”(右对齐),其中它的后缘与滚动视图的后缘对齐。

由于某种原因,自动布局会意外地将第二个尾随对齐的子视图放置在滚动视图的边界之外,到另一个子视图的前导(左侧)侧,这样子视图的后缘与前缘对齐滚动视图。

我正在尝试以编程方式执行此操作。代码如下。我为 2 个子视图使用 2 个标签。 “alpha”标签正确前导对齐,但“beta”标签未按应有的尾随对齐。

如果我尝试使用左右对齐而不是前导和尾随,也会发生这种情况。右对齐标签显示在与尾随标签相同的错误位置。

我已多次阅读此处和其他地方的 ios 6 发行说明和答案,但我不确定为什么会发生这种情况。

在视图控制器中:


- (void) viewDidLoad

    [super viewDidLoad];

    // Create and configure the scroll view.
    UIScrollView * scrollView = [[UIScrollView alloc] init];
    [scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];

    // For debugging.
    [scrollView setClipsToBounds:NO];
    scrollView.layer.borderColor = [UIColor redColor].CGColor;
    scrollView.layer.borderWidth = 1.0;

    [[self view] addSubview:scrollView];

    // Layout scrollview.

    // Horizontal: leading edge to superview's leading edge, with indent.
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:scrollView
                                                          attribute:NSLayoutAttributeLeading
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:self.view
                                                          attribute:NSLayoutAttributeLeading
                                                         multiplier:1.0
                                                           constant:20.0]];

    // Horizontal: trailing edge to superview's trailing edge, with indent.
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:scrollView
                                                          attribute:NSLayoutAttributeTrailing
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:self.view
                                                          attribute:NSLayoutAttributeTrailing
                                                         multiplier:1.0
                                                           constant:-20.0]];

    // Vertical: top edge to superview's top edge, with indent.
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:scrollView
                                                          attribute:NSLayoutAttributeTop
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:self.view
                                                          attribute:NSLayoutAttributeTop
                                                         multiplier:1.0
                                                           constant:20.0]];

    // Vertical: bottom edge to superview's bottom edge, with indent.
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:scrollView
                                                          attribute:NSLayoutAttributeBottom
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:self.view
                                                          attribute:NSLayoutAttributeBottom
                                                         multiplier:1.0
                                                           constant:-20.0]];

    // Create and configure first label which should be leading-aligned with scrollview.
    UILabel * labelAlpha = [[UILabel alloc] init];
    [labelAlpha setTranslatesAutoresizingMaskIntoConstraints:NO];
    [labelAlpha setText:@"Alpha"];
    [scrollView addSubview:labelAlpha];

    // Layout first label.

    // Horizontal: leading edge to scrollview's leading edge.
    [scrollView addConstraint:[NSLayoutConstraint constraintWithItem:labelAlpha
                                                           attribute:NSLayoutAttributeLeading
                                                           relatedBy:NSLayoutRelationEqual
                                                              toItem:scrollView
                                                           attribute:NSLayoutAttributeLeading
                                                          multiplier:1.0
                                                            constant:0.0]];

    // Vertical: top edge to scrollview's top edge.
    [scrollView addConstraint:[NSLayoutConstraint constraintWithItem:labelAlpha
                                                           attribute:NSLayoutAttributeTop
                                                           relatedBy:NSLayoutRelationEqual
                                                              toItem:scrollView
                                                           attribute:NSLayoutAttributeTop
                                                          multiplier:1.0
                                                            constant:0.0]];


    // Create and configure second label which should be trailing-aligned with scrollview.
    UILabel * labelBeta = [[UILabel alloc] init];
    [labelBeta setTranslatesAutoresizingMaskIntoConstraints:NO];
    [labelBeta setText:@"Beta"];
    [scrollView addSubview:labelBeta];

    // Layout second label.

    // Horizontal: trailing edge to scrollview's trailing edge.
    [scrollView addConstraint:[NSLayoutConstraint constraintWithItem:labelBeta
                                                           attribute:NSLayoutAttributeTrailing
                                                           relatedBy:NSLayoutRelationEqual
                                                              toItem:scrollView
                                                           attribute:NSLayoutAttributeTrailing
                                                          multiplier:1.0
                                                            constant:0.0]];
    // Vertical: top edge to scrollview's top edge.
    [scrollView addConstraint:[NSLayoutConstraint constraintWithItem:labelBeta
                                                           attribute:NSLayoutAttributeTop
                                                           relatedBy:NSLayoutRelationEqual
                                                              toItem:scrollView
                                                           attribute:NSLayoutAttributeTop
                                                          multiplier:1.0
                                                            constant:0.0]];


作为参考,视图控制器视图的 recursiveDescription 的输出支持了未对齐:

2013-07-15 22:04:23.892 Middleman[5669:907] <UIView: 0x20872960; frame = (0 0; 320 480); transform = [0, -1, 1, 0, 0, 0]; autoresize = RM+BM; layer = <CALayer: 0x20871e60>>
   | <UIScrollView: 0x208718a0; frame = (20 20; 440 280); gestureRecognizers = <NSArray: 0x20871f20>; layer = <CALayer: 0x208728e0>; contentOffset: 0, 0>
   |    | <UILabel: 0x20872ab0; frame = (0 0; 44 21); text = 'Alpha'; clipsToBounds = YES; userInteractionEnabled = NO; layer = <CALayer: 0x20872b90>>
   |    | <UILabel: 0x208730e0; frame = (-36 0; 36 21); text = 'Beta'; clipsToBounds = YES; userInteractionEnabled = NO; layer = <CALayer: 0x20873170>>

【问题讨论】:

我遇到了完全相同的问题(尽管我使用的是 C# MonoTouch 工具而不是 obj-c)。我正在使用以编程方式创建的视图创建非常简单的示例,并且无法使用自动布局使它们与 UIScrollView 的右侧对齐。你有没有运气解决这个问题? 【参考方案1】:

我在同样的问题上苦苦挣扎,终于找到了解决方案。注意我用的不是objective C,我用的是MonoTouch (C#),但原理应该是一样的。

问题在于 AutoLayout 解决 UIScrollView 约束的方式。对于许多其他视图,自动布局使用视图的对齐矩形,这在许多情况下与视图的框架重合。然而,在 UIScrollView 中,如果约束居中(例如 centerX),它似乎使用框架的大小(例如宽度),这就是为什么我的子视图通常在 UIScrollView 中居中,但是当添加约束以右对齐时子视图它使用 contentSize 宽度/高度。问题是在我的情况下 contentSize 宽度为 0(仅垂直滚动),所以右对齐只是意味着对齐到 0 宽度的框,这就是为什么我在左边缘看到我的子视图的原因。

通常,我也会通过约束解决 contentSize,但在这种情况下,只有高度 contentSize 不为零,因为我是垂直滚动的。我尝试在滚动视图中添加约束以正确设置 contentSize 宽度,但我能做到这一点的唯一方法(同时为我的页面保留必要的约束)是执行以下操作:

    插入一个“空白”UIView 作为 scrollView 的子项。 使用约束将此 UIView 的宽度设置为***别 View 容器(在 scrollView 之上)的宽度 对 scrollView 应用约束,使其右边缘必须大于或等于我的空白 UIView 的右边缘(类似于用于设置垂直 contentSize 以滚动页面的约束)。

现在,我的 UIScrollView 的 contentSize 宽度设置正确,并且按预期对齐到它的右边缘。

【讨论】:

如何找出contentSize?【参考方案2】:

我对此的解决方案是在 UIScrollView 中插入某种 helperView(例如 UIView、UITableView)并将 6 个约束应用于 helperView:

- equal width to scrollView 
- equal height to scrollView 
- 0 leading space to scrollView 
- 0 trailing space to scrollView 
- 0 top space to scrollView 
- 0 bottom space to scrollView 

您可以将其他 scrollView 的子视图与 helperView 边缘对齐。

【讨论】:

【参考方案3】:

您可以尝试使用文本的右对齐并设置标签的宽度约束。否则标签大小适合内容。

CGFloat width = CGRectGetWidth(self.view.frame)-40.0;
labelBeta.preferredMaxLayoutWidth = width; //required for multi line wrapping
[scrollView addConstraints:
    [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[labelBeta(width)]|"
                                            options:0 metrics:@@"width":@(width)
                                              views:NSDictionaryOfVariableBindings(labelBeta)]];

【讨论】:

如果您想右对齐 UILabel 以外的其他内容(例如图像视图)怎么办?

以上是关于子视图不会将后沿与其父 UIScrollView 对齐的主要内容,如果未能解决你的问题,请参考以下文章

将子视图固定到 stackview 的前沿和后沿

UIScrollView 在视图出现之前不会接受子视图

UIScrollView 子视图不会调整大小

UIScrollView 不会作为子视图在里面滚动

将一个水龙头从 UIScrollView 传递到其父视图

尽管内容大小正确,但 UIScrollview 不会滚动; textview 子视图框架也表现得很奇怪