以编程方式设置约束

时间:2013-04-09 03:56:52

标签: ios objective-c uiviewcontroller uiscrollview nslayoutconstraint

我正在试验如何使用UIScrollView。经过多次麻烦,我终于掌握了它。但现在我似乎遇到了另一个障碍。

在这个简单的应用程序中,我有一个滚动视图,为了使它工作,我必须将视图的底部空间设置为滚动视图约束为0,如here所述,它工作正常。我是通过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) (     “”     “” )

有人可以告诉我怎么做吗?我还附上了一个演示项目here,以便您可以更好地了解该问题。

更新

首先感谢您的回复。使用答案中提到的方法,我能够使它工作。然而,它的情况略有不同。现在我正在尝试以编程方式将视图加载到viewcontroller。

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

UITableViewController显示选项列表。根据用户的选择,该批次中的特定UIView将被UIViewController加载到UIScrollView

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

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

4 个答案:

答案 0 :(得分:56)

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

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

  2. 我不是故意的,但你的项目有一堆冗余的代码。 E.g。

    • 您正在为滚动视图设置框架,但这受到约束的约束,因此不执行任何操作(即,当应用约束时,将替换任何手动设置的frame设置)。通常,在自动布局中,请勿尝试直接更改frame:编辑约束。但是,无论如何都不需要改变约束,所以重点是没有意义。

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

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

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

    • 通过将您的观点存储在NIB中,您的生活将变得更加艰难。如果你留在故事板世界里会更容易。如果你真的需要,我们可以帮你做NIB的东西,但为什么要让自己的生活变得如此艰难呢?

    • 使用单元格原型来简化表格中单元格的设计。您还可以定义从单元格到下一个场景的segue。这消除了编写任何didSelectRowAtIndexPathprepareForSegue代码的任何需要。显然,如果你有一些东西你需要传递到下一个场景,一定要使用prepareForSegue,但到目前为止你所提出的并不需要,所以我在我的例子中对它进行了评论。

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

      textViewDidChange

      请注意,我的文本视图有两个高度限制,一个强制性的最小高度约束,以及一个中等优先级约束,我根据文本量更改了上面的优先级约束。重点是它说明了以编程方式更改约束的实际示例。您根本不应该使用scrollview的底部约束,但这显示了您可能想要调整约束的实际示例。


  4. 在IB中添加滚动视图时,它将自动获得您需要的所有约束。您可能不希望以编程方式添加约束(至少在没有删除现有底部约束的情况下)。

    两种方法可能更简单:

    1. 为现有的底部约束创建#pragma mark - UITextViewDelegate - (void)textViewDidChange:(UITextView *)textView { self.textViewHeightConstraint.constant = textView.contentSize.height; [self.scrollView layoutIfNeeded]; } ,例如IBOutlet。那你就可以做到

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

    3. 如果您决定添加新约束,则可以通过编程方式删除旧的底部约束,或者将旧的底部约束的优先级设置为尽可能低,这样就可以实现新约束(具有更高的约束)优先级将优先,并且优先不应用旧的,低优先级的底部约束。

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

答案 1 :(得分:21)

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

enter image description here

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

enter image description here

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

答案 2 :(得分: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的回答也会奏效!

答案 3 :(得分: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);
}];

尽力排序;)