UINavigationBar滑开而不是停留在

时间:2017-11-10 12:48:25

标签: ios swift uinavigationcontroller uinavigationbar

我创建了演示项目以显示问题。

我们在UINavigationController中有两个视图控制器。

MainViewController这是根。

class MainViewController: UIViewController {

    lazy var button: UIButton = {
        let button = UIButton()
        button.setTitle("Detail", for: .normal)
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.title = "Main"
        view.backgroundColor = .blue
        view.addSubview(button)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        button.widthAnchor.constraint(equalToConstant: 150).isActive = true
        button.heightAnchor.constraint(equalToConstant: 42).isActive = true
        button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
    }

    @objc func buttonTapped(_ sender: UIButton) {
        navigationController?.pushViewController(DetailViewController(), animated: true)
    }
}

推出DetailViewController

class DetailViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationController?.setNavigationBarHidden(true, animated: animated)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        navigationController?.setNavigationBarHidden(false, animated: animated)
    }
}

如您所见,我想隐藏UINavigationBar中的DetailViewController

Demo

问题

问题在于,UINavigationBar会滑离而不是与整个MainViewController一起停留在他的位置。如何更改该行为并保持弹出手势?

5 个答案:

答案 0 :(得分:3)

MainViewController中添加该方法

override func viewDidAppear(_ animated: Bool) {        
        UIView.animate(withDuration: 0) {
            self.navigationController?.setNavigationBarHidden(false, animated: false)
        }
    }

并在DetailViewController中使用以下方法替换您的方法

 override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    navigationController?.setNavigationBarHidden(true, animated: animated)
}

答案 1 :(得分:0)

以下代码是黑客攻击。

override func viewDidAppear(_ animated: Bool) {        
    UIView.animate(withDuration: 0) {
        self.navigationController?.setNavigationBarHidden(false, animated: false)
    }
}

不要像his post中的@sagarbhut所建议的那样编写这个奇怪的代码(在这个帖子中)。

你有两个选择。

  1. 哈克

  2. 不要破解。

  3. 使用像这样的便利功能

    https://developer.apple.com/documentation/uikit/uiview/1622562-transition

    如果您使用的是故事板,请创建自定义segue。

    https://www.appcoda.com/custom-segue-animations/

    实施UIViewControllerAnimatedTransitioning协议

    https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning

    你可以得到一些很好的结果,但我担心你需要努力工作。网上有很多教程讨论如何实现上述目的。

    enter image description here

答案 2 :(得分:0)

Twitter的导航过渡,推送的ViewController视图似乎占据了整个屏幕"隐藏了navigationBar" ,但仍然拥有流行手势动画即使在过渡动画期间,推送ViewController中的navigationBar显然也无法通过设置栏的隐藏属性来实现。

实现自定义导航系统是一种方法,但我建议通过在navigationBar的图层及其zPosition属性上进行简单的解决方案。您需要两个步骤,

  • 将navigationBar的图层zPosition设置为一个值,该值将其放置在包含导航堆栈中当前可见视图控制器视图的兄弟节点下:navigationController?.navigationBar.layer.zPosition = -1

    推动VC的viewDidLoad可能是一个很好的选择。

  • 现在导航栏位于VC视图的后面,您需要调整视图的框架,以确保它不与导航栏重叠(&#39) ; d导致navigationBar被覆盖)。您可以使用 viewWillLayoutSubviews 更改视图的origin.y,以在navigationBar的楼层(statusBarHeight + navigationBarHeight)下开始。

那将完成这项工作。您不需要修改推送的VC,除非您想要添加例如一个自定义后退按钮,如Twitter的个人资料屏幕案例。细节控制器的视图将位于导航栏的顶部,同时让您保持弹出手势过渡。以下是使用此更改修改的示例代码:

class MainViewController: UIViewController {

    lazy var button: UIButton = {
        let button = UIButton()
        button.setTitle("Detail", for: .normal)
        button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)

        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.title = "Main"
        view.backgroundColor = .blue

        // Default value of layer's zPosition is 0 so setting it to -1 will place it behind its siblings.
        navigationController?.navigationBar.layer.zPosition = -1

        // The `view` will be under navigationBar so lets set a background color to the bar
        // as the view's backgroundColor to simulate the default behaviour.
        navigationController?.navigationBar.backgroundColor = view.backgroundColor

        // Hide the back button transition image.
        navigationController?.navigationBar.backIndicatorImage = UIImage()
        navigationController?.navigationBar.backIndicatorTransitionMaskImage = UIImage()

        view.addSubview(button)
        addConstraints()
    }

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        // Place `view` under navigationBar.
        let statusBarPlusNavigationBarHeight: CGFloat = (navigationController?.navigationBar.bounds.height ?? 0) 
          + UIApplication.shared.statusBarFrame.height
        let viewHeight = UIScreen.main.bounds.height - statusBarPlusNavigationBarHeight
        view.frame = CGRect(origin: .zero, size: CGSize(width: view.bounds.width, height: viewHeight))
        view.frame.origin.y = statusBarPlusNavigationBarHeight
    }

    @objc func buttonTapped(_ sender: UIButton) {
        navigationController?.pushViewController(DetailViewController(), animated: true)
    }

    private func addConstraints() {
        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        button.widthAnchor.constraint(equalToConstant: 150).isActive = true
        button.heightAnchor.constraint(equalToConstant: 42).isActive = true
    }
}

