Swift 中的多视图控制器导航

Posted

技术标签:

【中文标题】Swift 中的多视图控制器导航【英文标题】:Multi View Controller Navigation in Swift 【发布时间】:2019-04-03 03:37:41 【问题描述】:

Swift 中的侧边菜单导航

Swift 4.2,Xcode 10.0

我的最终目标是能够轻松(通常)横向/导航通过我的视图控制器,如下所示。

我希望能够使用通用侧边菜单从视图控制器导航到视图控制器。至此,我想出了一个非常hacky 的方法来实现这一点,当我在侧边菜单中选择一个视图控制器时,它会关闭侧边菜单,然后它会显示下一个视图来自那个控制器,并假设它不是目标视图控制器,当我从那个视图控制器到目标视图控制器时,一个窗口会暂时覆盖它的内容。

从侧边菜单显示的视图控制器的唯一路径。

注意: 上图中的每种颜色都是应用程序在侧边菜单中点击某些内容后可以采用的另一条路径。

这个实现有很多错误,我什至不知道从哪里开始。在极少数情况下,您可以在 segue 之间看到中间视图控制器的内容。此外,由于在某些情况下实际到达目标视图所需的转场数量,动画可能会有点不稳定和失真。更不用说在我的侧菜单中添加另一行所需的巨大难度和复杂性。我很清楚这很糟糕,我拼命想找到一个解决方案,而不是我复杂的导航问题。最近我尝试过使用容器视图并将侧边菜单放在堆栈的底部,而不是像现在这样放在顶部,但它并没有起到任何作用。


在过去的几周里,我一直在疯狂地试图找出如何做到这一点。我发现了无数侧边菜单的实现,但到目前为止我发现的所有内容都只会在一个视图控制器上显示侧边菜单,而不是显示在所有侧边菜单的目标上并在它们都是 时处理它们可以这么说。本质上,侧边菜单需要能够出现在所有 3 个视图控制器上,并且无需hackily 切换视图控制器。同样,如果这个侧边菜单可以轻松扩展,那将是非常理想的,这样我就可以轻松地将多个部分添加到侧边菜单中。


【问题讨论】:

我相信这(youtube.com/watch?v=2kwCfFG5fDA)会帮助你。它最好的部分是您可以从任何屏幕调用它,导航堆栈上的位置无关紧要。 @Noah 对我的回答有任何反馈 您是否尝试过使用不可见的标签栏来代替?您可以设置选定的索引来显示您需要的视图控制器,而不是显示和关闭视图控制器。 【参考方案1】:

我为这个问题创建了一个示例项目。您可以在上图中看到输出。基本上我所做的是在侧边栏周围创建了一个包装类,然后我可以随时使用它:)

侧边栏

import UIKit
protocol SidebarDelegate 
    func sidbarDidOpen()
    func sidebarDidClose(with item: Int?)

class SidebarLauncher: NSObject

    var view: UIView?
    var delegate: SidebarDelegate?
    var vc: NavigationViewController?
    init(delegate: SidebarDelegate) 
        super.init()
        self.delegate = delegate
    

    func show()
        let bounds = UIScreen.main.bounds
        let v = UIView(frame: CGRect(x: -bounds.width, y: 0, width: bounds.width, height: bounds.height))
        v.backgroundColor = .clear
        let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavigationController") as! NavigationViewController
        v.addSubview(vc.view)
        vc.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            vc.view.topAnchor.constraint(equalTo: v.topAnchor),
            vc.view.leadingAnchor.constraint(equalTo: v.leadingAnchor),
            vc.view.bottomAnchor.constraint(equalTo: v.bottomAnchor),
            vc.view.trailingAnchor.constraint(equalTo: v.trailingAnchor, constant: -60)
            ])
        vc.delegate = self
        v.isUserInteractionEnabled = true
        v.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:))))
        self.view = v
        self.vc = vc
        UIApplication.shared.keyWindow?.addSubview(v)

        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut], animations: 
            self.view?.frame = CGRect(x: 0, y: 0, width: self.view!.frame.width, height: self.view!.frame.height)
            self.view?.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        , completion: completed in
            self.delegate?.sidbarDidOpen()
        )

    

    @objc func handleTapGesture(_ sender: UITapGestureRecognizer)
        closeSidebar(option: nil)
    
    func closeSidebar(option: Int?)
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut], animations: 
            if let view = self.view
                view.frame = CGRect(x: -view.frame.width, y: 0, width: view.frame.width, height: view.frame.height)
                self.view?.backgroundColor = .clear

            
        , completion: completed in
            self.view?.removeFromSuperview()
            self.view = nil
            self.vc = nil
            self.delegate?.sidebarDidClose(with: option)
        )
    


