正在跳过 UIView 动画

UIView animation is being skipped

好的快速背景:我在两个控制器之间进行切换:我的家庭控制器和一个以地图作为视图的控制器。当用户点击搜索栏的 textField 时触发 segue

我已经包含了动画前后的图像

过程如下:

我将搜索栏添加到 let keyWindow = UIApplication.shared.keyWindow 中与 table 视图中相同的位置

let preLeadingConstraint = searchView.leadingAnchor.constraint(equalTo: keyWindow.leadingAnchor, constant: 8)
let preTrailingConstraint = searchView.trailingAnchor.constraint(equalTo: keyWindow.trailingAnchor, constant: -8)
let preTopConstraint = searchView.topAnchor.constraint(equalTo: keyWindow.topAnchor, constant: originY)

NSLayoutConstraint.activate([preLeadingConstraint, preTrailingConstraint, preTopConstraint])
keyWindow.layoutIfNeeded()

这里没问题。另外:搜索栏在此之前激活了高度限制:heightAnchor.constraint(equalToConstant: PlacesSearchView.height).isActive = true

下一步是这个动画(自定义转场):

UIView.animate(withDuration: self.duration, animations: {
    homeController.view.frame.origin.x = -Global.width
    self.destination.view.frame.origin.x = 0
}) { _ in
    homeController.navigationController?.viewControllers.append(self.destination)
    self.destination.navigationController?.setNavigationBarHidden(false, animated: true)
}

非常简单,也没有问题

这部分是问题:

我停用了旧的搜索栏限制:

NSLayoutConstraint.deactivate(preConstraints)

我创建了关闭按钮并将其锚定到 keyWindow:

let side: CGFloat = 25
let margin: CGFloat = 8

closeButton?.topAnchor.constraint(equalTo: keyWindow.layoutMarginsGuide.topAnchor, constant: margin).isActive = true
closeButton?.centerXAnchor.constraint(equalTo: keyWindow.centerXAnchor).isActive = true
closeButton?.heightAnchor.constraint(equalToConstant: side).isActive = true
closeButton?.widthAnchor.constraint(equalTo: closeButton!.heightAnchor).isActive = true

然后我将新的约束应用到搜索栏。

topAnchor.constraint(equalTo: closeButton!.bottomAnchor, constant: yAxisMargin).isActive = true
leadingAnchor.constraint(equalTo: keyWindow.leadingAnchor, constant: 0).isActive = true
trailingAnchor.constraint(equalTo: keyWindow.trailingAnchor, constant: 0).isActive = true

并锚定到键盘:

bottomKeyboardConstraint = bottomAnchor.constraint(equalTo: keyWindow.bottomAnchor, constant: -keyboardHeight)
heightConstraint.isActive = false
bottomKeyboardConstraint?.isActive = true

instantiateTableView()
subviewTableView()

那我当然会应用神奇的公式:

UIView.animate(withDuration: 0.5, animations: {
    keyWindow.layoutIfNeeded() 
    // (I also animate background color)
}

但是! 令我惊讶的是...它跳过了动画。直接进入结束状态。就像直接看图 2。我试图做(愚蠢的)事情来让某种动画开始(例如,我测试了这个的简化版本:没有添加也没有停用任何约束,只是尝试改变 topConstraint.constant 到 0 和...同样的事情:搜索栏直接向上,没有任何动画)

我没有得到什么?感谢您的耐心回答,期待您的指点!

更新: 由于某种原因,我忘记将最后一个 UIView.animate 块放在 DispatchQueue.main.async 块中。这样做会导致......有时有动画,大多数时候没有动画。这变得越来越奇怪了。此外,在此之前,我从未将我的任何动画放在 DispatchQueue.main.async 块中,即使是在其他自定义 segue 中,这也很奇怪。

好的,我明白了,事情是这样的:

基本上... DispatchQueue.main.async 是问题的根源。让我解释一下。

首先,让我们明确一点,我在 DispatchQueue.main.async 块内执行 segue:

DispatchQueue.main.async {
     self.performSegue(withIdentifier: HomeViewController.storyboardData.segueIdentifiers.places, sender: self)
}

但显然...这还不够。

然后我有了将我的 override func perform() 代码放在 DispatchQueue.main.async 中的想法(是的……我知道!)。所以它看起来像这样:

override func perform() {
    DispatchQueue.main.async {
        ...
    }
}

而且...每次都是动画!

动画很奇怪,所以我基本上决定尝试将代码的不同部分放在块中,直到它可以工作并且......它最终可以工作。 (我承认做这种事情有点遗憾)。有兴趣者详情如下:

  • 有一部分代码负责在用户单击搜索按钮时设置动画。这在将整个 perform() 代码放入 DispatchQueue.main.async 块之前起作用,所以......我回滚到工作版本
  • 现在负责动画部分,以防用户单击文本字段:现在基本上看起来像这样(segueToEditing 是我创建关闭按钮、删除旧约束、添加新约束和动画的地方):

    DispatchQueue.main.async { self.segueToEditing() }

我仍然对我的解决方案的工作原理感到困惑,但我现在知道我不会弄乱 DispatchQueue.main.async 个块。