导航栏滑动而不是停留在原位。

4
我创建了一个演示项目来展示这个问题。
我们在 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)
    }
}

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

Demo

问题

问题在于,UINavigationBar会滑动而不是与整个MainViewController一起保持在原位。如何改变这种行为并保持pop手势?


3
这是一个导航控制器堆栈,由 navigationController 管理。 - Mannopson
你想要在视图转换期间动画隐藏NavBar吗?你可以尝试在viewDidAppear中隐藏它...这样应该会给你动画效果,但是它将会在新视图滑入到位之后才出现。 - DonMag
确切地说,它会一直保留,直到DetailViewController的动画结束,这太糟糕了。NavigationBar应该像下面的内容一样保持不变,我不知道为什么苹果开发人员将其更改为滑动到内容之外... - Nominalista
@ThirdMartian 这需要大量的工作和时间。你可以创建自己的演示风格。看看这个 - Mannopson
我认为这与问题没有任何关系,但在我看来,您可以随时点击主页指示器并返回到根目录。 - Nominalista
显示剩余13条评论
5个回答

4
在你的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

以下代码是黑客行为。

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

不要按照@sagarbhut在此帖子中建议的方式编写奇怪的代码。

你有两个选择。

  1. 黑客

  2. 不要黑客。

使用像这个一样的便利函数

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


0

Twitter的导航转换,在推出的ViewController视图中,似乎占据了整个屏幕 "隐藏navigationBar",但在转换动画期间仍具有弹出手势动画和navigationBar在推入的ViewController中可见,这显然无法通过设置bar的hidden属性来实现。

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

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

    推送VC的viewDidLoad可能是执行此操作的好地方。

  • 现在,navigationBar放置在VC的视图后面,您需要调整视图的框架,以确保它不重叠navigationBar(这会导致navigationBar被覆盖)。您可以使用 viewWillLayoutSubviews 来更改视图的 origin.y 以从navigationBar的底部开始 (statusBarHeight + navigationBarHeight)

这样就可以了。除非你想像Twitter的个人资料屏幕那样添加自定义返回按钮,否则不需要修改推送的VC。详细控制器的视图将位于导航栏顶部,同时让您保持弹出手势转换。下面是已经修改了这些变化的示例代码:

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)
    }
}

-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)
    }

}

1
结果是UINavigationBar向上滑动,它在DetailViewController上可见一毫秒。看起来不好。查看Twitter应用程序,当您点击用户时。 - Nominalista
好的 - 我会尝试查看Twitter应用程序(目前不使用它),以了解您想要实现的效果。 - DonMag

-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()

不,我认为这是同一件事情! - Mannopson
不是这样的,请按照我写的方式尝试,你会得到想要的结果。 - erik_m_martens
不同类型的实例化视图控制器如何帮助? - Nominalista
我之前提到过,我想保留pop手势。你的代码没有禁用它,但是当你使用手势进行pop时,导航栏从左侧滑动,这不是我想要实现的效果。 - Nominalista

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接