Swift-界面跳轉(zhuǎn)

隨著業(yè)務(wù)增加,項目中的模塊越來越多癞己,并且這些模塊進行相互的調(diào)用膀斋,使得它們交纏在一起,增加了維護成本痹雅,并且會降低開發(fā)效率仰担。此時就需要對整個項目進行模塊劃分,將這些模塊劃分給多個開發(fā)人員(組)進行維護绩社,然后在主工程中對這些模塊進行調(diào)用摔蓝。

每個模塊獨立存在,提供接口供其他模塊調(diào)用愉耙。從而如何有效且解耦的進行模塊間的調(diào)用成了重中之重贮尉。

那么如何傳遞參數(shù)并且初始化對應(yīng)模塊的主窗口成為了主要問題。下面會介紹兩種方案來解決這個問題朴沿。

方案1

得益于Swift中枚舉可以傳參的特性猜谚,我們可以通過枚舉來進行參數(shù)傳遞,然后添加方法來映射控制器赌渣。

在枚舉中通過參數(shù)來確定初始化控制器的數(shù)據(jù)魏铅,外部進行調(diào)用時可以很明確的知道需要傳入哪些參數(shù),同時還能傳遞非常規(guī)參數(shù)(Data坚芜、UIImage等)

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映射的問題后览芳,我們接下來解決如何統(tǒng)一跳轉(zhuǎn)方法。

項目中货岭,基本上都會使用UINavigationController來進行界面的跳轉(zhuǎn)路操,主要涉及pushpop操作千贯。此時簡便的做法是擴展UIViewController為其添加統(tǒng)一界面跳轉(zhuǎn)方法。

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

最后我們跳轉(zhuǎn)界面時進行如下調(diào)用即可

push(to: .targetA)

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

面向協(xié)議進行改造

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

經(jīng)過以上面向協(xié)議改造搞坝,我們在使用時的代碼如下:

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存在兩種情況:需要傳參搔谴、不需要傳參。不需要傳參的比較簡單桩撮,直接調(diào)用UIKit提供的初始化方法即可敦第。而需要傳參的UIViewController,就涉及到如何確定傳參的類型店量、傳入哪些參數(shù)芜果。此時需要利用協(xié)議的關(guān)聯(lián)類型來確定傳入的參數(shù)。

基于上述結(jié)論融师,制定的協(xié)議如下:

protocol Scene: UIViewController {
    
}

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

protocol NoneInputScene: Scene {
    
}

由此在跳轉(zhuǎn)方法中需要用到泛型

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最顯著的區(qū)別就是不再需要維護映射UIViewController的枚舉類型右钾。

使用枚舉來作為入?yún)ⅲ獠吭谡{(diào)用時可以很清晰的確定需要傳入?yún)?shù)的意義。而關(guān)聯(lián)類型則不具備這種優(yōu)勢舀射,不過這個問題通過使用枚舉作為關(guān)聯(lián)類型來解決窘茁,但是在UIViewController僅需要一個字符串類型時這種做法就顯得有點重。

方案2在模塊需要提供多個入口時脆烟,需要暴露出多個控制器的類型山林,增加了耦合。而方案1則僅需用暴露出枚舉類型即可邢羔。

Demo

Demo對方案1進行了演示驼抹,也是我目前在項目中使用的方案。
更多路由相關(guān)信息請查看一下參考鏈接

參考連接:

https://github.com/meili/MGJRouter

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

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載拜鹤,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者框冀。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市署惯,隨后出現(xiàn)的幾起案子左驾,更是在濱河造成了極大的恐慌,老刑警劉巖极谊,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诡右,死亡現(xiàn)場離奇詭異,居然都是意外死亡轻猖,警方通過查閱死者的電腦和手機帆吻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咙边,“玉大人猜煮,你說我怎么就攤上這事“苄恚” “怎么了王带?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長市殷。 經(jīng)常有香客問我愕撰,道長,這世上最難降的妖魔是什么醋寝? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任搞挣,我火速辦了婚禮,結(jié)果婚禮上音羞,老公的妹妹穿的比我還像新娘囱桨。我一直安慰自己,他們只是感情好嗅绰,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布舍肠。 她就那樣靜靜地躺著搀继,像睡著了一般。 火紅的嫁衣襯著肌膚如雪貌夕。 梳的紋絲不亂的頭發(fā)上律歼,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音啡专,去河邊找鬼险毁。 笑死,一個胖子當(dāng)著我的面吹牛们童,可吹牛的內(nèi)容都是我干的畔况。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼慧库,長吁一口氣:“原來是場噩夢啊……” “哼跷跪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起齐板,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤吵瞻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后甘磨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體橡羞,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年济舆,在試婚紗的時候發(fā)現(xiàn)自己被綠了卿泽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡滋觉,死狀恐怖签夭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情椎侠,我是刑警寧澤第租,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站我纪,受9級特大地震影響煌妈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宣羊,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望汰蜘。 院中可真熱鬧仇冯,春花似錦、人聲如沸族操。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至泼舱,卻和暖如春等缀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背娇昙。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工尺迂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人冒掌。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓噪裕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親股毫。 傳聞我的和親對象是個殘疾皇子膳音,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容