RxSwift (二)序列核心邏輯分析react
RxSwift (三)Observable的建立,訂閱,銷燬編程
RxSwift(四)高階函數swift
RxSwift(五)(Rxswift對比swift,oc用法)數組
Rxswift (六)銷燬者Dispose源碼分析markdown
RxSwift (十) 基礎使用篇 1- 序列,訂閱,銷燬閉包
RxSwift學習之十二 (基礎使用篇 3- UI控件擴展)併發
@TOCapp
實例1:
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) func buttonTapped() { print("button Tapped") } 複製代碼
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)") } } 複製代碼
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) } 複製代碼
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() 複製代碼
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) 複製代碼
實例5:
複製代碼
//監聽person對象的name的變化 self.person.rx.observeWeakly(String.self, "name") .subscribe(onNext: { (value) in print(value as Any) }) .disposed(by: disposeBag) 複製代碼
實例6:
複製代碼
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:
複製代碼
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:
複製代碼
let disposeBag = DisposeBag() var timer = Observable<Int>.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)") }) 複製代碼
/// 用 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:
咱們對比一下這兩段代碼:
將一個單獨的圖片設置到imageView上
let image: UIImage = UIImage(named: ...) imageView.image = image 複製代碼
let image: Observable<UIImage> = ... image.bind(to: imageView.rx.image) 複製代碼
Rx代碼:上面這段代碼是將一個圖片序列 「同步」 到imageView上。這個序列裏面的圖片能夠是異步產生的。這裏定義的 image 就是上圖中藍色部分(可監聽序列),imageView.rx.image就是上圖中橙色部分(觀察者)。而這種 「同步機制」 就是數據綁定(訂閱)。
實例30:
複製代碼
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) } } 複製代碼
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 } } 複製代碼
實例40:
self.button.addTarget(self, action:#selector(buttonTapped(sender:)), for: UIControlEvents.touchUpInside) @objc func buttonTapped(sender:UIButton?){ } 複製代碼
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<Int>.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<Int>.interval(1, scheduler: MainScheduler.instance) //將已過去的時間格式化成想要的字符串,並綁定到button上 timer.map(formatTimeInterval) .bind(to: button.rx.attributedTitle()) .disposed(by: disposeBag) } //按鈕圖標(image)的綁定 func test3() { //建立一個計時器(每1秒發送一個索引數) let timer = Observable<Int>.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<Int>.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) } } 複製代碼
實例50:
複製代碼
複製代碼
實例60:
複製代碼
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) } } 複製代碼
實例70:
複製代碼
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) } } 複製代碼
實例80:
複製代碼
複製代碼
實例90:UITextField使用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) 複製代碼
實例91:Rxswift監聽單個 textField 內容的變化
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) } } 複製代碼
實例92:Rxswift將textField的內容綁定到其餘控件上
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) } } 複製代碼
實例93:Rxswift同時監聽多個 textField 內容的變化
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) } } 複製代碼
實例94: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) } } 複製代碼
實例95:Rxswift實現textField事件監聽
複製代碼
實例100:
複製代碼
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) } } 複製代碼
//歌曲結構體 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])】") } } 複製代碼
實例111:
/*這裏咱們將 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)。*/ } } 複製代碼
實例120:
複製代碼
複製代碼
實例130:
複製代碼
複製代碼
實例140:
複製代碼
複製代碼
實例150:
複製代碼
複製代碼
實例160:
複製代碼
複製代碼