Swift SB 容器 Container View使用

Swift_Xu發表於2019-03-01

最近比較清閒,就把以前學習的過程記錄下吧,多少年後如果能在網際網路上找到自己的痕跡,想想還是一件蠻值得高興的事情

容器在開發過程中用到的地方還是蠻多的,像網易新聞的框架如果使用程式碼去實現的話,呼叫系統的API

addChildViewController(<#T##childController: UIViewController##UIViewController#>)
複製程式碼

使它成為子控制器,加入一個類似的容器裡面就可以,具體的大家在做專案過程中都用到過,這裡就不多說了。今天就分享一下SB上使用容器類管理VC的控制元件ContainerView(純程式碼裡沒有這個控制元件)

新建個專案(這裡我使用的是以前自己隨便寫的一個小demo),在Manstoryboard 的 view 上拖拽兩個ContainerView ,給他們兩個分別設定約束,寬度加起來等於螢幕寬度就可以。拖拽完後你會發現view上多出來兩根線並且關聯到兩個VC。其實這就是containerView 自帶的容器VC,它讓我們可以把程式碼和業務都分發到一個獨立的VC裡面去操作,耦合性更低。

F9020D89-DD10-499B-867C-EAEB671EA94C.png

為了後面的操作我們需要在swift檔案裡面給這兩個VC繫結兩個Class,取名為Class1 和Class2 都繼承於BaseVC(BaseVC裡面只是簡單的對導航欄裡面的操作) 。然後要將ContainerView關聯的VC跟我們swift檔案裡面的類關聯起來,並且繫結它們的storyboardId

496E928E-DF3D-4EEF-8AB3-5E00B88C88E4.png

到現在的話,基本上容器的使用算是完成了,我們想做的業務就可以分別在兩個vc裡面實現了。但是這樣的話就太不直觀了,接著說個例子吧。

現在好多App 都有雙列表,比方說京東的

E9CF7C2DEB41E4D5ABE42757F3743A19.png

這個雙列表頁面,我們就能使用ContainerView 來實現類似的效果。

首先我們在Class1 和Class2 裡面各自新增兩個tableView 並給他們附上資料,最終的效果是這樣的

4111837F-1E2A-4BC2-9907-C5689457B970.png

這個時候如果我們想做資料的互動,就需要傳值。OC裡面的傳值方式有很多,通知,代理,block 等等,其實最簡單的就是使用NSNotificationCenter ,通知比較簡單,我就不說了,swift裡面的使用跟OC差不多。這裡說一下代理的用法和swift裡面閉包的用法

代理在這裡的用法

先說下代理,因為我們使用的是容器ContainerView 它會自帶兩個VC,又加上原來的一個VC,相當於三個VC,怎麼ContainerView裡面取到對應關聯的兩個VC,這是一個關鍵。

在承載連個ContainerView 的VC裡面重寫下面的方法

 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        
    }
複製程式碼

只需要重寫系統的這個方法,這個方法是SB裡面的檢視切換的時候系統會呼叫的一個方法,它的引數 segue.destination

let vc = segue.destination
複製程式碼

返回的就是對應的ContainerView 自帶的VC

E7476A3F-5AAC-4209-924B-4D9F3EA52CC1.png

因為我們使用了兩個VC 所以要判斷下是具體哪個VC

    var class1:Class1? = nil
    var class2:Class2? = nil
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let vc = segue.destination as? Class1 {
            class1 = vc
        }
        if let vc = segue.destination as? Class2 {
            class2 = vc
        }
    }
複製程式碼

取到這兩個VC後,就好辦多了。然後要做的無非就是宣告協議,遵循代理,實現代理方法

protocol XWContainerViewResponseDelegate {
    func clickWithGetIndex(index:NSIndexPath)
}
複製程式碼

宣告一個協議,引數為NSIndexPath 型別,然後讓Class1 宣告一個代理屬性

    var delegate:XWContainerViewResponseDelegate?
複製程式碼

然後在Class1 tabviewVIew 的點選方法裡面呼叫代理(不需要去呼叫responds(to: <#T##Selector!#>) delegate? 就相當於OC裡面的responds)

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
       delegate?.clickWithGetIndex(index: (indexPath as? NSIndexPath)!)
   }
複製程式碼

然後讓Class2 去遵循協議,實現代理方法,這樣就OK了。但是不知道大家有沒有發現,OC使用的時候我們一般都會設定

XXXX.delegate = XXXX
複製程式碼

這一步我們還沒有寫,但是這Class1 和 Class2 又是獨立的,沒有關聯。所以這個時候我們就要用到承載這兩個容器所在的VC了,在上面的那個方法裡面,我們獲取到了這兩個VC,在這裡進行關聯

    var class1:Class1? = nil
    var class2:Class2? = nil
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let vc = segue.destination as? Class1 {
            class1 = vc
        }
        if let vc = segue.destination as? Class2 {
            class2 = vc
        }
        class1?.delegate = class2
    }
複製程式碼

OK,這樣就完整了,點選Class1 的cell 就會把對應的IndexPath 傳過去,就可以做操作了

閉包在這裡的用法

在說下使用閉包傳值的做法 其實核心的地方都在承載兩個ContainerView 所在的VC裡面。因為我們所做的操作是想點選Class1 裡面的cell,把對方的Indexpath 給傳過去,那麼肯定是想把IndexPath 給回撥出去。回撥出去之後Class2 怎麼去接受呢?其實Class2 也可以有一個閉包專門去接受這個資料,但是這樣就太麻煩了。我們可以在Class2 裡面宣告一個屬性,去實現它的屬性觀察器方法,這樣就能隨時監聽這個屬性的變化,更方便的去做操作

首先在class1 裡面去宣告閉包,順便給它一個屬性去記錄所要傳遞的IndexPath

    typealias CallBack = (_ index:NSIndexPath)->Void
    var xwCallBack:CallBack?
    var class1Index: NSIndexPath? = nil
複製程式碼

Class2 裡面的操作 宣告屬性觀察器

 var class2Index:NSIndexPath? = nil {
        willSet {
            
        }
        didSet {
            print("得到的從1傳過來的值  \(String(describing: class2Index?.row))")
        }
    }
複製程式碼

然後在Class1 cell的點選方法裡面去呼叫它

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        xwCallBack!(indexPath as NSIndexPath)   
    }
複製程式碼

ContainerView 承載所在的VC 讓Class1 實現閉包

 var class1:Class1? = nil
    var class2:Class2? = nil
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let vc = segue.destination as? Class1 {
            class1 = vc
        }
        if let vc = segue.destination as? Class2 {
            class2 = vc
        }
        
        unowned let weakSelf = self
        class1?.xwCallBack = { index in
            weakSelf.class2?.class2Index = index
        }
    }
複製程式碼

然後把Class1傳過來的IndexPath 賦值給Class2的 帶有屬性觀察器的屬性。OK,這樣就可以了

其實swift 裡面的東西 大致跟OC用法還是一樣的,只不過增加了一些確實特別好用的東西。無論什麼方法,用到好就行。黑貓白貓,逮到老鼠就是好貓

一起學習吧。。。

相關文章