如何使用Autolayout在我的UIScrollview上设置约束?

时间:2013-11-26 16:47:27

标签: ios uiscrollview storyboard autolayout

我花了两天时间尝试Mixed and Pure Autolayout approaches的各种解决方案来实现自动布局之前的简单滚动视图设置,现在它已经正式 - 我必须太愚蠢了。我主要在故事板中设置它(好吧,它就是这样)。

所以这是我的求助请求。

Viewtree:

UIView
-UIView
-UIView
..-UIScrollview
...-UIButton
...-UIButton
...-UIButton

按钮应该水平滚动(从左到右,反之亦然)。有人让我知道如何设置约束来实现这个使用纯Autolayout ???

-

我尝试了混合方法,如下:

UIView
- UIView
- UIView
..-UIScrollview
...-UIView (contentview)
....-UIButton
....-UIButton
....-UIButton

...并根据Apple的TechNote设置contentviewtranslatesAutoresizingMaskIntoConstraints设置的固定宽度和高度限制。使用约束设置按钮和滚动视图。这让滚动视图滚动(yay)但是唉,它滚动得太远了!据我所知,滚动宽度在某种程度上比我设置的内容视图加倍了??? !!! ???

我尝试了纯自动布局方法,包括contentview和没有。 所有视图均为translatesAutoresizingMaskIntoConstraints=NOself.view除外。按钮具有固定的宽度/高度约束,并固定到滚动视图的所有四个边缘。什么都不滚动。

所以我完全不知道为什么我无法让它正常工作。非常感谢任何帮助,如果您需要任何其他信息,请询问!

更新了解决方案的屏幕截图 - buttonZ约束:

enter image description here

编辑@ Jamie Forrest 因此,解决方案结果是最后一个按钮上的错误尾随约束。而不是6441,我设置的值是负数,-6441。棘手的是,在storyboard中设置值时,Pin工具栏中有两个选项:

enter image description here

当前画布值为负(导致无滚动),下面的选项为正(激活滚动)。这意味着我不是傻瓜,但我猜至少半盲。 虽然为了我的辩护,XCode没有为“错误”设置显示错误,这有点令人不安吗?

再次编辑 现在这很有趣......将尾随值从-6441(无滚动)更改为6441启用滚动。但我的老朋友“太多内容化”又回来了,导致内容大小是应有的两倍!获取正确内容滚动的解决方案是将尾随约束设置为ZERO!当在Storyboard中工作但是查看@Infinity James的代码时,这并不明显,它应该是它。

18 个答案:

答案 0 :(得分:65)

在您将限制条件粘贴到此处时,很难看到约束的确切值和设置,因此我不确定从您的屏幕截图中看错了。

为了解释您的设置中出现了什么问题,我创建了一个基本sample project,其中包含与您描述的视图层次结构和约束设置非常相似的视图层次结构和约束设置。水平滚动在示例项目中按预期工作,该项目使用Apple在Technical Note中描述的“Pure AutoLayout”方法。

我最初让Auto Layout与UIScrollView一起使用时遇到了很多麻烦。使其工作的关键是确保滚动视图中的所有项目一起使用具有最终链接到滚动视图的所有侧面的约束,这有助于AutoLayout系统能够为其确定contentSize滚动视图将大于其框架。看起来你试图在你的代码中做到这一点,但也许你有一些多余的约束使得contentSize太小。

另外值得注意的是,正如其他人所提到的,使用AutoLayout和UIScrollview,您不再显式设置contentSize。 AutoLayout系统根据您的约束计算contentSize。

我还发现this ebook chapter非常有助于我了解这一切是如何运作的。希望这一切都有所帮助。

答案 1 :(得分:48)

LOL欢迎来到愚蠢的俱乐部。我是其中一位创始人。 :d

对于VERTICAL滚动:我能让它工作的唯一方法(iOS 8,Xcode 6和纯自动布局)将以下约束添加到我的滚动视图(所有与超视图相关):

  • 等宽
  • Equal Heights
  • 中心Y对齐
  • 中心X对齐

我的结构:

  UIView
   - ScrollView
    - Subview
    - Subview
    - Subview
    - Subview
    - ...

这是最终结果:

Demo

这是设置:

Setup Full screen

  

<强> And here is the project

希望这可以避免某人在凌晨5点再次入睡。 :d

答案 2 :(得分:30)

简单的自包含示例

