以编程方式设置约束

以编程方式设置约束

问题描述:

我在试验如何使用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,以便您可以更好地了解这个问题。

UPDATE:

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

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

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

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

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

基于你的代码,进行审查几点意见:

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

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

    • 你被设置滚动视图的帧,但通过限制的约束,所以什么也不做(即当施加的限制,任何手动设置frame设置将被替代)。一般来说,在自动布局中,不要直接尝试更改frame:编辑约束。但是,无论如何不需要改变约束条件,所以这一点是没有意义的。

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

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

  3. 我建议你的项目更激进的简化:

    • 你被存储在发钞银行的意见使生活在自己更难。如果你留在故事板世界里,它会容易得多。如果你真的需要,我们可以帮助你做NIB的东西,但为什么让自己的生活如此艰难?

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

    • 假设您正在寻找一个以编程方式更改约束的实际示例,我已经设置了场景以便文本视图将基于文本视图中的文本以编程方式更改其高度。像往常一样,不是迭代通过约束来找到问题,而是在更改IB为我创建的现有约束时,我认为为约束设置IBOutlet以及编辑约束的constant属性效率更高直接,这就是我所做的。所以我设置视图控制器是文本视图的代表,并写道,更新文本视图的高度约束textViewDidChange

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

      注意,我的文本视图有两个高度限制,强制性的最小高度约束,以及根据文本数量改变的中等优先级约束。主要的一点是,它说明了一个以编程方式改变约束的实例。你根本不应该混淆scrollview的底部约束,但是这是一个真实世界的例子,你可能想调整一个约束。


当您添加在IB一个滚动视图,它会自动得到你所需要的所有约束。您可能不希望以编程方式添加约束(至少在不移除现有底部约束的情况下)。

两种方法可能比较简单:

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

    self.scrollViewBottomConstraint.constant = 0.0; 
    
  2. 或者最初创建在IB的图,其中底部限制是0.0,那么你就不必做任何编程的。如果要布置长滚动视图和子视图,请选择控制器,将其模拟度量标准从“推断”设置为“自由格式”。然后,您可以更改视图的大小,将滚动视图的顶部和底部约束设置为零,在滚动视图中布置所需的所有内容,然后在运行时呈现视图时,视图将适当调整大小,并且由于您已经将scrollview的顶部和底部约束定义为0.0,它将被正确调整大小。它在IB中看起来有点奇怪,但它在应用运行时就像一个魅力。

  3. 如果您决定添加一个新的约束条件,您可以通过编程方式删除旧的底部约束条件,或者将旧的底部约束条件的优先级降低到尽可能低的位置,这样您的新约束条件(具有更高的优先级)将优先,并且优先不适用旧的,低优先级的底部约束。

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

+0

喜罗布,感谢您的支持ponse。我更新了现有的约束,它工作正常。然而,现在我试图从一个单独的'UIView'加载视图,然后它不工作。在这种情况下,我必须做些不同的事情吗?我更新了我的问题。 – Isuru 2013-04-09 06:35:02

+1

@Isuru我已经完成了你的项目,并有很多评论,所以看到我修改后的答案。另外,如果您下载我的示例项目,这些项目是原始代码库的简化版本,您将在源代码中看到额外的评论。 – Rob 2013-04-09 17:56:16

+1

嗨,罗布,非常感谢你的这一切。这绝对是我获得问题的最佳答案之一。我经历了这两个项目并学到了很多东西。我进入iOS开发4个月,所以我对一些东西很陌生,但都很好。我想使用xib文件的原因是我正在使用这个应用程序工作,老年人坚持使用一个'UIViewController'并拥有邮件类型用户界面和_templates_ nib文件,因为它更易于管理,因为我们已经有了很多场景躺在身边。 – Isuru 2013-04-10 06:08:56

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

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

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

希望这有助于

即使罗布的回答将工作!

+0

您好,Kunal,感谢您的回复。我使用你的方法更新了现有的约束,它工作正常。然而,现在我试图从一个单独的'UIView'加载视图,然后它不工作。在这种情况下,我必须做些不同的事情吗?我更新了我的问题。 – Isuru 2013-04-09 06:35:30

+0

对不起,延迟回复。在NewMailViewController下面的代码导致问题如果([self.mailTypeName isEqualToString:@“Email”]){NSArray * nib = [[NSBundle mainBundle] loadNibNamed:@“Email”owner:self options:nil]; self.view = [nib objectAtIndex:0]; } 在这里,您将self.view重新分配给email.xib视图(并且email.xib没有滚动视图)。希望这可以帮助。 – Kunal 2013-04-09 09:28:46

+0

它没问题。 :) 我懂了。但是我在_Email.xib_中放了一个'UIScrollView',但它仍然无法工作。我该怎么办? – Isuru 2013-04-09 10:05:56

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

enter image description here

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

enter image description here

在Interface Builder调整布局时,但要小心,因为你可能最终删除约束,必须重新将其绑定到出口。

+0

这是非常有用的,完美工作thx – Guy 2015-04-19 09:52:02

您可以使用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); 
}]; 

做你最好是那种创造;)

+0

'TinyConstraints'是我现在最喜欢的:https://github.com/roberthein/TinyConstraints – SushiHangover 2017-02-16 07:12:56