UIScrollView的contentSize无效

62
我在nib的视图中放置了一个UIScrollView,并将其链接到IBOutlet属性。现在,当我在viewDidLoad方法中这样做时,似乎对contentSize没有影响。
self.sv.backgroundColor = [UIColor yellowColor]; // this works
CGSize size =  CGSizeMake(1000.0, 1000.0); 
[self.sv setContentSize:size]; // this does not

它的行为就像contentSize与框架大小相同一样。发生了什么事情?

当我关闭自动布局时,它开始起作用。为什么?


19
自动布局有时真的很让人头疼。 - Adam Waite
可以参考这篇非常好的文章来了解 - https://ios-tutor.blogspot.my/2014/11/this-is-my-second-post-in-ios-tutorial.html - jokerday
15个回答

84

我也遇到了同样的问题。 UIScrollView 的自动布局出现了问题。

解决方法:将UIScrollView中的所有内容都放到另一个UIView中,然后将该UIView作为UIScrollView的唯一子视图。 然后就可以使用自动布局了。

如果在末尾附近出现问题(UIScrollView滚动的任何方向的末尾),请将末尾的约束更改为最低可能的优先级。


6
我的回答实际上采用了苹果文档中提到的“混合方法”来配置UIScrollView,具体内容可以在http://developer.apple.com/library/ios/#releasenotes/General/RN-iOSSDK-6_0/_index.html中查看。我曾尝试过使用纯自动布局的方式,但由于某些原因无法使其正常工作。 - yuf
1
纯自动布局方式并不总是适用的,我同意。但它非常酷,因为子视图的约束描述了内容大小,你根本不需要设置contentSize。这里有一个例子(所有内容都是在代码中生成的):https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/ch20p570scrollViewInCode2 和这里(所有内容都是在nib中创建的):https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/ch20p573scrollViewAutoLayout2 - matt
1
马特发的链接似乎没有相关信息。这是我发布时正确的链接:https://developer.apple.com/library/ios/technotes/tn2154/_index.html - RajV
尝试在viewDidAppear()方法中添加contentSize属性。 - Deepak Singh Negi
@matt 你如何使用自动布局限制contentSize?我有一个高度依赖于屏幕大小的scrollView。如果屏幕是3.5英寸,我想把所有东西都放在较小的scrollView中。我为滚动视图中的子视图设置了多个约束,例如@"V:|-(<=39@500,>=10@500)-[leftView(72)]-(>=3@500,<=20@500)-[loginButton(30)]-0-|",但是contentSize对于两个scrollView高度是相同的。 - derpoliuk
显示剩余5条评论

71

我尝试使用viewWillLayoutSubviews来更新scrollView的contentSize,对我有用。

- (void)viewDidLayoutSubviews
{
  [self.bgScrollView setContentSize:CGSizeMake(320, self.view.frame.size.height* 1.5)];
}

Apple Doc

-(void)viewDidLayoutSubviews

通知视图控制器其视图刚刚完成布局子视图。

讨论

当视图控制器的视图的bounds更改时,视图将调整其子视图的位置,然后系统调用此方法。但是,调用此方法并不表示已经调整了视图子视图的各个布局。每个子视图负责调整自己的布局。

您的视图控制器可以重写此方法,在视图完成其子视图的布局后进行更改。该方法的默认实现不执行任何操作。


你需要将视图的高度乘以1.5来设置滚动视图的高度,但是如果滚动视图内部的控件具有可变高度,则此方法无效。例如,如果滚动视图中包含一个具有可变高度(取决于其内容)的UITextView,则滚动视图的高度将无法匹配其实际高度。 - Jerome
@Jerome 这只是一个更新内容大小的示例。您可以根据需要设置自己的内容大小以适应组件。 - HDdeveloper
你错过了调用super的方法:"[super viewDidLayoutSubviews];" - Uladzimir

21
最简单/最干净的方法是在viewDidAppear中设置contentSize,这样可以消除自动布局的影响。这不涉及添加随机视图。然而,依赖加载顺序来实现功能可能不是最好的想法。

1
非常感谢,您提供了我想要的完美答案。 - Gaurav

17

使用此代码。ScrollView setContentSize 应在主线程中异步调用。

Swift:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    DispatchQueue.main.async {
        var contentRect = CGRect.zero

        for view in self.scrollView.subviews {
           contentRect = contentRect.union(view.frame)
        }

        self.scrollView.contentSize = contentRect.size
    }
}

Objective C:

 - (void)viewDidLayoutSubviews {
     [super viewDidLayoutSubviews];

     dispatch_async(dispatch_get_main_queue(), ^ {
                        CGRect contentRect = CGRectZero;

                        for(UIView *view in scrollView.subviews)
                           contentRect = CGRectUnion(contentRect,view.frame);

                           scrollView.contentSize = contentRect.size;
                       });
}