根据对该问题的高票数和对答案的低票数,人们在这里找不到可理解的快速解决方案。让我试着添加一个。该项目是一个完全在Interface Builder中完成的自包含示例。你应该能够在10分钟或更短的时间内完成它。然后,您可以将您学到的概念应用到您自己的项目中。

enter image description here

原始问题询问滚动按钮。在这里,我只使用UIView s,但它们可以表示您喜欢的任何视图。我还选择了水平滚动,因为这种格式的故事板截图更加紧凑。但是,垂直滚动的原则是相同的。

关键概念

  • UIScrollView应该只使用一个子视图。这是一个&#39; UIView&#39;用作内容视图以保存您想要滚动的所有内容。
  • 使内容视图和滚动视图的具有相等的水平滚动高度。 (垂直滚动的宽度相等)
  • 确保所有可滚动内容都有设定的宽度并固定在所有方面。

开始一个新项目

它可以只是一个视图应用程序。

故事板

在这个例子中,我们将创建一个水平滚动视图。选择View Controller,然后在Size Inspector中选择Freeform。设置宽度1,000和高度300。这只是让我们在故事板上留出空间来添加将滚动的内容。

enter image description here

添加滚动视图

添加UIScrollView并将所有四边都固定到视图控制器的根视图。

enter image description here

添加内容视图

UIView作为子视图添加到滚动视图中。 这是关键。不要尝试在滚动视图中添加大量子视图。只需添加一个UIView即可。这将是您要滚动的其他视图的内容视图。将内容视图固定到四个方面的滚动视图。

enter image description here

等高度

现在,在文档大纲中, Command 单击内容视图和滚动视图的父视图,以便同时选择它们。然后将高度设置为相等( Control 从内容视图拖动到滚动视图)。 这也是关键。因为我们是水平滚动,滚动视图的内容视图不知道它应该有多高,除非我们以这种方式设置它。

enter image description here

注意:

  • 如果我们将内容垂直滚动,那么我们会将内容视图的宽度设置为等于滚动视图的父级宽度。

添加内容

添加三个UIView并为它们提供所有约束。我使用了8点边距。

enter image description here

约束:

  • 绿色视图:固定顶部,左侧和底部边缘。宽度为400。
  • 红色视图:固定顶部,左侧和底部边缘。宽度为300。
  • 紫色视图:固定所有四个边缘。无论剩余空间是什么,都要设置宽度(在这种情况下为268)。

设置宽度约束也是关键,以便滚动视图知道其内容视图的宽度。

完成

这就是全部。您现在可以运行您的项目。它的行为应该与此答案顶部的滚动图像相似。

对于垂直滚动,只需在此示例中交换所有宽度和高度方向(已测试且正在工作)。

进一步研究

答案 3 :(得分:9)

通过在UIScrollView中应用约束来隐式设置contentSize。

例如,你是否在UIView中有一个UIScrollView,它看起来像这样(我相信你知道):

    UIView *containerView               = [[UIView alloc] init];
    UIScrollView *scrollView            = [[UIScrollView alloc] init];
    [containerView addSubview:scrollView];
    containerView.translatesAutoresizingMaskIntoConstraints = NO;
    scrollView.translatesAutoresizingMaskIntoConstraints    = NO;
    NSDictionary *viewsDictionary       = NSDictionaryOfVariableBindings(containerView, scrollView);

    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|"
                                                                          options:kNilOptions
                                                                          metrics:nil
                                                                            views:viewsDictionary]];
    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|"
                                                                          options:kNilOptions
                                                                          metrics:nil

这将设置scrollView以填充containerView的大小(因此containerView必须具有一定的大小)。

然后,您可以通过隐式将其设置为足以按住这样的按钮来调整UIScrollView的contentSize:

    UIButton *buttonA                   = [[UIButton alloc] init];
    UIButton *buttonB                   = [[UIButton alloc] init];
    UIButton *buttonC                   = [[UIButton alloc] init];
    [scrollView addSubview:buttonA];
    [scrollView addSubview:buttonB];
    [scrollView addSubview:buttonC];
    buttonA.translatesAutoresizingMaskIntoConstraints       = NO;
    buttonB.translatesAutoresizingMaskIntoConstraints       = NO;
    buttonC.translatesAutoresizingMaskIntoConstraints       = NO;

    viewsDictionary                     = NSDictionaryOfVariableBindings(scrollView, buttonA, buttonB, buttonC);

    [scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[buttonA]-|"
                                                                       options:kNilOptions
                                                                       metrics:nil
                                                                         views:viewsDictionary]];
    [scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[buttonA]-[buttonB]-[buttonC]-|"
                                                                       options:NSLayoutFormatAlignAllBaseline
                                                                       metrics:nil
                                                                         views:viewsDictionary]];

