RxSwift學習:RxSwift對比Swift,OC用法(五)

語言: CN / TW / HK

RxSwift常用的資料處理

Target Action

例項1

  • 傳統程式碼 ``` button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)

func buttonTapped() { print("button Tapped") } - RxSwift程式碼 button.rx.tap .subscribe(onNext: { print("button Tapped") }) .disposed(by: disposeBag) ``` 你不需要使用 Target Action,這樣使得程式碼邏輯清晰可見。

代理

例項2

  • 傳統程式碼 ``` class ViewController: UIViewController { ... override func viewDidLoad() { super.viewDidLoad() scrollView.delegate = self } }

extension ViewController: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { print("contentOffset: (scrollView.contentOffset)") } } - RxSwift程式碼 class ViewController: UIViewController { ... override func viewDidLoad() { super.viewDidLoad()

    scrollView.rx.contentOffset
        .subscribe(onNext: { contentOffset in
            print("contentOffset: \(contentOffset)")
        })
        .disposed(by: disposeBag)
}

} ``` RxSwift實現的代理,你不需要書寫代理的配置程式碼,就能獲得想要的結果。

通知

例項3

  • 傳統程式碼 ``` var ntfObserver: NSObjectProtocol!

override func viewDidLoad() { super.viewDidLoad()

ntfObserver = NotificationCenter.default.addObserver(
      forName: .UIApplicationWillEnterForeground,
      object: nil, queue: nil) { (notification) in
    print("Application Will Enter Foreground")
}

}

deinit { NotificationCenter.default.removeObserver(ntfObserver) } - RxSwift程式碼 NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification) .subscribe(onNext: { (noti) in print(noti) }) .disposed(by: disposeBag) ```

閉包回撥

例項4

  • 傳統程式碼 ``` URLSession.shared.dataTask(with: URLRequest(url: url)) { (data, response, error) in guard error == nil else { print("Data Task Error: (error!)") return }

    guard let data = data else { print("Data Task Error: unknown") return }

    print("Data Task Success with count: (data.count)") }.resume() - RxSwift程式碼 URLSession.shared.rx.data(request: URLRequest(url: url)) .subscribe(onNext: { data in print("Data Task Success with count: (data.count)") }, onError: { error in print("Data Task Error: (error)") }) .disposed(by: disposeBag) ```

    KVO

例項5

  • 傳統程式碼

```

- RxSwift程式碼 //監聽person物件的name的變化 self.person.rx.observeWeakly(String.self, "name") .subscribe(onNext: { (value) in print(value as Any) }) .disposed(by: disposeBag) ```

手勢

例項6

  • 傳統程式碼 ```

- RxSwift程式碼 let disposeBag = DisposeBag() let tap = UITapGestureRecognizer() self.label.addGestureRecognizer(tap) self.label.isUserInteractionEnabled = true tap.rx.event.subscribe(onNext: { (tap) in print(tap.view) }) .disposed(by: disposeBag) ```

網路請求

例項7

  • 傳統程式碼 ```

- RxSwift程式碼 let url = URL(string: "https://www.baidu.com") URLSession.shared.rx.response(request: URLRequest(url: url!)).subscribe(onNext: { (response,data) in print(response) }, onError: { (error) in print(error) }, onCompleted: {

    }).disposed(by: disposeBag)

```

定時器

例項8

  • 傳統程式碼 ```

- RxSwift程式碼 let disposeBag = DisposeBag() var timer = Observable.interval(1, scheduler: MainScheduler.instance) timer.subscribe(onNext: { (num) in print(num) }) .disposed(by: disposeBag) ```

多個任務之間有依賴關係