这对我来说完美地运作,我已经在网上测试了几乎所有的解决方案,但它并没有像我预期的那样工作。 - khalid

15

在 Interface Builder 中使用 AutoLayout 和 UIScrollView 的超级简单方法:

步骤1:创建一个 UIScrollView

步骤2:创建一个作为滚动视图子视图的 UIView,如下所示:

-UIScrollView
---UIView
-----Your other content

(我们称之为contentView)。

第三步:在大小检查器中,为该视图设置高度和宽度(例如320x700)。

第四步(使用AutoLayout):从您的contentView创建明确的约束到其父视图(即UIScrollView):连接4个边缘(顶部、前导、尾随、底部),然后给它一个定义的宽度和高度,您希望它滚动到的位置。

例如:如果您的滚动视图覆盖整个屏幕,则可以使您的内容视图具有[设备宽度]的宽度和600的高度;然后将设置UIScrollView的内容大小以匹配。

或者:

第四步(不使用AutoLayout):使用IB将这两个新控件连接到您的视图控制器(从每个控件到视图控制器的.h @ implementation中ctrl+drag)。假设分别称为scrollViewcontentView。它应该像这样:

@interface YourViewController : UIViewController

@property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
@property (strong, nonatomic) IBOutlet UIView *contentView;

@end

第五步(不使用AutoLayout):在视图控制器的.h文件中添加(实际上是覆盖)以下方法:

-(void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    self.scrollView.contentSize = self.contentView.frame.size;
}

到目前为止,这是最好和最简单的方法。 - Reza_Rg
非常好的答案。这实际上是一个非常棘手的问题。另外,请记得调整滚动视图的设置以正确分页,以便预期的滚动不会在中途结束。 - Juan Reyes
@ProjectNoa 是的,没错。 - brandonscript

15
这里有两个问题:(1) viewDidLoad太早了,你必须等到布局完成后才能执行。(2) 如果你想要在使用从nib中获取的scrollview时使用自动布局,则必须使用约束来完全描述contentSize的大小 (然后根本不设置contentSize),或者如果你想要在代码中设置它,你必须防止scrollview的子视图上的约束决定contentSize。听起来你想做后者。为此,你需要一个UIView作为scrollview的唯一顶级子视图,并在代码中将其设置为不使用自动布局,启用其autoresizingMask并删除其其他外部约束。我在这里展示了如何做的示例:https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/ch20p573scrollViewAutoLayout/ch20p573scrollViewAutoLayout/ViewController.m 但请注意下一个示例,它展示了如何完全使用约束,而不是contentSize

1
你必须使用约束来完全描述contentSize的大小(然后在代码中根本不设置contentSize),这是解决问题的正确方法。 - Fervus
1
@Fervus 除了iOS 8,目前为止,它是有问题的。 - matt
@matt,你的链接已经失效了。能否请你修复一下? - Samuel
@Samuel 我的书籍当前版本的示例在这里:https://github.com/mattneub/Programming-iOS-Book-Examples 目前需要的示例以“bk2ch07”开头。 - matt

5
您可以将这些代码行添加到您的*.m文件中:
- (void)viewDidLoad{
   [scroll setContentSize:CGSizeMake(320, 800)]   ;
   [scroll setScrollEnabled:TRUE];
   [scroll setShowsVerticalScrollIndicator:NO];
   [scroll setShowsHorizontalScrollIndicator:YES];
}    

为此,您需要在*.h文件中添加一个UIScrollView的IBOutlet属性,方法如下:
IBOutlet UIScrollView *scroll;

并从Storyboard中连接此内容。

或者,

您可以将此方法用于您的*.m文件:

-(void)viewDidLayoutSubviews 
{
  [scroll setContentSize:CGSizeMake(320, self.view.frame.size.height* 1.5)];
   // this will pick height automatically from device's height and multiply it with 1.5  
}

这两种解决方案在我的Xcode-5、Xcode-6、Xcode-6.1和Xcode-6.2中都适用。


3

viewDidAppear中设置contentSize非常关键。

但是我发现在3.5英寸屏幕和4英寸屏幕上需要有所变化。在4英寸屏幕上可以工作,而旧的3.5英寸屏幕上却不能。两者都在iOS 7上运行。实在是太奇怪了!


1

我无法让基于约束的自动布局起作用。由于我的视图已经是UIScrollView的子类,所以我通过重写setContentView:并忽略自动布局的零高度setContentSize:消息来解决了这个问题。

@interface MyView : UIScrollView {}
@end

@implementation MyView
- (void)setContentSize:(CGSize)aSize {
    if (aSize.height > 0)
        [super setContentSize:aSize];
}
@end

1

我曾经以编程方式设置过uiscrollview,直到我观看了以下精彩的教程,逐步演示如何让uiscrollview和uiview工作:https://www.youtube.com/watch?v=PgeNPRBrB18

观看完视频后,我相信你会开始喜欢Interface Builder。

点赞


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