答案 4 :(得分:8)

关于在UIScrollView中使用AutoLayout有很多问题,我们忽略的关键点是UIScrollView的内部视图对内容视图而不是UIScrollView本身进行约束。请参阅Technical Note TN2154,您可以找到:

  

UIScrollView类通过更改其边界的原点来滚动其内容。要使其与“自动布局”一起使用,滚动视图中的顶部,左侧,底部和右侧边缘现在表示其内容视图的边缘。

下图将描述: enter image description here

您可以发现尾随空间为500个点,如果对UIScrollView进行约束,则视图将被放置并应更新其帧。但是,没有警告也没有错误。因为所有约束都是针对内容视图的。

UIScrollView将根据内部视图的约束来计算内容视图的大小。 (例如,内容大小:宽度= 100(前导空格)+ 200(视图宽度)+ 500(尾随空格),高度= 131(顶部间距)+ 200(高度)+ 269(底部间距)

如何在UIScrollView中添加视图约束:

  1. 在内容视图中对视图的位置进行成像。
  2. 将顶部,右侧,底部,左侧间距添加到内容视图的边缘,此外,还添加这些视图的宽度和高度。
  3. 一切都已完成。

    使用scrollview处理AutoLayout的一种简单方法是在滚动视图中添加包含所有子视图的容器视图。

    结论:使用UIScrollView了解AutoLayout的关键是内部视图对内容视图进行约束,但不对UIScrollView本身进行约束。

    附上example code

答案 5 :(得分:5)

以下解决方案适用于 scrollView with autolayout且没有contentSize

  1. 将scrollView拖放到viewController并应用任何约束来覆盖所需的空间。
  2. 拖动滚动视图中的 UIView 并使其覆盖scrollView的整个空间,并从scrollView应用约束为顶部,左侧,右侧,底部空间
  3. 根据滚动的需要设置内部视图的高度(以及需要水平滚动时的宽度)。如果需要,也可以从代码中完成此部分。
  4. 临界。在第(3)点将高度设置为某个较大值后,返回第(2)点并确保将顶部,左侧,右侧,底部值设置为零,因为Xcode可能具有当您强制更改(3)中的高度时,为您更改了它们。
  5. 你已经完成了。现在,您可以在此视图上添加任意数量的控件,并应用彼此相关的约束(在没有此视图的情况下似乎不起作用)。如果您不想使用此视图,那么您必须为与scrollView相关的每个控件应用约束(彼此不相关)。

    压倒性的小费..............

    <强>临界即可。让我们说清楚UIScrollView 1000宽和100高。 (事实上​​,这些值通常是动态的,当然,取决于设备的宽度等。但现在只说1000宽和100高。)让我们说你正在进行水平滚动。所以在UIScrollView中放置一个UIView。 (这是&#34;内容视图&#34;。)将内容视图top,bottom,leading,trailing的所有四个约束设置为滚动视图。 即使看起来不对,也要将它们全部归零。将内容UIView的高度设置为100并忘记这一点。现在:您想要水平滚动,因此请设置内容视图的宽度,例如1225。

    请注意,内容视图的宽度现在比父卷轴视图的宽度大225。没关系:事实上,你必须这样做。注意

    ...你做 NOT 将尾随宽度设置为负225 ...

    你会认为你必须&#34;匹配&#34;宽度正常情况。但如果你这样做,那么根本不会起作用。

    您必须将前导和尾随数字设置为 ZERO ,绝不负数(即使宽度为&#34;更大&#34;)

    有趣的是,您实际上可以将前导/尾随数字设置为任何值(请尝试说&#34; 50&#34;),它会为您提供反弹余量。 (它通常看起来很棒:尝试一下。)任何一端的任何负值都会#34;默默地打破&#34;。

    注意,令人愤怒的是,经常是Xcode(无论如何都是7.3.1),

    将有助于&#39;将这些值设置为负数!

    因为它会尝试为您自动计算它们。如果是这样,它会默默地打破。在第一个实例中将所有四个值都设置为零。并且将内容视图的宽度设置得比&#34; 1000&#34;更宽。在示例中。

    <强>编辑: 我最终使用UITableView而不是UIScrollView满足了我的大部分要求。因为tableView在我看来更加灵活和动态。

答案 6 :(得分:4)

我认为您遇到了contentSize的问题。在使用“纯”AutoLayout方法时,请查看this blog post如何处理contentSize。它的要点是你的约束隐含地定义了内容大小。使用AutoLayout时,切勿明确设置它。我在博客文章末尾添加了示例项目,以演示它是如何工作的

答案 7 :(得分:3)

你应该像这样整理你的布局 ViewControllerView包含ScrollViewScrollView包含ContainerViewContainerView包含2 Labels
enter image description here

然后按照3个步骤使您的ScrollView可以滚动

  1. ScrollView引脚(顶部/右侧/底部/左侧)设置为ViewControllerView
    • ContainerView引脚(顶部/右侧/底部/左侧)设置为ScrollView
    • 设置Horizontally in Container设置Vertically in Container
    • Label1 pin(顶部 /右/左)到ContainerView
    • Label1 pin(右/左/ 底部)到ContainerView顶部Label1
  2. enter image description here

      

    HERE is the demo project

    希望这个帮助

答案 8 :(得分:3)

技术说明中有一条你可能已经看过了。您可以使用固定在滚动视图边缘的约束隐式设置滚动视图的内容大小。

这是一个简单的例子。使用一个视图创建一个故事板,该视图具有一个滚动视图。设置滚动视图约束以使其适合您放入的视图的大小。

在滚动视图内添加一个视图。使用约束显式设置该视图的大小(并确保大小大于滚动视图)。

现在为该内部视图添加四个约束,将内部视图的四个边锁定到其父滚动视图。这四个约束将导致内容大小扩展以适应内部视图。

如果要将多个视图添加到滚动视图(例如水平布局),则会将第一个子视图的左侧锁定在滚动视图的左侧,将子视图水平锁定到彼此,以及滚动视图右侧最后一个子视图的右侧。这些约束将强制滚动视图的内容大小扩展以适应所有子视图及其约束。

答案 9 :(得分:3)

如果您的问题是“我如何在垂直滚动的UIScrollView中放置一堆UITextField,以便在他们有焦点时移出键盘”,最好的答案是:

别。

使用带静态单元格的UITableViewController。

你可以免费获得这种向外滚动的行为,如果你的视图控制器显示在UINavigationController中,那么所有内容插入Just Work。

答案 10 :(得分:1)

经过一段时间处理这个问题后,我终于找到了解决方案。我正在使用通用班级规模的故事板(600x600)。我创建了一个与scrollView大小相同的UIView(contentView),并为scrollView创建了Top,Bottom,Leading和Trailing的约束。然后我手动将contentView的大小剪切到600x600。故事板停止尝试调整所有内容,我可以工作,但视图在真实设备或模拟器上看起来很糟糕。 我制作了2个这种剪裁尺寸的约束插座。

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentViewWidthConstraint; 
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentViewHeightConstraint;

然后在viewDidLoad

CGSize viewSize = self.view.frame.size;
self.contentViewWidthConstraint.constant = viewSize.width;
self.contentViewHeightConstraint.constant = viewSize.height;

效果很好。

答案 11 :(得分:1)

swift 中,您可以使用此工作解决方案。

<强>约束上

ScrollView:领先,尾随,顶部,底部=超级视图

ContentView:领先,尾随,顶部,底部= ScrollView。高度固定/相对于内容。

您可以将宽度约束(contentView)设置为等于scrollview,但在构建时选择remove remove,因为您将以编程方式添加该约束。这只是因为IB没有抱怨警告。

extension UIView {

    func setupContentViewForViewWithScroll(contentView vwContent : UIView) {
        //Set constraint for scrollview content
        let constraint = NSLayoutConstraint(item: vwContent, attribute: NSLayoutAttribute.Width, relatedBy: .Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: self.bounds.size.width)
        vwContent.addConstraint(constraint)
        self.layoutSubviews()
    }

}

在View Controller viewDidLayoutSubviews中,我只是调用此方法:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    self.view.setupContentViewForViewWithScroll(contentView: vwContent)
}

答案 12 :(得分:1)

我遇到了类似的问题。我设置了每一个约束,并总是想知道为什么它仍然调整一些子视图。我的解决方案是将clipsToBounds设置为YES。

答案 13 :(得分:1)

类似的问题我今天使用的是iOS 8.4,Xcode 6.4

有一个包含滚动视图的视图,其中包含一个包含子视图的contentView(UIView)。

到处都是自动布局。 滚动视图边缘通过约束固定到父视图边缘。 内容视图边缘被固定到具有约束的滚动视图边缘。

最初内容视图会拒绝调整滚动视图的整个宽度。我不得不在内容视图上添加一个额外的约束,使其宽度与父滚动视图匹配。或者我可以设置contentView.centerX == scrollView.centerX约束。除了固定边缘之外,其中任何一个突然使内容视图的大小合适。

// Either one of these additional constraints are required to get autolayout to correctly layout the contentView. Otherwise contentView size is its minimum required size
scrollView.addConstraint(NSLayoutConstraint(item: contentView, attribute: .CenterX, relatedBy: .Equal, toItem: scrollView, attribute: .CenterX, multiplier: 1.0, constant: 0))
scrollView.addConstraint(NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Width, relatedBy: .Equal, toItem: scrollView, attribute: .Width, multiplier: 1.0, constant: 0.0))

使用表单的视觉约束将内容视图的边缘固定到滚动视图

let cvConstraints = ["H:|[contentView]|", "V:|[contentView]|"]

我使用例程迭代数组并将它们添加到scrollView。

答案 14 :(得分:1)

我花了几天时间试图找到一个如何使用AutoLayout查看嵌入式Scrollview的解决方案,将滚动视图置于可见屏幕的中心,该屏幕视图适用于所有设备/屏幕尺寸以及屏幕旋转。

我花了好几天时间尝试使用Autolayout,并且接近但从未足够接近。所以最后我必须在viewDidLoad中为每个屏幕添加3行代码。

见下面的解决方案:

  1. 创建滚动视图并使用您想要的任何对象填充它
  2. 启用自动布局
  3. 然后垂直和水平居中ScrollView Centre ScrollView
  4. 选择查看,然后选择&#39;添加缺失的约束&#39; - 这样做就可以了
  5. 结果是生成了很多约束。为视图创建了2个新的:&#39; Horiz空间scrollview to View&#39;和&#39; Vert空间scrollview查看&#39;反之亦然。
  6. 删除Horiz空间滚动视图以查看&#39;所以你现在在视图上留下了3个约束。 2用于在视图中输入滚动视图,另一个用于在滚动视图和视图之间设置垂直空间
  7. 现在通过单击并将Ctrl拖动到头文件并创建一个NSLayoutConstraint IBOutlet(我称之为我的约束版VVVV)来将Vert约束链接到您的代码
  8. 现在转到.m文件并将这些代码行添加到viewDidLoad中(使用填充量来播放以获得正确的垂直居中)

    if (IPAD)
    

    {     self.constraintVertVtoSV.constant = 150.0; }

  9. 现在应该在所有设备上运行并正确居中并且仍然可以正常滚动。

答案 15 :(得分:1)

如果像我这样你只是在子视图中使用没有约束的静态内容,就像你可以这样做:

override func viewDidLayoutSubviews() {
    scrollView.contentSize = CGSizeMake(320, 800)
}

答案 16 :(得分:1)

纯粹的自动布局方法非常有效,但如果您从非自动布局迁移,那么设置起来会非常痛苦。我现在已经完成了几次,我有一些一般的提示:

  • 从小处开始:即使这意味着重新创建故事板视图,也只需从几个元素开始,然后慢慢构建视图,确保在添加一些元素后测试滚动是否有效。
  • 关闭所有内容的translatesAutoresizingMaskIntoConstraints:这一直是导致约束冲突的原因。
  • 正确设置UIScrollView约束:确保滚动视图四面连接到父视图,否则根本不会扩展。

答案 17 :(得分:0)

我知道这是一个外行人的解决方案,而不是Apple在文档中提出的建议,但它适用于我两次,内容不同,可以很快设置: 在故事板视图控制器中插入UIView。 在UIView中插入一个Table View,Dynamic,0 Prototype单元格,Style Plain或Grouped。 在表视图中插入滚动视图, 在滚动视图中插入内容。 多数民众赞成,自定义视图控制器中没有设置。