2013-05-30 49 views
39

以下代码(在viewDidLoad中调用)将导致全红屏幕。我希望它是一个完全绿色的屏幕。为什么它是红色的?我怎样才能让它变成绿色?iOS自动化与UIScrollview:为什么滚动视图的内容视图不能填充滚动视图?

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]]; 

回答

66

滚动视图的约束与其他视图的约束不同。 contentView及其superviewscrollView)之间的限制条件为scrollViewcontentSize,而不是其frame。这看起来可能会让人困惑,但实际上它非常有用,这意味着您不必调整contentSize,而是contentSize会自动调整以适合您的内容。此行为在Technical Note TN2154中描述。

例如,如果要将contentView大小定义为屏幕或类似内容,则必须在contentView与主视图之间添加约束。诚然,与将内容放入滚动视图相反,所以我可能不会建议这样做,但它可以完成。


为了说明这个概念,是的contentView的大小由它的内容来驱动,而不是由scrollViewbounds,添加标签到您的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(并因此调整scrollViewcontentSize)以适应具有标准边距的标签。因为我没有指定标签的宽度/高度,所以会根据您放入该标签的文字进行调整。


如果你想contentView也调整到主视图的宽度,你可以做重新定义viewDict像这样,然后添加这些额外的限制(除了所有其它的,上面):

UIView *mainView = self.view; 

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

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

有一个known issue (bug?)在scrollviews多标签,如果你想让它根据文字的数量调整大小,你所要做的手的一些手法,如:

dispatch_async(dispatch_get_main_queue(), ^{ 
    randomLabel.preferredMaxLayoutWidth = self.view.bounds.size.width; 
}); 
+1

我似乎无法获取标签,以垂直扩展,如果文本太长,以适应一条线。我已将行数设置为0,并将lineBreakMode设置为NSLineBreakByWordWrapping。我错过了什么吗? – rdelmar

+0

此外,我在答案中添加了最后一行,以将contentView的宽度固定为屏幕的宽度(否则contentView的宽度足够宽以容纳长文本)。如果我用高度限制明确设置标签的高度,那么它就可以工作。 – rdelmar

+0

是的,马特发现了[一些解决方法](http://stackoverflow.com/questions/13149733/ios-autolayout-issue-with-uilabels-in-a-resizing-parent-view)这个问题/多行标签的缺陷。我已将其中一个添加到我的答案的底部。彻底不满意的解决方案。看起来像你必须在硬编码值或这个kludgy解决方法之间进行选择。 – Rob

1

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

经过与Autolayout的战斗日子,可惜我找不到任何解决方案。最后,它甚至不重要(wat?ok,对不起)。最后,我刚刚在contentView上移除Autolayout(使用固定大小的contentView),然后在layoutSubviews中,我调整了self.scrollView.contentInset。 (我正在使用Xcode5,iOS 7)

我知道这不是这个问题的直接解决方案。但我只想指出一个简单的解决方法。它完美地将固定大小的contentView集中在一个scrollView中,只需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); 
} 
-1

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

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

对滚动视图的子视图的约束必须导致要填充的大小,然后将其解释为滚动视图的内容大小。 (这不应与用于自动布局的intrinsicContentSize方法混淆)。要使用自动布局调整滚动视图的框架,约束必须是关于滚动视图的宽度和高度的显式,或者滚动视图的边缘必须是绑在其子树之外的视图上。

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

下面是如何配置滚动​​视图,首先将混合的方法,然后将纯的做法两个例子

+0

欢迎来到Stack Overflow。请复习[我如何写出一个好答案](https://meta.stackexchange.com/questions/7656/how-do-i-write-a-good-answer-to-a-question)。 – Rick