下面的代码(在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]];
滚动视图的约束与其他视图的约束略有不同。contentView
与其superview
(scrollView
)之间的约束是对scrollView
的contentSize
,而不是对frame
。这可能看起来令人困惑,但它实际上非常有用,这意味着您永远不必调整contentSize
,但contentSize
会自动调整以适应您的内容。此行为在技术说明TN2154中有描述。
如果你想定义contentView
的屏幕大小或类似的东西,你必须在contentView
和主视图之间添加一个约束,例如。不可否认,这与将内容放入滚动视图是对立的,所以我可能不会建议这样做,但这是可以做到的。
为了说明这个概念,contentView
的大小将由其内容驱动,而不是由scrollView
的bounds
驱动,请在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
(因此,scrollView
的contentSize
)被调整以适合具有标准边距的标签。因为我没有指定标签的宽度/高度,它会根据你放入标签的文本进行调整。
如果你想让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]];
scrollviews中的多行标签有一个已知的问题(bug?),如果你想让它根据文本的数量来调整大小,你必须做一些技巧,比如:
dispatch_async(dispatch_get_main_queue(), ^{
randomLabel.preferredMaxLayoutWidth = self.view.bounds.size.width;
});
我试过了。但是!如果您还启用了缩放,则此自动布局代码将成为阻碍。它在缩放时保持调整contentView的大小。也就是说,如果我放大(zoomScale> 1),我将无法滚动屏幕外的部分。
经过几天与Autolayout的斗争,遗憾的是我找不到任何解决方案。最后,它甚至不重要(什么?)好的,抱歉)。最后,我只是在contentView上删除自动布局(使用固定大小的contentView),然后在layoutSubviews中,我调整 self.scrollView.contentInset
。(我使用的是Xcode5, iOS 7)
- (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);
}
一般情况下,Auto Layout会将视图的上、左、下、右边缘视为可见边缘。也就是说,如果你把一个视图固定在父视图的左边缘,你实际上是把它固定在父视图边界的最小x值上。修改父视图的边界原点不会改变视图的位置。
UIScrollView类通过改变边界的原点来滚动它的内容。为了使自动布局工作,滚动视图中的上、左、下、右边缘现在意味着其内容视图的边缘。
对滚动视图的子视图的约束必须产生要填充的大小,然后将其解释为滚动视图的内容大小。(这不应该与用于自动布局的intrinsicContentSize方法混淆。)要使用自动布局来调整滚动视图框架的大小,必须明确限制滚动视图的宽度和高度,或者将滚动视图的边缘与子树之外的视图绑定。
请注意,您可以通过在视图和滚动视图子树之外的视图(例如滚动视图的父视图)之间创建约束,使滚动视图的子视图看起来漂浮(而不是滚动)在其他滚动内容之上。
下面是如何配置滚动视图的两个示例,首先是混合方法,然后是纯方法