43

以下代码(在 viewDidLoad 中调用)导致全红屏。我希望它是一个完全绿色的屏幕。为什么是红色的?我怎样才能让它全部变绿?

UIScrollView* scrollView = [UIScrollView new];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.backgroundColor = [UIColor redColor];
[self.view addSubview:scrollView];

UIView* contentView = [UIView new];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
contentView.backgroundColor = [UIColor greenColor];
[scrollView addSubview:contentView];

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView,contentView);

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics:0 views:viewDict]];

[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:0 views:viewDict]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" options:0 metrics:0 views:viewDict]];
4

3 回答 3

73

滚动视图的约束与其他视图的约束略有不同。contentViewof和 its superview(the scrollView)之间的约束是针对scrollView's 的contentSize,而不是针对 its 的frame。这可能看起来令人困惑,但实际上非常有用,这意味着您无需调整contentSize,而是contentSize会自动调整以适应您的内容。此行为在技术说明 TN2154中进行了描述。

例如,如果你想定义contentView屏幕的大小或类似的东西,你必须contentView在主视图和主视图之间添加一个约束。诚然,这与将内容放入滚动视图是对立的,所以我可能不建议这样做,但可以做到。


为了说明这个概念, 的大小contentView将由其内容驱动,而不是由 的bounds,在scrollView您的 中添加一个标签contentView

UIScrollView* scrollView = [UIScrollView new];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.backgroundColor = [UIColor redColor];
[self.view addSubview:scrollView];

UIView* contentView = [UIView new];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
contentView.backgroundColor = [UIColor greenColor];
[scrollView addSubview:contentView];

UILabel *randomLabel = [[UILabel alloc] init];
randomLabel.text = @"this is a test";
randomLabel.translatesAutoresizingMaskIntoConstraints = NO;
randomLabel.backgroundColor = [UIColor clearColor];
[contentView addSubview:randomLabel];

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView, contentView, randomLabel);

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics:0 views:viewDict]];

[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:0 views:viewDict]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" options:0 metrics:0 views:viewDict]];

[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[randomLabel]-|" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[randomLabel]-|" options:0 metrics:0 views:viewDict]];

现在您会看到contentView(以及因此contentSizescrollView)被调整以适应具有标准边距的标签。而且因为我没有指定标签的宽度/高度,它将根据您放入该标签的文本进行调整。


如果您还希望contentView调整到主视图的宽度,您可以重新定义您的viewDictlike,然后添加这些额外的约束(除了上面的所有其他约束):

UIView *mainView = self.view;

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView, contentView, randomLabel, mainView);

[mainView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[contentView(==mainView)]" options:0 metrics:0 views:viewDict]];

滚动视图中的多行标签存在一个已知问题(错误?),如果您希望它根据文本量调整大小,您必须做一些花招,例如:

dispatch_async(dispatch_get_main_queue(), ^{
    randomLabel.preferredMaxLayoutWidth = self.view.bounds.size.width;
});
于 2013-05-30T19:03:37.067 回答
1

我试过 Rob 的答案一开始看起来不错。但!如果您还启用了缩放,则此自动布局代码会妨碍您。缩放时它会不断调整 contentView 的大小。也就是说,如果我放大(zoomScale > 1),我将无法滚动屏幕外的部分。

经过几天与 Autolayout 的斗争,遗憾的是我找不到任何解决方案。最后,这甚至都没有关系(wat?好的,对不起)。最后,我只是删除了 contentView 上的 Autolayout(使用固定大小的 contentView),然后在 layoutSubviews 中调整 self.scrollView.contentInset. (我正在使用 Xcode5,iOS 7)

我知道这不是这个问题的直接解决方案。但我只想指出一个简单的解决方法。它非常适合在滚动视图中居中固定大小的内容视图,只需 2 行代码!希望它可以帮助某人;)

- (void)layoutSubviews {
    [super layoutSubviews];

    // move contentView to center of scrollView by changing contentInset,
    // because I CANNOT set this with AUTOLAYOUT!!!
    CGFloat topInset = (self.frame.size.height - self.contentView.frame.size.height)/2;
    self.scrollView.contentInset = UIEdgeInsetsMake(topInset, 0, 0, 0);
}
于 2014-02-14T18:05:32.837 回答
-1

通常,自动布局将视图的顶部、左侧、底部和右侧边缘视为可见边缘。也就是说,如果您将视图固定到其父视图的左边缘,您实际上是将其固定到父视图边界的最小 x 值。更改超级视图的边界原点不会更改视图的位置。

UIScrollView 类通过更改其边界的原点来滚动其内容。为了使用自动布局,滚动视图中的顶部、左侧、底部和右侧边缘现在意味着其内容视图的边缘。

滚动视图的子视图上的约束必须导致要填充的大小,然后将其解释为滚动视图的内容大小。(这不应与用于 Auto Layout 的 intrinsicContentSize 方法混淆。)要使用 Auto Layout 调整滚动视图的框架大小,必须明确限制滚动视图的宽度和高度,或者滚动视图的边缘必须是绑定到其子树之外的视图。

请注意,您可以通过在视图和滚动视图的子树之外的视图(例如滚动视图的超级视图)之间创建约束,使滚动视图的子视图看起来浮动(不滚动)在其他滚动内容之上。

这里有两个如何配置滚动​​视图的例子,首先是混合方式,然后是纯方式

于 2017-11-28T09:16:05.510 回答