Swift中导航跳转

376 阅读3分钟

我正在参加「掘金·启航计划」

前言

我们在swift开发中可以利用一些前端的路由思想来导航我们的控制器。我们最简单明了的使用的就是pushpresent,以及pod的形式进行导航跳转。

当然我们也可以通过stroyboard的方式进行连线,通常是展示TabbarControoler,对应不同的主模块。今天介绍下通过枚举的形式来选择控制器的跳转。

1. Navigator

我们定义一个类,定义枚举Scene,对应的几个大的模块

class Navigator {

    static let `default` = Navigator()

    // MARK: - 控制器列表

    enum Scene: NavigatorProtocol {

        case tabs(viewModel: HomeTabBarViewModel)

        case home(viewModel: HomeViewModel)

        case mine(viewModel: MineViewModel)

        case business(viewModel: BusinessViewModel)

        case function(viewModel: FunctionViewModel)

        case login(viewModel: LoginViewModel)

    }
  }

遵循协议NavigatorProtocol,我们可以自己定义些通用的接口

protocol NavigatorProtocol { }

我们创建对应的get方法获取控制器

/// 创建对应控制器

    /// - Parameter segue: 类型

    /// - Returns: 控制器

    func get(segue: NavigatorProtocol) -> UIViewController? {

        switch segue as? Navigator.Scene {

        case .tabs(let viewModel):

            return HomeTabBarController.creatTabBar(viewModel: viewModel, navigator: self)

        case .home(let viewModel):

            return HomeViewController(viewModel: viewModel, navigator: self)

        case .mine(let viewModel):

            return MineViewController(viewModel: viewModel, navigator: NavigatorMine())

        case .login(let viewModel):

            return LoginViewController(viewModel: viewModel, navigator: self)

        case .business(let viewModel):

            return BusinessViewController(viewModel: viewModel, navigator: NavigatorBusiness())

        case .function(let viewModel):

            return FunctionViewController(viewModel: viewModel, navigator: NavigatorFunction())
 }

    }           

我这里的控制器持有了具体模块的navigator类,这里后面在说明。我们提供一个跳转的方法

func show(segue: NavigatorProtocol,

              sender: UIViewController?,

              transition: Transition = .navigation(type: .cover(direction: .left))) {

        if let target = get(segue: segue) {

            show(target: target, sender: sender, transition: transition)

        }

    }

这里用了动画类Hero库

2. pop 和 dismiss

我们拓展下pop和dismiss的方法

extension Navigator {

    func pop(sender: UIViewController?, toRoot: Bool = false) {

        if toRoot {

            sender?.navigationController?.popToRootViewController(animated: true)

        } else {

            sender?.navigationController?.popViewController(animated: true)

        }

    }

    func dismiss(sender: UIViewController?) {

        sender?.navigationController?.dismiss(animated: true, completion: nil)

    }

}

常规操作

3. 显示的动画

我们一般会有模态弹出,或者导航push,以及一些翻转动画,直接上代码,动画效果也是使用的Hero库

// MARK: - 显示控制器动画方式


extension Navigator {

    enum Transition {

        case root(into: UIWindow)

        case navigation(type: HeroDefaultAnimationType)

        case customModal(type: HeroDefaultAnimationType)

        case modal

        case detail

        case alert

        case custom

    }

    /// 判断切换动画

    /// - Parameters:

    ///   - target: 目标vc

    ///   - sender: father vc

    ///   - transition: 切换方式

    private func show(target: UIViewController, sender: UIViewController?, transition: Transition) {

        switch transition {

        case .root(into: let window):

            UIView.transition(with: window, duration: 0.1, options: .transitionCrossDissolve, animations: {

                window.rootViewController = target

            }, completion: nil)

            return

        case .custom: return

        default: break

        }

        guard sender != nil else {

            fatalError("You need to pass in a sender for .navigation or .modal transitions")

        }

        if let nav = sender as? UINavigationController {

            nav.pushViewController(target, animated: false)

            return

        }

        switch transition {

        case .navigation(let type):

            if let nav = sender?.navigationController {

                // push controller to navigation stack

                nav.hero.navigationAnimationType = .autoReverse(presenting: type)

                nav.pushViewController(target, animated: true)

            }

        case .customModal(let type):

            // present modally with custom animation

            DispatchQueue.main.async {

                let nav = NavigationController(rootViewController: target)

                nav.hero.modalAnimationType = .autoReverse(presenting: type)

                sender?.present(nav, animated: true, completion: nil)

            }

        case .modal:

            // present modally

            DispatchQueue.main.async {

                let nav = NavigationController(rootViewController: target)

                sender?.present(nav, animated: true, completion: nil)

            }

        case .detail:

            DispatchQueue.main.async {

                let nav = NavigationController(rootViewController: target)

                sender?.showDetailViewController(nav, sender: nil)

            }

        case .alert:

            DispatchQueue.main.async {

                sender?.present(target, animated: true, completion: nil)

            }

        default: break

        }

    }

}

4. 子模块

子模块也是类似的情况定义控制器,并实现get方法

class NavigatorBusiness: Navigator {

    enum Scene: NavigatorProtocol {

        case rcim

        case thirdLogin

        case thirdShare

    }

    override func get(segue: NavigatorProtocol) -> UIViewController? {

        switch segue as? NavigatorBusiness.Scene {

        case .rcim:

            let imVc = RcIMListViewController()

            return imVc

        case .thirdLogin:

            let loginVC = ThirdPartLoginViewController(viewModel: nil, navigator: self)

            return loginVC

        case .thirdShare:

            let shareVC = ThirdPartShareViewController(viewModel: nil, navigator: self)

            return shareVC

        case .none:

            return nil

        }

    }


    /// 确定控制器

    override func show(segue: NavigatorProtocol,

                       sender: UIViewController?,

                       transition: Transition = .navigation(type: .cover(direction: .left))) {

        super.show(segue: segue, sender: sender, transition: transition)

    }


}

image.png

5. 使用

我们的根视图通过get的方式获取当前导航类下面枚举的控制器,根据需要是否传递naviagtor

image.png

跳转

self.navigator?.show(segue: NavigatorFunction.Scene.calendar, sender: self)

使用模态

self.navigator?.show(segue: NavigatorFunction.Scene.calendar, sender: self,transition: .modal)

image.png

6. 小结

通过枚举的方式定义不同的控制器,实现对应初始化的方法,方便了我们统一管理和修改。使用的时候通过枚举也方便我们使用。类似flutter中路由管理,通过路径查找对应的控制器。