extension SidebarLauncher: NavigationDelegate
    func navigation(didSelect: Int?) 
        closeSidebar(option: didSelect)
    

导航控制器

import UIKit
protocol NavigationDelegate
    func navigation(didSelect: Int?)


class NavigationViewController: UIViewController

    @IBOutlet weak var buttonLaunchVC: UIButton!
    @IBOutlet weak var buttonSecondViewController: UIButton!
    @IBOutlet weak var buttonThirdViewController: UIButton!


    var delegate: NavigationDelegate?

    override func viewDidLoad() 
        super.viewDidLoad()
        [buttonLaunchVC,buttonSecondViewController,buttonThirdViewController].forEach(
            $0?.addTarget(self, action: #selector(didSelect(_:)), for: .touchUpInside)
        )
    

    @objc func didSelect(_ sender: UIButton)
        switch sender 
        case buttonLaunchVC:
            delegate?.navigation(didSelect: 0)
        case buttonSecondViewController:
            delegate?.navigation(didSelect: 1)
        case buttonThirdViewController:
            delegate?.navigation(didSelect: 2)
        default:
            break
        
    


    @IBAction func CloseMenu(_ sender: Any) 
        delegate?.navigation(didSelect: nil)
    



视图控制器

class ViewController: UIViewController 

    @IBAction func OpenMenu(_ sender: Any) 
        SidebarLauncher(delegate: self ).show()
    


extension ViewController: SidebarDelegate
    func sidbarDidOpen() 
        print("Sidebar opened")
    

    func sidebarDidClose(with item: Int?) 
        guard let item = item else return
        print("Did select \(item)")
        switch item 
        case 0:
           break
        case 1:
            let v = UIStoryboard.main.SecondVC()
            present(v!, animated: true)
        case 2:
            let v = UIStoryboard.main.ThirdVC()
            present(v!, animated: true)
        default:
            break
        
    

主要感兴趣的领域是 SidebarLauncher 类 它的作用:当你调用 show() 方法时。它创建一个 UIView,然后将其添加到 keywindow(即您当前的视图),然后添加 NavigationController。

为了设置与侧边栏的通信,我创建了两个协议

    SidebarDelegate:

侧边栏委托是主要协议,您可以通过它了解用户是否选择了任何 ViewController。

    导航委托: 该协议用于包装器和导航控制器之间的通信。当用户点击任何按钮时。它通知包装类。

包装类有一个方法 closeSidebar 然后关闭侧边栏并通知控制器类侧边栏关闭选项。

在 sidebarDidClose 中,您可以决定如何处理用户所做的选择。

我有点着急,这就是我使用 Int 的原因,而您应该考虑使用适合您需要的结构或类来确定要打开哪个 ViewController。

https://github.com/sahilmanchanda2/SidebarTest

【讨论】:

有什么方法可以将示例项目发布到 GitHub 上? @NoahWilder 我现在在办公室。到家后上传。 谢谢,有机会上传我会第一时间查看的 @NoahWilder,我已经添加了项目。如果您需要什么,请告诉我 我只是试用了它并定制了一些东西,它的效果非常好!非常感谢!【参考方案2】:

您需要使用协调器

raywenderlich Coordinator

townsend Coordinator

【讨论】:

以上是关于Swift 中的多视图控制器导航的主要内容,如果未能解决你的问题,请参考以下文章

Swift - 在 UIPageViewController 中的 3 个不同视图控制器之间导航按钮

在Swift中的导航栏中设置图像

使用swift在选项卡式视图中的导航栏上添加按钮

Swift:嵌入在导航控制器中的视图之间的自定义segue

Swift 4仅为一个视图控制器设置导航属性

Swift:删除嵌入在导航控制器中的表格视图控制器页脚下方的空间