例項9:\ 例如,先通過使用者名稱密碼取得 Token 然後通過 Token 取得使用者資訊

  • 傳統程式碼 ``` /// 用回撥的方式封裝介面 enum API {

    /// 通過使用者名稱密碼取得一個 token static func token(username: String, password: String, success: (String) -> Void, failure: (Error) -> Void) { ... }

    /// 通過 token 取得使用者資訊 static func userinfo(token: String, success: (UserInfo) -> Void, failure: (Error) -> Void) { ... } }

/// 通過使用者名稱和密碼獲取使用者資訊 API.token(username: "beeth0ven", password: "987654321", success: { token in API.userInfo(token: token, success: { userInfo in print("獲取使用者資訊成功: (userInfo)") }, failure: { error in print("獲取使用者資訊失敗: (error)") }) }, failure: { error in print("獲取使用者資訊失敗: (error)") }) - RxSwift程式碼 /// 用 Rx 封裝介面 enum API {

/// 通過使用者名稱密碼取得一個 token
static func token(username: String, password: String) -> Observable<String> { ... }

/// 通過 token 取得使用者資訊
static func userInfo(token: String) -> Observable<UserInfo> { ... }

}

/// 通過使用者名稱和密碼獲取使用者資訊 API.token(username: "beeth0ven", password: "987654321") .flatMapLatest(API.userInfo) .subscribe(onNext: { userInfo in print("獲取使用者資訊成功: (userInfo)") }, onError: { error in print("獲取使用者資訊失敗: (error)") }) .disposed(by: disposeBag) ```

等待多個併發任務完成後處理結果

例項10:\ 例如,需要將兩個網路請求合併成一個

通過 Rx 來實現: ``` /// 用 Rx 封裝介面 enum API {

/// 取得老師的詳細資訊
static func teacher(teacherId: Int) -> Observable<Teacher> { ... }

/// 取得老師的評論
static func teacherComments(teacherId: Int) -> Observable<[Comment]> { ... }

}

/// 同時取得老師資訊和老師評論 Observable.zip( API.teacher(teacherId: teacherId), API.teacherComments(teacherId: teacherId) ).subscribe(onNext: { (teacher, comments) in print("獲取老師資訊成功: (teacher)") print("獲取老師評論成功: (comments.count) 條") }, onError: { error in print("獲取老師資訊或評論失敗: (error)") }) .disposed(by: disposeBag) ``` 這樣你可用寥寥幾行程式碼來完成相當複雜的非同步操作。

資料繫結

例項11

image.png

在 RxSwift 裡有一個比較重要的概念就是資料繫結(訂閱)。就是指將可監聽序列繫結到觀察者上:

我們對比一下這兩段程式碼:

  1. 傳統程式碼:

將一個單獨的圖片設定到imageView上 let image: UIImage = UIImage(named: ...) imageView.image = image 2. Rx程式碼: let image: Observable<UIImage> = ... image.bind(to: imageView.rx.image) Rx程式碼:上面這段程式碼是將一個圖片序列 “同步” 到imageView上。這個序列裡面的圖片可以是非同步產生的。這裡定義的 image 就是上圖中藍色部分(可監聽序列),imageView.rx.image就是上圖中橙色部分(觀察者)。而這種 “同步機制” 就是資料繫結(訂閱)。

RxSwift UI 用法

UILabel

例項12

  • 傳統程式碼 ```

``` - RxSwift程式碼

  1. RxSwift簡單使用UILabel ``` import UIKit import RxSwift import RxCocoa

class ViewController: UIViewController {

let disposeBag = DisposeBag()

override func viewDidLoad() {

    //建立文字標籤
    let label = UILabel(frame:CGRect(x:20, y:40, width:300, height:100))
    self.view.addSubview(label)

    //建立一個計時器(每0.1秒傳送一個索引數)
    let timer = Observable<Int>.interval(0.1, scheduler: MainScheduler.instance)

    //將已過去的時間格式化成想要的字串,並繫結到label上
    timer.map{ String(format: "%0.2d:%0.2d.%0.1d",
                      arguments: [($0 / 600) % 600, ($0 % 600 ) / 10, $0 % 10]) }
    .bind(to: label.rx.text)
    .disposed(by: disposeBag)
}

} 2. UILabel富文字 :將資料繫結到 attributedText 屬性上 import UIKit import RxSwift import RxCocoa

class ViewController: UIViewController {

let disposeBag = DisposeBag()

override func viewDidLoad() {

    //建立文字標籤
    let label = UILabel(frame:CGRect(x:20, y:40, width:300, height:100))
    self.view.addSubview(label)

    //建立一個計時器(每0.1秒傳送一個索引數)
    let timer = Observable<Int>.interval(0.1, scheduler: MainScheduler.instance)

    //將已過去的時間格式化成想要的字串,並繫結到label上
    timer.map(formatTimeInterval)
    .bind(to: label.rx.attributedText)
    .disposed(by: disposeBag)
}

//將數字轉成對應的富文字
func formatTimeInterval(ms: NSInteger) -> NSMutableAttributedString {
    let string = String(format: "%0.2d:%0.2d.%0.1d",
                     arguments: [(ms / 600) % 600, (ms % 600 ) / 10, ms % 10])
    //富文字設定
    let attributeString = NSMutableAttributedString(string: string)
    //從文字0開始6個字元字型HelveticaNeue-Bold,16號
    attributeString.addAttribute(NSAttributedStringKey.font,
                                 value: UIFont(name: "HelveticaNeue-Bold", size: 16)!,
                                 range: NSMakeRange(0, 5))
    //設定字型顏色
    attributeString.addAttribute(NSAttributedStringKey.foregroundColor,
                                 value: UIColor.white, range: NSMakeRange(0, 5))
    //設定文字背景顏色
    attributeString.addAttribute(NSAttributedStringKey.backgroundColor,
                                 value: UIColor.orange, range: NSMakeRange(0, 5))
    return attributeString
}

} ```

