美文网首页
Swift-界面跳转

Swift-界面跳转

作者: Happiness__ | 来源:发表于2020-11-26 15:12 被阅读0次

随着业务增加,项目中的模块越来越多,并且这些模块进行相互的调用,使得它们交缠在一起,增加了维护成本,并且会降低开发效率。此时就需要对整个项目进行模块划分,将这些模块划分给多个开发人员(组)进行维护,然后在主工程中对这些模块进行调用。

每个模块独立存在,提供接口供其他模块调用。从而如何有效且解耦的进行模块间的调用成了重中之重。

那么如何传递参数并且初始化对应模块的主窗口成为了主要问题。下面会介绍两种方案来解决这个问题。

方案1

得益于Swift中枚举可以传参的特性,我们可以通过枚举来进行参数传递,然后添加方法来映射控制器。

在枚举中通过参数来确定初始化控制器的数据,外部进行调用时可以很明确的知道需要传入哪些参数,同时还能传递非常规参数(DataUIImage等)

enum Scene {

    case targetA
    case targetB(data: Data)

    func transToViewController() -> UIViewController  {
        switch self {
        case .targetA:
            return ViewControllerA()
        case let .targetB(data):
            return ViewControllerB(data: data)
        }
    }

}

解决了UIViewController映射的问题后,我们接下来解决如何统一跳转方法。

项目中,基本上都会使用UINavigationController来进行界面的跳转,主要涉及pushpop操作。此时简便的做法是扩展UIViewController为其添加统一界面跳转方法。

extension UIViewController {
    
    func push(to scene: Scene, animated: Bool = true) {
        let scene = scene.transToViewController()
        DispatchQueue.main.async { [weak self] in
            self?.navigationController?.pushViewController(scene, animated: animated)
        }
    }
    
    func pop(toRoot: Bool = false, animated: Bool = true) {
        DispatchQueue.main.async { [weak self] in
            if toRoot {
                self?.navigationController?.popToRootViewController(animated: animated)
            } else {
                self?.navigationController?.popViewController(animated: animated)
            }
        }
    }

最后我们跳转界面时进行如下调用即可

push(to: .targetA)

push(to: .targetB(data: Data()))

面向协议进行改造

protocol Scene: UIViewController {
    
}

protocol SceneAdpater {
    
    func transToScene() -> Scene
}

protocol Navigable: UIViewController {
    
    func push(to scene: SceneAdpater, animated: Bool)

    func pop(toRoot: Bool, animated: Bool)
}
extension Navigable {
    
    func push(to scene: SceneAdpater, animated: Bool = true) {
        let scene = scene.transToScene()
        DispatchQueue.main.async { [weak self] in
            self?.navigationController?.pushViewController(scene, animated: animated)
        }
    }
    
    func pop(toRoot: Bool = false, animated: Bool = true) {
        DispatchQueue.main.async { [weak self] in
            if toRoot {
                self?.navigationController?.popToRootViewController(animated: animated)
            } else {
                self?.navigationController?.popViewController(animated: animated)
            }
        }
    }
    
}

经过以上面向协议改造,我们在使用时的代码如下:

enum TestScene: SceneAdpater {

    case targetA
    case targetB(data: Data)

    func transToScene() -> Scene {
        switch self {
        case .targetA:
            return ViewControllerA()
        case let .targetB(data):
            return ViewControllerB(data: data)
        }
    }

}

class ViewControllerA: UIViewController, Navigable, Scene {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        push(to: TestScene.targetB(data: Data()))
    }
    
}

