ios - 即使调用 needforlayout() ,单独类中的动画自动布局约束也会引发错误

标签 ios swift autolayout

我已经在 https://github.com/rDivaDuck/TestAppError 上创建了该问题的最小工作示例

我的 HomeController 是一个 UICollectioviewController,我有一个设置启动器类,我试图在导航栏按下按钮时从底部向上设置动画,我的代码通过停用向下约束并激活向上约束来实现此目的然后调用 window.layoutifneeded() 来动画更改。但是,我仍然抛出一个典型的约束冲突错误,即向下约束仍然处于事件状态。

我可以使用 .frame 和动画 CGRect 更改来实现良好的效果,但我想使用自动布局获得相同的效果。

class SettingsLauncher {

    var homeController: HomeController?
    let shadowView = UIView()

    let MenuView: UIView = {
        let View = UIView(frame: .zero)
        View.backgroundColor = UIColor.white
        View.translatesAutoresizingMaskIntoConstraints = false
        return View
    }()

    var downConstraint: NSLayoutConstraint?
    var upConstraint: NSLayoutConstraint?

    func showSettings() {
        if let window = UIApplication.shared.keyWindow{
            shadowView.backgroundColor = UIColor(white: 0,
                                                alpha: 0.4)
            shadowView.addGestureRecognizer(UITapGestureRecognizer(target: self,
                                                                  action: #selector(settingsDown)))
            window.addSubview(shadowView)
            shadowView.frame = window.frame
            shadowView.alpha = 0

            window.addSubview(MenuView)

            downConstraint = MenuView.topAnchor.constraint(equalTo: window.bottomAnchor)
            downConstraint?.isActive = true
            upConstraint = MenuView.topAnchor.constraint(equalTo: window.bottomAnchor, constant: -300)
            MenuView.heightAnchor.constraint(equalToConstant: 300).isActive = true
            MenuView.leadingAnchor.constraint(equalTo: window.leadingAnchor).isActive = true
            MenuView.trailingAnchor.constraint(equalTo: window.trailingAnchor).isActive = true
            MenuView.layoutIfNeeded()
            window.layoutIfNeeded()
            settingsUp()
        }
    }

    func settingsUp() {
        downConstraint?.isActive = false
        upConstraint?.isActive = true
        UIView.animate(withDuration: 0.5,
                       delay: 0,
                       usingSpringWithDamping: 1,
                       initialSpringVelocity: 1,
                       options: .curveEaseOut,
                       animations: {
                        self.shadowView.alpha = 1
                        if let window = UIApplication.shared.keyWindow {
                            window.layoutIfNeeded()
                        }
        }, completion: nil)
    }

    @objc func settingsDown() {
        downConstraint?.isActive = true
        upConstraint?.isActive = false
        UIView.animate(withDuration: 0.5,
                       animations: {
                        self.shadowView.alpha = 0
                        if let window = UIApplication.shared.keyWindow {
                            window.layoutIfNeeded()
                        }
        }, completion: nil )
    }

}
(
    "<NSLayoutConstraint:0x600001b5e440 V:[UIWindow:0x7fde18514030]-(-300)-[UIView:0x7fde18525ea0]   (active)>",
    "<NSLayoutConstraint:0x600001b5f250 V:[UIWindow:0x7fde18514030]-(0)-[UIView:0x7fde18525ea0]   (active)>"
)

我该如何解决这个问题?

最佳答案

您好,欢迎来到 StackOverflow,您的项目中存在多个错误。主要问题是,每次点击按钮并调用 showSettings() 时,都会向 MenuView 添加与之前的约束冲突的新约束,因此要解决此问题,您应该执行以下约束部分代码仅一次:

var viewInitialized = false

func showSettings() {
    if let window = UIApplication.shared.keyWindow {
        if !viewInitialized {
            viewInitialized = true

            shadowView.backgroundColor = UIColor(white: 0, alpha: 0.4)
            shadowView.addGestureRecognizer(UITapGestureRecognizer(target: self,
                                                                   action: #selector(settingsDown)))
            window.addSubview(shadowView)
            shadowView.frame = window.frame
            shadowView.alpha = 0

            window.addSubview(MenuView)

            downConstraint = MenuView.topAnchor.constraint(equalTo: window.bottomAnchor)
            downConstraint?.isActive = true
            upConstraint = MenuView.topAnchor.constraint(equalTo: window.bottomAnchor, constant: -300)
            MenuView.heightAnchor.constraint(equalToConstant: 300).isActive = true
            MenuView.leadingAnchor.constraint(equalTo: window.leadingAnchor).isActive = true
            MenuView.trailingAnchor.constraint(equalTo: window.trailingAnchor).isActive = true
            MenuView.layoutIfNeeded()
            window.layoutIfNeeded()
        }
        settingsUp()
    }
}

此外,在 settingsDown() 中切换激活语句,以便您在激活另一个约束之前停用约束,有时这会导致警告。

@objc func settingsDown() {
    upConstraint?.isActive = false
    downConstraint?.isActive = true
    ...

我发现的项目的其他问题:

  • AppDelegate.swift 中为什么要创建一个新窗口?已经有一个,您可以使用它。删除这行无用的代码即可 window = UIWindow(frame: UIScreen.main.bounds)
  • 为什么要在 AppDelegate.swift 中创建导航 Controller ?将其作为第一个 View Controller 添加到 Storyboard 中。对于 HomeController 来说也是如此,将 CollectionViewController 添加到 Storyboard并将其类更改为 HomeController
  • 将 View 直接添加到主窗口并不是一个好的做法。您应该将设置 View 呈现为模态视图 Controller ,并将呈现样式设置为当前上下文
  • SettingsLauncher.swiftshadowView 缺少约束

关于ios - 即使调用 needforlayout() ,单独类中的动画自动布局约束也会引发错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52420536/

相关文章:

ios - Xcode 的 "Target Membership"是否与 Bundle 相关?

javascript - 如何调用可以读取条形码并将数据返回到html的 native 应用程序

ios - EXC_BAD_ACCESS 在 enumerateObjectsUsingBlock 中设置传递回写错误

ios - AWSCognito Auth `getSession` 不会在 Swift 中触发回调

swift - 如何在 swift 中在两个以上 View 之间切换

ios - 如何使用 UIBezierPath 创建 UIImage

ios - 多行 UILabel 导致额外的填充

ios - 如何使用自动布局设置两个 View 之间的空间(纵横比)

ios - 为什么我以编程方式创建的 View 会忽略其约束?

ios - 通过编程设置带有Autolayout的按钮