UIButton

例項13

  • 傳統程式碼 ``` self.button.addTarget(self, action:#selector(buttonTapped(sender:)), for: UIControlEvents.touchUpInside) @objc func buttonTapped(sender:UIButton?){

    } - RxSwift程式碼 let disposeBag = DisposeBag() //由於tap事件裡點選事件用的最多,所以RX預設的tap就是點選事件 self.button.rx.tap .subscribe(onNext: { () in print("點選來了") }) .disposed(by: disposeBag)

//RXSwift監聽按鈕除了點選外的事件: self.button.rx.controlEvent(.touchUpOutside).subscribe(onNext: { () in

    })
        .disposed(by: disposeBag)

import UIKit import RxSwift import RxCocoa

class ViewController: UIViewController {

let disposeBag = DisposeBag()

@IBOutlet weak var button: UIButton!

override func viewDidLoad() {
    //按鈕點選響應1
    button.rx.tap
        .subscribe(onNext: { [weak self] in
            self?.showMessage("按鈕被點選")
        })
        .disposed(by: disposeBag)

    //按鈕點選響應2
    button.rx.tap
         .bind { [weak self] in
         self?.showMessage("按鈕被點選")
           }
           .disposed(by: disposeBag)

// }

//按鈕標題(title)的繫結 func test1() { //建立一個計時器(每1秒傳送一個索引數) let timer = Observable.interval(1, scheduler: MainScheduler.instance)

//根據索引數拼接最新的標題,並繫結到button上 timer.map{"計數($0)"} .bind(to: button.rx.title(for: .normal)) .disposed(by: disposeBag) }

//按鈕富文字標題(attributedTitle)的繫結 func test2() { //建立一個計時器(每1秒傳送一個索引數) let timer = Observable.interval(1, scheduler: MainScheduler.instance)

    //將已過去的時間格式化成想要的字串,並繫結到button上
    timer.map(formatTimeInterval)
        .bind(to: button.rx.attributedTitle())
        .disposed(by: disposeBag)

}

//按鈕圖示(image)的繫結 func test3() { //建立一個計時器(每1秒傳送一個索引數) let timer = Observable.interval(1, scheduler: MainScheduler.instance)

//根據索引數選擇對應的按鈕圖示,並繫結到button上 timer.map({ let name = $0%2 == 0 ? "back" : "forward" return UIImage(named: name)! }) .bind(to: button.rx.image()) .disposed(by: disposeBag) }

//按鈕背景圖片(backgroundImage)的繫結 func test4() { //建立一個計時器(每1秒傳送一個索引數) let timer = Observable.interval(1, scheduler: MainScheduler.instance)

//根據索引數選擇對應的按鈕背景圖,並繫結到button上 timer.map{ UIImage(named: "($0%2)")! } .bind(to: button.rx.backgroundImage()) .disposed(by: disposeBag) }

//將數字轉成對應的富文字 func formatTimeInterval(ms: NSInteger) -> NSMutableAttributedString { let string = String(format: "%0.2d:%0.2d.%0.1d", arguments: [(ms / 600) % 600, (ms % 600 ) / 10, ms % 10]) //富文字設定 let attributeString = NSMutableAttributedString(string: string) //從文字0開始6個字元字型HelveticaNeue-Bold,16號 attributeString.addAttribute(NSAttributedStringKey.font, value: UIFont(name: "HelveticaNeue-Bold", size: 16)!, range: NSMakeRange(0, 5)) //設定字型顏色 attributeString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.white, range: NSMakeRange(0, 5)) //設定文字背景顏色 attributeString.addAttribute(NSAttributedStringKey.backgroundColor, value: UIColor.orange, range: NSMakeRange(0, 5)) return attributeString }

//顯示訊息提示框
func showMessage(_ text: String) {
    let alertController = UIAlertController(title: text, message: nil, preferredStyle: .alert)
    let cancelAction = UIAlertAction(title: "確定", style: .cancel, handler: nil)
    alertController.addAction(cancelAction)
    self.present(alertController, animated: true, completion: nil)
}

} ```

