26

我正在尝试如何使用 UIScrollView。经过一番折腾,我终于掌握了窍门。但现在我似乎遇到了另一个障碍。

在这个简单的应用程序中,我有一个滚动视图,为了让它工作,我必须将视图的底部空间设置为滚动视图约束为 0,如此处所述它工作正常。我是通过IB做的。

现在我遇到了一个场景,我必须以编程方式完成那部分。我在viewDidLoad方法中添加了以下代码。

NSLayoutConstraint *bottomSpaceConstraint = [NSLayoutConstraint constraintWithItem:self.view
                                                                             attribute:NSLayoutAttributeBottom
                                                                             relatedBy:NSLayoutRelationEqual
                                                                                toItem:self.scrollView
                                                                             attribute:NSLayoutAttributeBottom
                                                                            multiplier:1.0
                                                                              constant:0.0];
[self.view addConstraint:bottomSpaceConstraint];

但这似乎不起作用。它在控制台窗口中输出以下消息,我不知道该怎么做。

无法同时满足约束。以下列表中的至少一个约束可能是您不想要的。试试这个:(1)查看每个约束并尝试找出您不期望的;(2) 找到添加了一个或多个不需要的约束的代码并修复它。(注意:如果您看到不理解的 NSAutoresizingMaskLayoutConstraints,请参阅 UIView 属性 translatesAutoresizingMaskIntoConstraints 的文档)(“”,“”)

有人可以告诉我该怎么做吗?我还在此处附加了一个演示项目,以便您对这个问题有更好的了解。

更新:

首先感谢您的回复。使用答案中提到的方法,我能够让它工作。然而,在稍微不同的情况下,它不是。现在我正在尝试以编程方式将视图加载到视图控制器上。

如果我可以进一步解释。有 2 个视图控制器。第一个是 a UITableViewController,第二个是 a UIViewController。里面那个UIScrollView. 还有多个UIViews 并且其中一些视图的高度超过了屏幕的正常高度。

显示UITableViewController选项列表。根据用户的选择,一个特定UIView的批次将被加载UIViewControllerUIScrollView.

在这种情况下,上述方法不起作用。滚动没有发生。由于我要单独加载视图,我是否必须做一些不同的事情?

我在这里上传了一个演示项目,以便您可以看到它的实际效果。请查看并从表格视图列表中Email.xib选择电子邮件。

4

4 回答 4

57

根据对您的代码的审查,一些评论:

  1. 当视图出现时,您通常不需要调整约束。如果您发现自己这样做了,这通常意味着您没有正确配置故事板(或至少没有有效地配置)。您真正需要设置/创建约束的唯一时间是(a)您正在以编程方式添加视图(我的建议是工作比它的价值更多);或 (b) 您需要对约束进行一些运行时调整(参见下面第 3 点下的第三个项目符号)。

  2. 我并不是要详细说明它,但是您的项目有一堆冗余代码。例如

    • 您正在为滚动视图设置框架,但这是由约束控制的,因此什么都不做(即当应用约束时,任何手动设置的frame设置都将被替换)。一般来说,在自动布局中,不要尝试frame直接更改:编辑约束。但是,无论如何都不需要改变约束,所以这一点没有实际意义。

    • 您正在为滚动视图设置内容大小,但在自动布局中,这也受(子视图的)约束控制,因此这是不必要的。

    • 您正在为滚动视图设置约束(已经为零),但是您没有将 NIB 中的视图添加到滚动视图中,也破坏了那里的任何意图。最初的问题是如何更改滚动视图的底部约束。但它的底部约束已经为零,所以我认为没有理由再次将其设置为零。

  3. 我建议对您的项目进行更彻底的简化:

    • 通过将视图存储在 NIB 中,您的生活变得更加艰难。如果您留在故事板世界中,那就容易多了。如果您真的需要,我们可以帮助您完成 NIB 的工作,但为什么要让自己的生活如此艰难呢?

    • 使用单元原型来促进表格中单元的设计。您还可以定义从单元到下一个场景的转场。这消除了编写任何代码的didSelectRowAtIndexPath需要。prepareForSegue显然,如果您有一些需要传递到下一个场景的内容,请务必使用prepareForSegue,但到目前为止您所展示的内容都不需要这样做,因此我在示例中对其进行了注释。

    • 假设您正在寻找以编程方式更改约束的实际示例,我已经设置了场景,以便文本视图将根据文本视图中的文本以编程方式更改其高度。IBOutlet与往常一样,在更改 IB 为我创建的现有约束时,我认为设置约束并直接编辑约束的constant属性会更有效,而不是遍历约束以找到有问题的约束,这就是我所做的。所以我将视图控制器设置为文本视图的委托,并编写了一个textViewDidChange更新文本视图高度约束的代码:

      #pragma mark - UITextViewDelegate
      
      - (void)textViewDidChange:(UITextView *)textView
      {
          self.textViewHeightConstraint.constant = textView.contentSize.height;
          [self.scrollView layoutIfNeeded];
      }
      

      请注意,我的文本视图有两个高度约束,一个强制性的最小高度约束和一个中等优先级约束,我根据文本量在上面进行了更改。要点是它说明了以编程方式更改约束的实际示例。您根本不必纠结于滚动视图的底部约束,但这显示了您何时可能想要调整约束的真实示例。


