UIView在UIScrollView中不遵循自动布局约束

5

我正在将一个UIView添加到UIScrollView中,并将其约束为填充水平空间,除了一些边距。我的可视化约束如下:

@"|-16-[theLineView]-16-|"

我将视图设置为一个像素高,这样它就会显示为一条线,并将其放置在两个文本标签之间。
@"V:[someOtherStuff]-[aTextLabel]-[theLineView]-[anotherLabel]"

然而,我发现线的宽度只扩展到其上下方最长标签的宽度。为什么会这样?P.S 我已经阅读了此链接:http://developer.apple.com/library/ios/#technotes/tn2154/_index.html

enter image description here

代码

以下是一个测试项目中视图控制器代码的全部内容,该项目在iPad模拟器上出现了此问题。

- (void)viewDidLoad

{[super viewDidLoad];}

self.scrollView = [[UIScrollView alloc] init];
self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
self.scrollView.backgroundColor = [UIColor greenColor];
[self.view addSubview:self.scrollView];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[scrollView]|"
                                                                 options:0
                                                                 metrics:0
                                                                   views:@{@"scrollView":self.scrollView}]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|"
                                                                  options:0
                                                                  metrics:0
                                                                    views:@{@"scrollView":self.scrollView}]];

self.line1 = [[UIView alloc] init];
self.line2 = [[UIView alloc] init];
self.label1 = [[UILabel alloc] init];
self.label2 = [[UILabel alloc] init];
self.label3 = [[UILabel alloc] init];

for (UILabel *label in @[self.label1, self.label2, self.label3])
{
    label.text = @"I am a label and I am long enough that I can be multiline on an iphone but single on ipad";
}

for (UIView *view in @[self.line1, self.line2, self.label1, self.label2, self.label3])
{
    view.translatesAutoresizingMaskIntoConstraints = NO;
    view.backgroundColor = [UIColor redColor];
    [self.scrollView addSubview:view];
}

//horizontal layout - all views/labels should fill the horizontal space expect for margin
for (UIView *view in  @[self.line1, self.line2, self.label1, self.label2, self.label3])
{
    NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-16-[view]-16-|"
                                                                   options:0
                                                                   metrics:0
                                                                     views:@{@"view":view}];
    [self.scrollView addConstraints:constraints];
}

//vertical layout - stack em up
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[lab1]-[line1(==1)]-[lab2]-[line2(==1)]-[lab3]-|"
                                                                  options:0
                                                                  metrics:0
                                                                    views:@{@"lab1":self.label1, @"line1":self.line1, @"lab2":self.label2, @"line2":self.line2, @"lab3":self.label3}]];

}


你的 1 像素的 UIView 上还有其他的约束吗? - Yazid
[view]和[theLineView]是同一个视图吗?您在选项参数中设置了任何对齐选项吗? - rdelmar
没有其他限制或选项参数。我会尝试在测试项目中重现。是的,那些视图是相同的,已经更正了代码片段。 - Ben Packard
请查看此处:https://dev59.com/RWQn5IYBdhLWcg3wa2qV - catamphetamine
4个回答

8

UIScrollView会自动缩小以适应其内部视图。您需要在某个地方绝对设置宽度。

推荐策略:

  1. 使用约束条件(leading、trailing、top、bottom)完全固定滚动视图在其父视图中。
  2. 在UIScrollView中创建一个UIView,并将所有所需内容放入其中。
  3. 设置约束条件,使UIView充当内容视图(这意味着它足够大以包含所有元素)。大量使用内在内容大小、收缩抵抗力和元素与约束的链接。生成的布局必须明确定义且唯一(这意味着如果您删除所有与外部的约束,布局仍将起作用)。
  4. 将UIView的边界连接到其父视图的边界上(实际内容视图为UIScrollView的超级视图,而不是UIScrollView本身!)。

如果您在界面构建器中执行此操作(可能性是有的),则每次触碰该场景时都需要重新检查约束条件。而“触碰”指的不仅是“修改”,还包括“选择”。


对我没用,如何将UIView设置为填满整个滚动视图的宽度? - AndiDog
正如我所说:contentView始终填满整个scrollView,因为scrollView会缩小。你必须在内部设置宽度。在你的例子中,将格式从“|-16-[view]-16-|”更改为“|-16-[view(288)]-16-|”应该会给你一个很好的结果。 - patric.schenke
3
这个方法可行。在我看来,第四种方法是更好的选择,因为它简单明了,避免了任何神奇数字(这意味着它支持旋转等)。 - Ben Packard

2

在Patric Schenke的回答中,进一步解释了第4点; 因为scrollView的内容大小是流动的,将内部视图固定到其边缘并不能确定视图的宽度。您的左侧固定会起作用,但两个都不行。根据上一个级别容器计算视图的宽度是正确的方法。只要您的self.scrollView紧贴其容器(我称之为containerView),此代码行将实现您想要的功能。将此行添加到水平约束的for循环中:

// Pin view's width to match the scrollView container's width
// -32 constant offset for margins
[containerView addConstraint:
  [NSLayoutConstraint constraintWithItem:view 
                               attribute:NSLayoutAttributeWidth
                               relatedBy:NSLayoutRelationEqual
                                  toItem:containerView
                               attribute:NSLayoutAttributeWidth
                              multiplier:1.0f
                                constant:-32]];

2

我找到了一个适合您使用情况的有效解决方案。请参见此处.


0
我找到了一种简单的基于约束的方法来实现这个(我还没有测试这个解决方案的脆弱程度):
...@"H:|-16-[view]-16-|"... // (your original constraint)

[self.scrollView addConstraint:
  [NSLayoutConstraint constraintWithItem:view 
                               attribute:NSLayoutAttributeCenterX
                               relatedBy:NSLayoutRelationEqual
                                  toItem:self.scrollView
                               attribute:NSLayoutAttributeCenterX
                              multiplier:1.0f
                                constant:0.0f]];

这会将view拉伸到视图的另一侧。对于水平滚动的内容来说可能不是理想的,但在垂直方向上应该可以工作。

我意识到这已经过去一年了,但对于单维度滚动的使用情况而言,这比patric.schenke的答案更简单(他的答案很好,也更健壮)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接