UISwitch

例項14

  • 傳統程式碼 ```

- RxSwift程式碼 import UIKit import RxSwift import RxCocoa

class ViewController: UIViewController {

//分段選擇控制元件
@IBOutlet weak var segmented: UISegmentedControl!
//圖片顯示控制元件
@IBOutlet weak var imageView: UIImageView!

let disposeBag = DisposeBag()

override func viewDidLoad() {
    //建立一個當前需要顯示的圖片的可觀察序列
    let showImageObservable: Observable<UIImage> =
        segmented.rx.selectedSegmentIndex.asObservable().map {
            let images = ["js.png", "php.png", "react.png"]
            return UIImage(named: images[$0])!
    }

    //把需要顯示的圖片繫結到 imageView 上
    showImageObservable.bind(to: imageView.rx.image)
        .disposed(by: disposeBag)
}

func test1() { switch1.rx.isOn.asObservable() .subscribe(onNext: { print("當前開關狀態:($0)") }) .disposed(by: disposeBag) }

func test2() { switch1.rx.isOn .bind(to: button1.rx.isEnabled) .disposed(by: disposeBag) } } ```

UISegmentedControl

例項15

  • 傳統程式碼 ```

- RxSwift程式碼 import UIKit import RxSwift import RxCocoa

class ViewController: UIViewController {

//分段選擇控制元件
@IBOutlet weak var segmented: UISegmentedControl!
//圖片顯示控制元件
@IBOutlet weak var imageView: UIImageView!

let disposeBag = DisposeBag()

override func viewDidLoad() {
    //建立一個當前需要顯示的圖片的可觀察序列
    let showImageObservable: Observable<UIImage> =
        segmented.rx.selectedSegmentIndex.asObservable().map {
            let images = ["js.png", "php.png", "react.png"]
            return UIImage(named: images[$0])!
    }

    //把需要顯示的圖片繫結到 imageView 上
    showImageObservable.bind(to: imageView.rx.image)
        .disposed(by: disposeBag)
}

func test1() { segmented.rx.selectedSegmentIndex.asObservable() .subscribe(onNext: { print("當前項:($0)") }) .disposed(by: disposeBag) } }

```

UITextField

