即使在调用needforlayout()的情况下,也可以在单独的类抛出错误时对自动布局约束进行动画处理

时间:2018-09-20 08:14:18

标签: 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)>"
)

我该如何解决?

1 个答案:

答案 0 :(得分:0)

您好,欢迎来到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中创建导航控制器?将其添加到情节提要中作为第一个视图控制器。 HomeController的相同操作是将一个CollectionViewController添加到情节提要,并将其类更改为HomeController
  • 将视图直接添加到主窗口不是一个好习惯。您应该将设置视图呈现为模态视图控制器,并且其呈现样式应设置在当前上下文之上
  • SettingsLauncher.swift shadowView中缺少约束