添加视图,删除它并再次添加它会破坏AutoLayout约束

时间:2015-02-24 02:54:09

标签: ios xcode swift uikit autolayout

我有一个非常简单的Custom Container View Controller在两个视图之间交换。当添加内容视图(带有.xib的UIViewController,其中包含一个具有AutoLayout约束的按钮)时,它会很好地排列,没有冲突的约束。按下该按钮可将该视图交换为另一个视图(同一类型视图的另一个实例),该视图也可以在没有冲突约束的情况下完美排列。

当我再次交换视图以重新插入第一个视图(已经存储并且是先前删除的相同视图)时,iOS是"无法同时满足约束。"每次在第二次交换之后,iOS都会抛出不满意约束的相同警告。

显示视图控制器的代码:

func displayController(controller:UIViewController) {

    self.addChildViewController(controller)
    controller.view.setTranslatesAutoresizingMaskIntoConstraints(false)
    controller.view.frame = CGRectMake(0.0, 0.0, self.view.bounds.width, self.view.bounds.height)
    self.view.addSubview(controller.view)

    self.view.addConstraint(NSLayoutConstraint(item: controller.view, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0))
    self.view.addConstraint(NSLayoutConstraint(item: controller.view, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 0))
    self.view.addConstraint(NSLayoutConstraint(item: controller.view, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0))
    self.view.addConstraint(NSLayoutConstraint(item: controller.view, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Height, multiplier: 1.0, constant: 0))

    controller.didMoveToParentViewController(self)
    self.currentViewController = controller;
}

删除视图控制器的代码

func hideController(controller:UIViewController) {

    controller.willMoveToParentViewController(nil)
    controller.view.removeFromSuperview()
    controller.removeFromParentViewController()

    if self.currentViewController == controller {

        self.currentViewController = nil
    }
}

交换视图的代码只调用这两种方法:

func switchToViewController(controller:UIViewController) {

    if self.currentViewController != nil {

        self.hideController(self.currentViewController!)
    }

    self.displayController(controller)
}

两个子视图控制器都使用相同的.xib和一个在InterfaceBuilder中设置了约束的大按钮。

第一次添加和删除这些子视图时,它们显示正常,没有任何警告。

First child view controller Second child view controller

再次添加第一个视图后,按钮的高度错误,我得到一个"无法同时满足约束"警告。

First child view controller added a second time

2015-02-23 21:40:17.223 Swift Container View Controller[27976:832141] Unable to simultaneously satisfy constraints.
(
"<NSLayoutConstraint:0x7fa57a41eed0 'UIView-Encapsulated-Layout-Height' V:[UIView:0x7fa57a715b40(667)]>",
"<NSLayoutConstraint:0x7fa57a71d1c0 V:[UIButton:0x7fa57a71bce0'Switch to Yellow View']-(413)-|   (Names: '|':UIView:0x7fa57a71cff0 )>",
"<NSLayoutConstraint:0x7fa57a71d260 V:|-(262)-[UIButton:0x7fa57a71bce0'Switch to Yellow View']   (Names: '|':UIView:0x7fa57a71cff0 )>",
"<NSLayoutConstraint:0x7fa57a71d300 V:[UIButton:0x7fa57a71bce0'Switch to Yellow View'(125)]>",
"<NSLayoutConstraint:0x7fa57a48ff10 UIView:0x7fa57a71cff0.height == UIView:0x7fa57a715b40.height>"
)

我非常确定按钮上的约束是正确的,因为它们第一次正确布局,但随后在使用时中断。

2 个答案:

答案 0 :(得分:2)

问题在于您以多种方式定义子视图控制器的高度。这三行很重要

"<NSLayoutConstraint:0x7fa57a71d1c0 V:[UIButton:0x7fa57a71bce0'Switch to Yellow View']-(413)-|   (Names: '|':UIView:0x7fa57a71cff0 )>",

这告诉我们按钮的底部被约束到视图的底部,常数(距离)为413.

"<NSLayoutConstraint:0x7fa57a71d260 V:|-(262)-[UIButton:0x7fa57a71bce0'Switch to Yellow View']   (Names: '|':UIView:0x7fa57a71cff0 )>",

这告诉我们按钮的顶部被限制在视图的顶部,常量(距离)为262.

"<NSLayoutConstraint:0x7fa57a71d300 V:[UIButton:0x7fa57a71bce0'Switch to Yellow View'(125)]>",

这告诉我们按钮被约束到固定高度,常数(距离)为125。

为了同时满足这3个约束。视图控制器视图的高度必须为800(413 + 262 + 125),不能更高,不能更低。

将视图控制器视图添加到容器时,您尝试使用新约束再次定义高度

self.view.addConstraint(NSLayoutConstraint(item: controller.view, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Height, multiplier: 1.0, constant: 0))

在日志中显示:

"<NSLayoutConstraint:0x7fa57a41eed0 'UIView-Encapsulated-Layout-Height' V:[UIView:0x7fa57a715b40(667)]>"

由于视图不能同时处于667pts和800pts的高度,因此必须破坏某些约束,并且您的界面显示不正确。

要解决此问题,我们需要重新考虑按钮周围的约束。答案是不使用按钮的顶部和底部约束。而是定义按钮的宽度和高度,然后将按钮中心x和y与视图控制器中心x和y相匹配。

请记住,如果您需要(优先级1000)约束从边缘到边缘(即从上到下或导致尾随),这将定义超级视图的大小。最好只限制2个边和宽度和高度,或者与父节点的相对点(例如中心)匹配。

答案 1 :(得分:0)

问题是约束'UIView-Encapsulated-Layout-Height'。这是由SDK添加的约束,原因不明。它很少发生,但是当它发生时,我发现的唯一解决方法是给我自己的一个约束优先级为999.在你的情况下:

let heightConstraint = NSLayoutConstraint(item: controller.view, attribute: .Height, relatedBy: .Equal, toItem: self.view, attribute: .Height, multiplier: 1.0, constant: 0)
heightConstraint.priority = 999
self.view.addConstraint( heightConstraint)

SDK约束仅暂时添加,因此您的布局应按预期工作。