【问题标题】:Auto Layout constraint breaks when set active设置为活动时自动布局约束中断
【发布时间】:2019-01-19 13:34:08
【问题描述】:

我有一组 AL 约束定位一个子 vc,它有两个位置,展开和折叠。

我发现当我添加折叠约束时,顶部锚点到底部锚点约束带有一个常量,当第一次创建 vc 时,当我激活它时似乎有额外的间距。似乎是因为当时没有实际高度。

当我在 viewDidLayoutSubviews 中添加约束时,额外的间距消失了,并且约束正常运行。除了现在当我在动画中的约束之间切换时,当我切换到展开的约束并且约束中断时,我无法停用折叠的约束。可能是因为在整个过渡动画中都会调用 viewDidLayoutSubviews。

这是 vc 设置的摘要。

var foregroundExpandedConstraint: NSLayoutConstraint!
var foregroundCollapsedConstraint: NSLayoutConstraint!

var foregroundViewController: UIViewController? {
    didSet {

        setupforegroundViewController(foregroundViewController: foregroundViewController!)
    }
}

func setupforegroundViewController(foregroundViewController: UIViewController) {

    addChildViewController(foregroundViewController)
    foregroundViewController.didMove(toParentViewController: self)

    guard let foregroundView = foregroundViewController.view else { return }
    foregroundView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(foregroundView)

    foregroundExpandedConstraint = foregroundView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 15)

    let height =  view.safeAreaLayoutGuide.layoutFrame.height - 50 - 15
    let cellHeight = ((height) / 6)        
    foregroundCollapsedConstraint = NSLayoutConstraint(item: foregroundView, attribute: .top, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .bottom, multiplier: 1, constant: (-cellHeight) * 2 - 50)

    let foregroundViewControllerViewConstraints = [
        foregroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        foregroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        foregroundView.heightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.heightAnchor, constant: -50 - 15),
        foregroundExpandedConstraint!
        ]


    NSLayoutConstraint.activate(foregroundViewControllerViewConstraints)
}

这里的动画是使用 UIViewPropertyAnimator 执行的。

func animateTransitionIfNeeded(state: ForegroundState, duration: TimeInterval) {

    let containerFrameAnimator = UIViewPropertyAnimator(duration: duration, dampingRatio: 1) {
        [unowned self] in

        switch state {
        case .expanded:
            self.foregroundCollapsedConstraint?.isActive = false
            self.foregroundExpandedConstraint?.isActive = true
            self.view.layoutIfNeeded()
        case .collapsed:
            self.foregroundExpandedConstraint?.isActive = false
            self.foregroundCollapsedConstraint?.isActive = true
            self.view.layoutIfNeeded()
        }
    }

    containerFrameAnimator.addCompletion {  [weak self] (position) in

        if position == .start {
            switch state {
            case .collapsed:
                self?.foregroundCollapsedConstraint?.isActive = false
                self?.foregroundExpandedConstraint?.isActive = true
                self?.foregroundIsExpanded = true
                self?.view.layoutIfNeeded()
            case .expanded:
                self?.foregroundExpandedConstraint?.isActive = false
                self?.foregroundCollapsedConstraint?.isActive = true
                self?.foregroundIsExpanded = false
                self?.view.layoutIfNeeded()
            }
        } else if position == .end {
            switch state {
            case .collapsed:
                self?.foregroundExpandedConstraint?.isActive = false
                self?.foregroundCollapsedConstraint?.isActive = true
                self?.foregroundIsExpanded = false
            case .expanded:
                self?.foregroundExpandedConstraint?.isActive = false
                self?.foregroundCollapsedConstraint?.isActive = true
                self?.foregroundIsExpanded = true
            }
        }
        self?.runningAnimations.removeAll()
    }

再次重申,当我使用以下代码时,将约束设置为 vc 添加到视图层次结构中,它没有正确布局。检查我看到的约束在视图没有布局子视图被调用后发生了变化。除折叠约束外,每个约束都会相应更改。

当我在视图中添加折叠约束时,布局子视图是否正常运行,但是我无法继续停用它并且约束中断。

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    let height =  view.safeAreaLayoutGuide.layoutFrame.height - 50 - 15
    let cellHeight = ((height) / 6)

    if let v = foregroundViewController?.view {
        foregroundCollapsedConstraint = NSLayoutConstraint(item: v, attribute: .top, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .bottom, multiplier: 1, constant: (-cellHeight) * 2 - 50)
    }
}

编辑:我创建了一个 repo 来展示这个问题:https://github.com/louiss98/UIViewPropertyAnimator-Layout-Test

有什么建议吗?

【问题讨论】:

  • 如果您展示您的代码(请参阅:minimal reproducible example),您可能会获得更多帮助。在没有看到您实际在做什么的情况下,您最好更改两个约束的 priority,而不是尝试激活/停用。
  • @DonMag 我已经包含了相关代码,你能看看它,看看是否需要改变优先级。另外我一直认为将约束设置为 active 与将其优先级设置为 required (999) 相同,反之亦然。
  • 我不知道。但请查看here 了解一些一般性见解
  • @Stefan - 创建一个显示您的问题的示例项目......尽可能简单。如果无法运行您的代码,真的很难说出可能出了什么问题。
  • @DonMag 这是我创建的展示该问题的存储库的链接:github.com/louiss98/UIViewPropertyAnimator-Layout-Test。看起来这可能是在转换期间如何设置约束的问题。

标签: swift uiviewcontroller autolayout nslayoutconstraint


【解决方案1】:

您可以通过更改常量而不是创建新约束来消除“损坏”约束。

在您的viewDidLayoutSubviews() 函数中,

改变:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    let height =  view.safeAreaLayoutGuide.layoutFrame.height - 50 - 15
    let cellHeight = ((height) / 6)

    foregroundCollapsedConstraint = NSLayoutConstraint(item: testViewController.view, attribute: .top, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .bottom, multiplier: 1, constant: (-cellHeight) * 2 - 50)
}

到:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    let height =  view.safeAreaLayoutGuide.layoutFrame.height - 50 - 15
    let cellHeight = ((height) / 6)

    foregroundCollapsedConstraint.constant = (-cellHeight) * 2 - 50
}

【讨论】:

    猜你喜欢
    • 2014-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多