class ViewControllerB: UIViewController, Scene {

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    init(data: Data) {
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

方案2

初始化UIViewController存在两种情况:需要传参、不需要传参。不需要传参的比较简单,直接调用UIKit提供的初始化方法即可。而需要传参的UIViewController,就涉及到如何确定传参的类型、传入哪些参数。此时需要利用协议的关联类型来确定传入的参数。

基于上述结论,制定的协议如下:

protocol Scene: UIViewController {
    
}

protocol NeedInputScene: Scene {
    
    associatedtype Input
    
    init(input: Input)
}

protocol NoneInputScene: Scene {
    
}

由此在跳转方法中需要用到泛型

protocol Navigable: UIViewController {
    
    func push<S: NeedInputScene>(to scene: S.Type, input: S.Input, animated: Bool)
    
    func push<S: NoneInputScene>(to scene: S.Type, animated: Bool)
}

extension Navigable {
    
    func push<S: NeedInputScene>(to scene: S.Type, input: S.Input, animated: Bool = true) {
        let scene = scene.init(input: input)
        DispatchQueue.main.async { [weak self] in
            self?.navigationController?.pushViewController(scene, animated: animated)
        }
    }
    
    func push<S: NoneInputScene>(to scene: S.Type, animated: Bool = true) {
        let scene = scene.init()
        DispatchQueue.main.async { [weak self] in
            self?.navigationController?.pushViewController(scene, animated: animated)
        }
    }
}

使用示例:

class ViewControllerA: UIViewController, Navigable {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        push(to: ViewControllerB.self, input: Data())
        
        push(to: ViewControllerC.self)
    }
    
}

class ViewControllerB: UIViewController, NeedInputScene {
    
    typealias Input = Data

    required init(input: Data) {
        super.init(nibName: nil, bundle: nil)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class ViewControllerC: UIViewController, NoneInputScene {
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

方案2相较于方案1最显著的区别就是不再需要维护映射UIViewController的枚举类型。

使用枚举来作为入参,外部在调用时可以很清晰的确定需要传入参数的意义。而关联类型则不具备这种优势,不过这个问题通过使用枚举作为关联类型来解决,但是在UIViewController仅需要一个字符串类型时这种做法就显得有点重。

方案2在模块需要提供多个入口时,需要暴露出多个控制器的类型,增加了耦合。而方案1则仅需用暴露出枚举类型即可。

Demo

Demo对方案1进行了演示,也是我目前在项目中使用的方案。
更多路由相关信息请查看一下参考链接

参考连接:

https://github.com/meili/MGJRouter

https://casatwy.com/iOS-Modulization.html

http://blog.cnbang.net/tech/3080/

相关文章

  • Swift-界面跳转

    随着业务增加,项目中的模块越来越多,并且这些模块进行相互的调用,使得它们交缠在一起,增加了维护成本,并且会降低开发...

  • iOS集成ReactNative跳转、传值

    iOS跳转RN界面iOS跳转RN界面传值iOS跳转不同的RN界面(一)iOS跳转不同的RN界面(二)RN界面跳转到...

  • 打开设置选项

    跳转到流量使用情况设置界面 跳转到vpn界面 电池管理界面 打开关于设备界面

  • Android界面跳转到几种方式

    简单界面跳转 带数据传递的界面跳转 接收数据 带有回调的界面跳转 回调传递数据到上个界面 接收回调的数据

  • 课程三

    1.导航器: 路由进行跳转 界面获取到跳转传递的参数: 界面返回上一页或者跳转到指定的界面

  • 003 功能实现-Android中跳转系统特定设置界面

    1.跳转到系统的辅助功能界面 2. 跳转到添加帐户界面 3.跳转到系统的包含飞行模式的界面 4.跳转到系统的更多连...

  • 微信小程序wx.switchTab跳转不刷新数据的问题

    wx.switchTab 跳转页面不刷新数据 业务代码场景从a界面带参数跳转到b界面,b界面再把结果返回到界面。 ...

  • iOS控制器间跳转

    当我们从A界面跳转到C而需要返回到B界面时,可在A界面跳转方法中进行如下操作,A界面要取当前界面,否则返回到A界面...

  • 界面跳转

    模态跳转(Modal)普通的视图控制器一般只有模态跳转的功能,这个方法是所有视图控制器对象都可以用的。 一般跳转前...

  • 主流界面搭建(类似百思不得姐主界面)

    一.界面搭建 1.项目需求 主界面能左右滚动,还能上下滚动,点击按钮跳转界面 2.分析界面 点击按钮跳转界面可以自...

网友评论

      本文标题:Swift-界面跳转

      本文链接:https://www.haomeiwen.com/subject/wpaaiktx.html