我正在参加「掘金·启航计划」
前言
我们在swift开发中可以利用一些前端的路由思想来导航我们的控制器。我们最简单明了的使用的就是push, present,以及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)
}
}
5. 使用
我们的根视图通过get的方式获取当前导航类下面枚举的控制器,根据需要是否传递naviagtor
跳转
self.navigator?.show(segue: NavigatorFunction.Scene.calendar, sender: self)
使用模态
self.navigator?.show(segue: NavigatorFunction.Scene.calendar, sender: self,transition: .modal)
6. 小结
通过枚举的方式定义不同的控制器,实现对应初始化的方法,方便了我们统一管理和修改。使用的时候通过枚举也方便我们使用。类似flutter中路由管理,通过路径查找对应的控制器。