首页 文章

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

提问于
浏览
170

我花了两天时间尝试各种解决方案Mixed and Pure Autolayout approaches来实现自动布局之前的简单滚动视图设置,它就像它的方式一样_1131021 .

所以这是我的求助 .

Viewtree:

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

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

我尝试过混合方法,如下所示:

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

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

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

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

UPDATED Screenshot with solution - buttonZ约束:

enter image description here

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

enter image description here

当前画布值为负(导致无滚动),下面的选项为正(激活滚动) . 这意味着我不是傻瓜,但我猜至少半盲 . 虽然,为了我的辩护,isn 't it somewhat disturbing that XCode doesn' t显示"incorrect"设置的错误?

EDITED AGAIN 现在这很有趣......将-6441(无滚动)的尾随值更改为6441启用滚动 . 但我的老朋友"too much contentsize"回来了,导致内容大小是应有的两倍!获取正确内容滚动的解决方案是将尾随约束设置为ZERO!当在Storyboard中工作但是查看@Infinity James的代码时,这并不明显,它应该是什么 .

18 回答

  • 1

    当你在这里粘贴时,很难看到约束的确切值和设置,所以我不确定你看错了你的截图 .

    代替对's wrong in your setup, I'已创建基本sample project的解释,其具有与您描述的视图层次结构和约束设置非常相似的视图层次结构和约束设置 . 水平滚动在示例项目中按预期工作,该项目使用Apple在Technical Note中描述的"Pure AutoLayout"方法 .

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

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

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

  • 9

    大声笑欢迎来到愚蠢的俱乐部 . 我是其中一位创始人 . :d

    For VERTICAL scrolling :我能让它工作的唯一方法(iOS 8,Xcode 6和纯自动布局)将以下约束添加到我的Scroll View(所有与superview相关):

    • 等宽

    • 等高

    • 中心Y对齐

    • 中心X对齐

    我的结构:

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

    这是最终结果:

    Demo

    这是设置:

    Setup
    Full screen

    这是项目 .

    希望这可以避免某人在凌晨5点睡着 . :d

  • 3

    简单的自包含示例

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

    enter image description here

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

    关键概念

    • UIScrollView 应该只使用一个子视图 . 这是一个'UIView',用作内容视图,用于保存您想要滚动的所有内容 .

    • 使内容视图和滚动视图的父级具有相等的水平滚动高度 . (垂直滚动的宽度相等)

    • 确保所有可滚动内容都具有设置的宽度并固定在所有边上 .

    开始一个新项目

    它可以只是一个单一的视图应用程序 .

    故事板

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

    enter image description here

    Add a Scroll View

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

    enter image description here

    Add a Content View

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

    enter image description here

    Equal Heights

    现在,在文档大纲中,命令单击内容视图和滚动视图的父视图,以便同时选择它们 . 然后将高度设置为相等(从内容视图控制拖动到滚动视图) . 这也是关键 . 因为我们是水平滚动,所以除非我们以这种方式设置,否则滚动视图's content view won'知道应该有多高 .

    enter image description here

    注意:

    • 如果我们将内容垂直滚动,那么我们将设置内容视图's width to be equal to the scroll view' s父级的宽度 .

    Add content

    添加三个 UIView 并为它们提供所有约束 . 我用了8点的余量 .

    enter image description here

    约束:

    • 绿色视图:固定顶部,左侧和底部边缘 . 使宽度为400 .

    • 红色视图:固定顶部,左侧和底部边缘 . 宽度为300 .

    • 紫色视图:固定所有四个边缘 . 无论剩余空间是什么,都要做宽度(在这种情况下为268) .

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

    完成了

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

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

    进一步研究

  • 0

    通过在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]];
    
  • 1

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

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

    下图将描述:
    enter image description here

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

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

    How to add constraints for views in the UIScrollView:

    • 在内容视图中对视图的位置进行成像 .

    • 将内部,右侧,底部,左侧间距添加到内容视图的边缘,此外,还添加这些视图的宽度和高度 .

    一切都完成了 .

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

    Conclusion: the key point to understand AutoLayout with UIScrollView is inner views make constraints against the content view but not UIScrollView itself.

    example code

  • 1

    以下解决方案适用于 scrollView with autolayout and without contentSize

    • 拖放一个scrollView到viewController并应用任何约束来覆盖你想要的空间 .

    • 在scrollView中拖放 UIView 并使其覆盖scrollView的整个空间并从scrollView应用 constraints to be top, left, right, bottom space .

    • 根据滚动的需要设置内部视图的 height (以及需要水平滚动时的宽度) . 如果需要,也可以从代码中完成此部分 .

    • 严重 . 在点(3)中将高度设置为某个较大值后,返回到第(2)点并确保设置顶部,左侧,右侧,底部值 back to zero ,因为当您强制更改时,Xcode可能已经为您更改了它们(3)中的高度 .

    而且你已经完成了 . 现在,您可以在此视图上添加任意数量的控件,并应用彼此相关的约束(如果没有此视图,这些约束似乎不起作用) . 如果您不想使用此视图,则必须为与scrollView相关的每个控件应用约束(彼此不相关) .


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

    Critical . 让我们说清楚UIScrollView是 1000 wide 和100高 . (事实上,通常这些值是动态的,当然,取决于设备的宽度等 . 但现在只说1000宽和100高 . )假设您正在进行水平滚动 . 所以在UIScrollView中放置一个UIView . (即"content view" . )将内容视图top,bottom,leading,trailing的所有四个约束设置为滚动视图 . Make them all zero even if that seems wrong. 将内容UIView的高度设置为100并忘记它 . 现在:您想要水平滚动,因此将内容视图的宽度设置为1225 .

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

    ...你没有将尾随宽度设置为负225 ...

    你会认为你必须"match"宽度 as you normally would . 但如果你这样做, it will not work at all.

    您必须将前导和尾随数字设置为零,从不为负数(即使宽度为“更大”)

    有趣的是,您实际上可以将前导/尾随数字设置为任何 positive 值(例如"50"),它会为您提供反弹余量 . (它通常看起来很棒:尝试一下 . )任何一端的负值都是"silently break" .

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

    将“帮助”将这些值设置为负数!

    因为它试图为你自动计算它们 . 如果是这样,它会默默地打破 . 在第一个实例中将所有四个值都设置为零 . 并且将内容视图的宽度设置为比示例中的“1000”宽得多 .


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

  • 5

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

  • 1

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

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

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

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

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

  • 3

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

    别 .

    使用带有静态单元格的UITableViewController .

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

  • 1

    你应该像这样组织你的布局
    ViewControllerView 包含 ScrollViewScrollView 包含 ContainerViewContainerView 包含2 Labels

    enter image description here

    Then follow 3 steps for make your ScrollView 可以滚动

    • ScrollView 引脚(顶部/右侧/底部/左侧)设置为 ViewControllerView

    • ContainerView 引脚(顶部/右侧/底部/左侧)设置为 ScrollView

    • 设置 Horizontally in Containerdon't set Vertically in Container

    • Label1 pin( top / right / left)改为 ContainerView

    • Label1 pin(右/左/ bottom )到 ContainerViewtopLabel1

    enter image description here

    HERE是演示项目

    希望这有帮助

  • 4

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

    • 从小处开始:即使这意味着重新创建故事板视图,只需从几个元素开始并慢慢构建视图,确保在添加一些元素后测试滚动是否有效 .

    • 关闭translatesAutoresizingMaskIntoConstraints对所有事情:这总是导致约束冲突的原因 .

    • 正确设置UIScrollView约束:确保滚动视图的所有边都连接到父视图,否则它根本不会展开 .

  • 1

    经过一段时间处理这个问题,我终于找到了解决方案 . 我正在使用通用 class 规模的故事板(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;
    

    效果很好 .

  • 30

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

    我花了几天时间尝试用Autolayout来做这件事,并且接近但从未足够接近 . 所以最后我还要在viewDidLoad中为每个屏幕添加3行代码 .

    请参阅下面的解

    • 创建滚动视图并使用您想要的任何对象填充它

    • 开启自动布局

    • 然后垂直和水平居中ScrollView
      Centre ScrollView

    • 选择 View ,然后选择 'Add missing constraints' - 然后选择它

    • 结果是生成了很多约束 . 为视图创建了2个新的:'Horiz space scrollview to View'和'Vert space scrollview to view',反之亦然 .

    • 删除'Horiz space scrollview to View',这样您现在在视图上有3个约束 . 2用于在视图中输入滚动视图,另一个用于在滚动视图和视图之间设置垂直空间

    • 现在通过单击并将Ctrl拖动到头文件并将其拖动到头文件并创建NSLayoutConstraint IBOutlet(我称为mine constraintVertVtoSV)将Vert约束链接到您的代码

    • 现在转到.m文件并将这些代码行添加到viewDidLoad中(使用填充量来获取正确的顶点居中)

    if (IPAD)
    
    • 现在应该在所有设备上运行并正确居中并仍然正确滚动 .
  • 3

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

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

    类似的问题我今天有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 .

  • 48

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

  • 8

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

    Contraints

    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)
    }
    
  • 1

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

相关问题