ios 以编程方式设置约束

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15893022/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 23:07:26  来源:igfitidea点击:

Setting constraints programmatically

iosobjective-cuiviewcontrolleruiscrollviewnslayoutconstraint

提问by Isuru

I'm experimenting with how to use UIScrollView. After much trouble, I finally got the hang of it. But now I've seem to hit another snag.

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

In this simple app, I have a scroll view with and in order for it to work, I have to set the view's bottom space to scrollview constraint to 0 as described hereand it works fine. I'm doing it through the IB.

在这个简单的应用程序,我有一个滚动视图与和为了使它的工作,我所描述的视图的底部空间,滚动型的限制设置为0,在这里,它工作正常。我是通过IB做的。

Now I've come across a scenario where I have to do that part programmatically. I've added the below code in the viewDidLoadmethod.

现在我遇到了一个场景,我必须以编程方式完成那部分。我在方法中添加了以下代码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];

But it doesn't seem to work. It outputs the following message in the console window adn i don't know what to make of it.

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

Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) ( "", "" )

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

Can someone please tell me how to do this? I've also attached a demo project hereso that you can get a better idea on the issue.

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

UPDATE:

更新:

First off thanks for the responses. Using the ways mentioned in the answers I was able to get it working. However ina slightly different scenario its not. Now I'm trying to load a view onto a viewcontroller programatically.

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

If I may explain further. There are 2 view controllers. First one being a UITableViewControllerand the second one a UIViewController. Inside that a UIScrollView. Also There are multiple UIViews and some of those views' height exceeds the normal height of the screen.

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

The UITableViewControllerdisplays a list of options. Based on the user's selection, a particular UIViewout of the lot will be loaded into the UIViewControllerwith the UIScrollView.

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

In this scenario, the above method doesn't work. The scrolling isn't happening. Do I have to do something different since I'm loading the view separately?

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

I've uploaded a demo project hereso that you can see it in action. Please see the Email.xiband select Emailfrom the table view list.

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

回答by Rob

Based upon a review of your code, a few comments:

根据对您的代码的,提出几点意见:

  1. You generally don't need to adjust constraints when a view appears. If you find yourself doing this, it often means that you haven't configured your storyboard correctly (or at least, not efficiently). The only time you really need to set/create constraints is either (a) you're adding views programmatically (which I'm suggesting is more work than it's worth); or (b) you need to do some runtime adjustment of constraints (see third bullet under point 3, below).

  2. I don't mean to belabor it, but your project had a bunch of redundant code. E.g.

    • You were setting the frame for the scroll view, but that is governed by constraints, so that does nothing (i.e. when the constraints are applied, any manually set framesettings will be replaced). In general, in auto layout, don't try changing framedirectly: Edit the constraints. But, no changing of constraints is needed at all anyway, so the point is moot.

    • You were setting the content size for the scroll view, but in auto layout, that, too, is governed by constraints (of the subviews), so that was unnecessary.

    • You were setting constraints for the scrollview (which were already zero), but then you weren't adding the view from the NIB into the scrollview, defeating any intent there, too. The original question was how to change the bottom constraint of the scroll view. But the bottom constraint for that is already zero, so I see no reason to set it to zero again.

  3. I'd suggest a more radical simplification of your project:

    • You're making life much harder on yourself by storing your views in NIBs. It's much easier if you stay within the the storyboard world. We can help you do the NIB stuff if you really need to, but why make life so hard on yourself?

    • Use cell prototypes to facilitate the design of the cells in your table. You can also define the segues to go from the cells to the next scene. This eliminates any need to write any didSelectRowAtIndexPathor prepareForSeguecode. Clearly, if you have something you need to pass to the next scene, by all means use prepareForSegue, but nothing you've presented thus far requires that, so I've commented it out in my examples.

    • Assuming you were looking for a practical example of programmatically changing constraints, I've set up the scene so that the text view will change its height programmatically, based upon the text in the text view. As always, rather than iterating through the constraints to find the one in question, when altering an existing constraint that IB created for me, I think it's far more efficient to set up an IBOutletfor the constraint, and edit the constantproperty for the constraint directly, so that's what I've done. So I set up the view controller to be the delegate of the text view, and wrote a textViewDidChangethat updated the text view's height constraint:

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

      Note, my text view has two height constraints, a mandatory minimum height constraint, and a medium priority constraint that I change above based upon the amount of text. The main point is that it illustrates a practical example of changing constraints programmatically. You shouldn't have to muck around with the scrollview's bottom constraint at all, but this is shows a real-world example of when you might want to adjust a constraint.

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

  2. 我并不是要说这些,但你的项目有一堆冗余代码。例如

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

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

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

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

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

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

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

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

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



When you add a scrollview in IB, it will automatically get all the constraints you need. You probably don't want to be adding a constraint programmatically (at least not without removing the existing bottom constraint).

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

Two approaches might be simpler:

两种方法可能更简单:

  1. Create an IBOutletfor your existing bottom constraint, say scrollViewBottomConstraint. Then you can just do

    self.scrollViewBottomConstraint.constant = 0.0;
    
  2. Or create your view initially in IB where the bottom constraint is 0.0 and then you don't have to do anything programmatically at all. If you want to layout a long scrollview and it's subviews, select the controller, set it's simulated metrics from "inferred" to "free form". Then you can change the size of the view, set the scrollview's top and bottom constraints to be zero, layout everything you want inside the scroll view, and then when the view is presented at runtime, the view will be resized appropriately, and because you've defined the scrollview's top and bottom constraints to be 0.0, it will be resized properly. It looks a bit odd in IB, but it works like a charm when the app runs.

  3. If you're determined to add a new constraint, you could either programmatically remove the old bottom constraint, or set the old bottom constraints' priority down as low as possible, and that way your new constraint (with higher priority) will take precedence, and the old, low-priority bottom constraint will gracefully not be applied.

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

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

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

But you definitely don't want to just add a new constraint.

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

回答by devios1

It's possible to create outlets to represent layout constraints in your view controller. Just select the constraint you want in interface builder (e.g. via "select and edit" on the measurements pane of the view you are arranging). Then go to the outlets pane and drag a "New Referencing Outlet" to your code file (.h or .m). This will bind the constraint to an NSLayoutConstraintinstance that you can access from your controller and adjust dynamically on the fly (generally via the constantproperty, which is poorly named because it's not a constant at all).

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

enter image description here

在此处输入图片说明

(Note that in XCode 6 you can double-click the constraint to select it for editing.)

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

enter image description here

在此处输入图片说明

Be careful when adjusting the layout in interface builder, however, as you may end up deleting the constraint and have to re-bind it to the outlet.

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

回答by Kunal

Looking at the console information, i feel that you are creating ambiguity when you add two same type of constraint.

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

So instead of creating and adding new constraint, try updating the previous constraint that is already in the constraints array.

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

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;
        }
    }

Hope this helps

希望这可以帮助

Even Rob's answer will work!

即使是罗布的回答也会奏效!

回答by ERbittuu

You can use https://github.com/SnapKit/Masonryfor adding constraints programmatically.

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

It is power of AutoLayout NSLayoutConstraints with a simplified, chainable and expressive syntax. Supports iOS and OSX Auto Layout.

它是 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],]];

in just few lines

只需几行

Heres the same constraints created using MASConstraintMaker

这是使用 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);
}];

Or even shorter

甚至更短

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

do your best is sort ;)

尽力而为;)