当您在 IB 中添加滚动视图时,它会自动获取您需要的所有约束。您可能不想以编程方式添加约束(至少在不删除现有底部约束的情况下不会)。

两种方法可能更简单:

  1. 为您现有的底部约束创建一个IBOutlet,比如说scrollViewBottomConstraint。然后你可以做

    self.scrollViewBottomConstraint.constant = 0.0;
    
  2. 或者最初在 IB 中创建您的视图,其中底部约束为 0.0,然后您根本不需要以编程方式执行任何操作。如果要布局长滚动视图及其子视图,请选择控制器,将其模拟指标从“推断”设置为“自由形式”。然后你可以改变视图的大小,将滚动视图的顶部和底部约束设置为零,在滚动视图内布局你想要的一切,然后当视图在运行时呈现时,视图将被适当地调整大小,因为你'已将滚动视图的顶部和底部约束定义为 0.0,它将正确调整大小。它在 IB 中看起来有点奇怪,但是当应用程序运行时它就像一个魅力。

  3. 如果您决定添加一个新约束,您可以以编程方式删除旧的底部约束,或者将旧的底部约束的优先级设置得尽可能低,这样您的新约束(具有更高优先级)将优先,并且旧的、低优先级的底部约束将不会被优雅地应用。

但是你绝对不想只添加一个新的约束。

于 2013-04-09T04:55:20.757 回答
21

可以创建出口来表示视图控制器中的布局约束。只需在界面生成器中选择您想要的约束(例如,通过您正在安排的视图的测量窗格上的“选择和编辑”)。然后转到出口窗格并将“新引用出口”拖到您的代码文件(.h 或 .m)。这会将约束绑定到NSLayoutConstraint您可以从控制器访问并动态调整的实例(通常通过constant属性,因为它根本不是常量而命名不佳)。

在此处输入图像描述

(请注意,在 XCode 6 中,您可以双击约束来选择它进行编辑。)

在此处输入图像描述

但是,在界面构建器中调整布局时要小心,因为您最终可能会删除约束并且必须将其重新绑定到插座。

于 2014-04-01T23:23:34.827 回答
12

查看控制台信息,我觉得您在添加两个相同类型的约束时会产生歧义。

因此,不要创建和添加新的约束,而是尝试更新已经在约束数组中的先前约束。

for(NSLayoutConstraint *constraint in self.view.constraints)
    {
        if(constraint.firstAttribute == NSLayoutAttributeBottom && constraint.secondAttribute == NSLayoutAttributeBottom &&
           constraint.firstItem == self.view && constraint.secondItem == self.scrollView)
        {
            constraint.constant = 0.0;
        }
    }

希望这可以帮助

甚至 Rob 的回答也会奏效!

于 2013-04-09T04:56:59.500 回答
2

您可以使用https://github.com/SnapKit/Masonry以编程方式添加约束。

它是 AutoLayout NSLayoutConstraints 的强大功能,具有简化的、可链接的和富有表现力的语法。支持 iOS 和 OSX 自动布局。

UIView *superview = self.view;

UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[superview addConstraints:@[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
                             attribute:NSLayoutAttributeTop
                             relatedBy:NSLayoutRelationEqual
                                toItem:superview
                             attribute:NSLayoutAttributeTop
                            multiplier:1.0
                              constant:padding.top],

[NSLayoutConstraint constraintWithItem:view1
                             attribute:NSLayoutAttributeLeft
                             relatedBy:NSLayoutRelationEqual
                                toItem:superview
                             attribute:NSLayoutAttributeLeft
                            multiplier:1.0
                              constant:padding.left],

[NSLayoutConstraint constraintWithItem:view1
                             attribute:NSLayoutAttributeBottom
                             relatedBy:NSLayoutRelationEqual
                                toItem:superview
                             attribute:NSLayoutAttributeBottom
                            multiplier:1.0
                              constant:-padding.bottom],

[NSLayoutConstraint constraintWithItem:view1
                             attribute:NSLayoutAttributeRight
                             relatedBy:NSLayoutRelationEqual
                                toItem:superview
                             attribute:NSLayoutAttributeRight
                            multiplier:1
                              constant:-padding.right],]];

短短几行

这是使用 MASConstraintMaker 创建的相同约束

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
    make.left.equalTo(superview.mas_left).with.offset(padding.left);
    make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
    make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];

甚至更短

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(superview).with.insets(padding);
}];

尽力而为;)

于 2016-08-18T17:02:14.697 回答