class DetailViewController: UIViewController {

    // Some giant button to replace the navigationBar's back button item :)
    lazy var button: UIButton = {
        let b: UIButton = UIButton(frame: CGRect(origin: .zero, size: CGSize(width: 80, height: 40)))
        b.frame.origin.y = UIApplication.shared.statusBarFrame.height
        b.backgroundColor = .darkGray
        b.setTitle("back", for: .normal)
        b.addTarget(self, action: #selector(DetailViewController.backButtonTapped), for: .touchUpInside)
        return b
    }()

    @objc func backButtonTapped() {
        navigationController?.popViewController(animated: true)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        view.addSubview(button)
    }
}

答案 3 :(得分:-1)

这个可能正是你要找的......

在开始推/弹之前启动NavBar隐藏/显示动画

class MainViewController: UIViewController {

    lazy var button: UIButton = {
        let button = UIButton()
        button.setTitle("Detail", for: .normal)
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.title = "Main"
        view.backgroundColor = .blue
        view.addSubview(button)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        button.widthAnchor.constraint(equalToConstant: 150).isActive = true
        button.heightAnchor.constraint(equalToConstant: 42).isActive = true
        button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
    }

    @objc func buttonTapped(_ sender: UIButton) {
        navigationController?.setNavigationBarHidden(true, animated: true)
        navigationController?.pushViewController(DetailViewController(), animated: true)
    }
}

class DetailViewController: UIViewController {

    lazy var button: UIButton = {
        let button = UIButton()
        button.setTitle("Go Back", for: .normal)
        button.backgroundColor = .red
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        view.addSubview(button)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        button.widthAnchor.constraint(equalToConstant: 150).isActive = true
        button.heightAnchor.constraint(equalToConstant: 42).isActive = true
        button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
    }

    @objc func buttonTapped(_ sender: UIButton) {
        navigationController?.setNavigationBarHidden(false, animated: true)
        navigationController?.popViewController(animated: true)
    }

}

答案 4 :(得分:-2)

使用此帖子stackoverflow.com/a/5660278/7270113中的自定义推送转换。为了消除后退手势(我理解的是你想要做的事情),只需要杀死导航堆栈。您必须提供一种退出DetailViewController的替代方法,因为即使您取消隐藏导航控制器,由于导航堆栈为空,所以后退将会消失。

@objc func buttonTapped(_ sender: UIButton) {

    let transition = CATransition()
    transition.duration = 0.5
    transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    transition.type = kCATransitionFade
    navigationController?.view.layer.add(transition, forKey: nil)

    let storyboard = UIStoryboard(name: "NameOfYourStoryBoard", bundle: .main)
    let viewController = storyboard.instantiateViewController(withIdentifier: "IdentifierOfDetailViewController") as! DetailViewController
    navigationController?.setViewControllers([viewController], animated: true) // This method will perform a push
}

您的导航控制器将从现在开始使用此过渡动画,如果您要删除它,可以使用

navigationController?.view.layer.removeAllAnimations()