例項16:UITextField使用RxSwift的基本用法

  • 傳統程式碼 ```

- RxSwift程式碼 self.textFiled.rx.text.orEmpty .subscribe(onNext: { (text) in print(text) }) .disposed(by: disposeBag) // textfiled繫結Button的文字 self.textFiled.rx.text .bind(to: self.button.rx.title()) .disposed(by: disposeBag) ``` 例項17:RxSwift監聽單個 textField 內容的變化

  • RxSwift程式碼 ``` import UIKit import RxSwift import RxCocoa

class ViewController: UIViewController {

let disposeBag = DisposeBag()

override func viewDidLoad() {

    //建立文字輸入框
    let textField = UITextField(frame: CGRect(x:10, y:80, width:200, height:30))
    textField.borderStyle = UITextBorderStyle.roundedRect
    self.view.addSubview(textField)

    //當文字框內容改變時,將內容輸出到控制檯上
    textField.rx.text.orEmpty.asObservable()
        .subscribe(onNext: {
            print("您輸入的是:\($0)")
        })
        .disposed(by: disposeBag)

//當文字框內容改變時,將內容輸出到控制檯上 textField.rx.text.orEmpty.changed .subscribe(onNext: { print("您輸入的是:($0)") }) .disposed(by: disposeBag)

}

} ``` 例項18:RxSwift將textField的內容繫結到其他控制元件上

  • RxSwift程式碼 ``` import UIKit import RxSwift import RxCocoa

class ViewController: UIViewController {

let disposeBag = DisposeBag()

override func viewDidLoad() {

    //建立文字輸入框
    let inputField = UITextField(frame: CGRect(x:10, y:80, width:200, height:30))
    inputField.borderStyle = UITextBorderStyle.roundedRect
    self.view.addSubview(inputField)

    //建立文字輸出框
    let outputField = UITextField(frame: CGRect(x:10, y:150, width:200, height:30))
    outputField.borderStyle = UITextBorderStyle.roundedRect
    self.view.addSubview(outputField)

    //建立文字標籤
    let label = UILabel(frame:CGRect(x:20, y:190, width:300, height:30))
    self.view.addSubview(label)

    //建立按鈕
    let button:UIButton = UIButton(type:.system)
    button.frame = CGRect(x:20, y:230, width:40, height:30)
    button.setTitle("提交", for:.normal)
    self.view.addSubview(button)


    //當文字框內容改變
    let input = inputField.rx.text.orEmpty.asDriver() // 將普通序列轉換為 Driver
        .throttle(0.3) //在主執行緒中操作,0.3秒內值若多次改變,取最後一次

    //內容繫結到另一個輸入框中
    input.drive(outputField.rx.text)
        .disposed(by: disposeBag)

    //內容繫結到文字標籤中
    input.map{ "當前字數:\($0.count)" }
        .drive(label.rx.text)
        .disposed(by: disposeBag)

    //根據內容字數決定按鈕是否可用
    input.map{ $0.count > 5 }
        .drive(button.rx.isEnabled)
        .disposed(by: disposeBag)
}

} ``` 例項19:RxSwift同時監聽多個 textField 內容的變化

  • RxSwift程式碼 ``` import UIKit import RxSwift import RxCocoa

class ViewController: UIViewController {

let disposeBag = DisposeBag()

@IBOutlet weak var textField1: UITextField!
@IBOutlet weak var textField2: UITextField!
@IBOutlet weak var label: UILabel!

override func viewDidLoad() {

    Observable.combineLatest(textField1.rx.text.orEmpty, textField2.rx.text.orEmpty) {
        textValue1, textValue2 -> String in
        return "你輸入的號碼是:\(textValue1)-\(textValue2)"
        }
        .map { $0 }
        .bind(to: label.rx.text)
        .disposed(by: disposeBag)
}

} ``` 例項20:RxSwift實現textField事件監聽 通過 rx.controlEvent 可以監聽輸入框的各種事件,且多個事件狀態可以自由組合。除了各種 UI 控制元件都有的 touch 事件外,輸入框還有如下幾個獨有的事件:

  • editingDidBegin:開始編輯(開始輸入內容)

  • editingChanged:輸入內容發生改變

  • editingDidEnd:結束編輯

  • editingDidEndOnExit:按下 return 鍵結束編輯

  • allEditingEvents:包含前面的所有編輯相關事件

  • RxSwift程式碼 textField.rx.controlEvent([.editingDidBegin]) //狀態可以組合 .asObservable() .subscribe(onNext: { _ in print("開始編輯內容!") }).disposed(by: disposeBag) ``` import UIKit import RxSwift import RxCocoa

class ViewController: UIViewController {

//使用者名稱輸入框
@IBOutlet weak var username: UITextField!

//密碼輸入框
@IBOutlet weak var password: UITextField!

let disposeBag = DisposeBag()

override func viewDidLoad() {
    super.viewDidLoad()

    //在使用者名稱輸入框中按下 return 鍵
    username.rx.controlEvent(.editingDidEndOnExit).subscribe(onNext: {
        [weak self] (_) in
        self?.password.becomeFirstResponder()
    }).disposed(by: disposeBag)

     //在密碼輸入框中按下 return 鍵
    password.rx.controlEvent(.editingDidEndOnExit).subscribe(onNext: {
        [weak self] (_) in
        self?.password.resignFirstResponder()
    }).disposed(by: disposeBag)
}

} ```

UITextView

例項21

  • 傳統程式碼 ```

- RxSwift程式碼 import UIKit import RxSwift import RxCocoa

