Swift中的多视图控制器导航

Noa*_*der 5 navigation animation menu segue swift

Swift中的侧边菜单导航

Swift 4.2,Xcode 10.0

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

导航

我希望能够通过常用的侧面菜单从视图控制器导航到视图控制器.到目前为止,我已经想出了一种令人难以置信的hacky方法来实现这一点,当我在侧面菜单中选择一个视图控制器时,它会解散侧面菜单,然后它会先从那一个呈现下一个视图控制器,并假设这不是目标视图控制器,当我从该视图控制器转移到目标视图控制器时,窗口暂时覆盖其内容.

通过故事板的路径

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

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

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


过去几周,我一直在疯狂地试图找出如何做到这一点.我已经找到了无数的侧面菜单实现,但是我在这个日期找到的所有东西只会在一个视图控制器上显示侧面菜单,而不是显示在所有侧面菜单的目标上,如果它们全部在同一个目标上则对它们进行处理水平如此说.从本质上讲,侧面菜单需要能够拿出的所有3个视图控制器,并带走需要hackily Segue公司在视图控制器.同样,如果这个侧面菜单可以轻松扩展,那将是非常理想的,所以我可以轻松地在侧面菜单中添加多个部分.


Sah*_*nda 5

在此处输入图片说明

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

侧边栏

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

导航控制器

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


}
Run Code Online (Sandbox Code Playgroud)

视图控制器

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
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

  1. 侧边栏委托:

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

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

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

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

我有点匆忙,这就是我使用Int 的原因,而您应该考虑使用适合您需要确定打开哪个 ViewController 的 struct 或 class。

https://github.com/sahilmanchanda2/SidebarTest