class ViewController: UIViewController {

let disposeBag = DisposeBag()

@IBOutlet weak var textView: UITextView!

override func viewDidLoad() {

    //開始編輯響應
    textView.rx.didBeginEditing
        .subscribe(onNext: {
            print("開始編輯")
        })
        .disposed(by: disposeBag)

    //結束編輯響應
    textView.rx.didEndEditing
        .subscribe(onNext: {
            print("結束編輯")
        })
        .disposed(by: disposeBag)

    //內容發生變化響應
    textView.rx.didChange
        .subscribe(onNext: {
            print("內容發生改變")
        })
        .disposed(by: disposeBag)

    //選中部分變化響應
    textView.rx.didChangeSelection
        .subscribe(onNext: {
            print("選中部分發生變化")
        })
        .disposed(by: disposeBag)
}

} ```

UITableView

  • 傳統Swift使用UITableView\ 例項22: ``` //歌曲結構體 struct Music { let name: String //歌名 let singer: String //演唱者

    init(name: String, singer: String) { self.name = name self.singer = singer } }

//歌曲列表資料來源 struct MusicListViewModel { let data = [ Music(name: "無條件", singer: "陳奕迅"), Music(name: "你曾是少年", singer: "S.H.E"), Music(name: "從前的我", singer: "陳潔儀"), Music(name: "在木星", singer: "朴樹"), ] }

class ViewController: UIViewController {

//tableView物件
@IBOutlet weak var tableView: UITableView!

//歌曲列表資料來源
let musicListViewModel = MusicListViewModel()

override func viewDidLoad() {
    super.viewDidLoad()

    //設定代理
    tableView.dataSource = self
    tableView.delegate = self
}

}

extension ViewController: UITableViewDataSource { //返回單元格數量 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return musicListViewModel.data.count }

//返回對應的單元格
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
    -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "musicCell")!
    let music = musicListViewModel.data[indexPath.row]
    cell.textLabel?.text = music.name
    cell.detailTextLabel?.text = music.singer
    return cell
}

}

extension ViewController: UITableViewDelegate { //單元格點選 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { print("你選中的歌曲資訊【(musicListViewModel.data[indexPath.row])】") } } ``` - RxSwift 的UITableView

例項23: ``` /這裡我們將 data 屬性變成一個可觀察序列物件(Observable Squence), 而物件當中的內容和我們之前在陣列當中所包含的內容是完全一樣的。 / //歌曲列表資料來源 struct MusicListViewModel { let data = Observable.just([ Music(name: "無條件", singer: "陳奕迅"), Music(name: "你曾是少年", singer: "S.H.E"), Music(name: "從前的我", singer: "陳潔儀"), Music(name: "在木星", singer: "朴樹"), ]) }

import UIKit import RxSwift import RxCocoa

class ViewController: UIViewController {

//tableView物件
@IBOutlet weak var tableView: UITableView!

//歌曲列表資料來源
let musicListViewModel = MusicListViewModel()

//負責物件銷燬
let disposeBag = DisposeBag()

override func viewDidLoad() {
    super.viewDidLoad()

    //將資料來源資料繫結到tableView上
    musicListViewModel.data
        .bind(to: 
        /*
   rx.items(cellIdentifier:):這是 Rx 基於 cellForRowAt 資料來源方法的一個封裝。
   傳統方式中我們還要有個 numberOfRowsInSection 方法,
   使用 Rx 後就不再需要了(Rx 已經幫我們完成了相關工作)。
   */ 
   tableView.rx.items(cellIdentifier:"musicCell")) { _, music, cell in
            cell.textLabel?.text = music.name
            cell.detailTextLabel?.text = music.singer
        }.disposed(by: disposeBag)

    //tableView點選響應
   /*
   rx.modelSelected: 這是 Rx 基於 UITableView委託回撥方法 didSelectRowAt 的一個封裝。
   */ tableView.rx.modelSelected(Music.self).subscribe(onNext: { music in
        print("你選中的歌曲資訊【\(music)】")
    }).disposed(by: disposeBag)
    /*DisposeBag:作用是 Rx 在檢視控制器或者其持有者將要銷燬的時候,
    自動釋法掉繫結在它上面的資源。
    它是通過類似“訂閱處置機制”方式實現(類似於 NotificationCenter 的 removeObserver)。*/
}

}

``` 收錄自|地址

Swift書籍資料下載:下載地址