二、UITextField 與 UITextView
1,監(jiān)聽單個 textField 內(nèi)容的變化(textView 同理)
(1)下面樣例中我們將 textField 里輸入的內(nèi)容實時地顯示到控制臺中。


(2)樣例代碼
注意:.orEmpty 可以將 String? 類型的 ControlProperty 轉成 String,省得我們再去解包。
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag()
override func viewDidLoad() {
//創(chuàng)建文本輸入框
let textField = UITextField(frame: CGRect(x:10, y:80, width:200, height:30))
textField.borderStyle = UITextBorderStyle.roundedRect
self.view.addSubview(textField)
//當文本框內(nèi)容改變時,將內(nèi)容輸出到控制臺上
textField.rx.text.orEmpty.asObservable()
.subscribe(onNext: {
print("您輸入的是:\($0)")
})
.disposed(by: disposeBag)
}
}
(3)當然我們直接使用 change 事件效果也是一樣的。
//當文本框內(nèi)容改變時,將內(nèi)容輸出到控制臺上
textField.rx.text.orEmpty.changed
.subscribe(onNext: {
print("您輸入的是:\($0)")
})
.disposed(by: disposeBag)
2,將內(nèi)容綁定到其他控件上
(1)效果圖
- 我們將第一個
textField里輸入的內(nèi)容實時地顯示到第二個textField中。 - 同時
label中還會實時顯示當前的字數(shù)。 - 最下方的“提交”按鈕會根據(jù)當前的字數(shù)決定是否可用(字數(shù)超過 5 個字才可用)


(2)樣例代碼
Throttling 的作用:
Throttling是RxSwift的一個特性。因為有時當一些東西改變時,通常會做大量的邏輯操作。而使用Throttling特性,不會產(chǎn)生大量的邏輯操作,而是以一個小的合理的幅度去執(zhí)行。比如做一些實時搜索功能時,這個特性很有用。
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag()
override func viewDidLoad() {
//創(chuàng)建文本輸入框
let inputField = UITextField(frame: CGRect(x:10, y:80, width:200, height:30))
inputField.borderStyle = UITextBorderStyle.roundedRect
self.view.addSubview(inputField)
//創(chuàng)建文本輸出框
let outputField = UITextField(frame: CGRect(x:10, y:150, width:200, height:30))
outputField.borderStyle = UITextBorderStyle.roundedRect
self.view.addSubview(outputField)
//創(chuàng)建文本標簽
let label = UILabel(frame:CGRect(x:20, y:190, width:300, height:30))
self.view.addSubview(label)
//創(chuàng)建按鈕
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)
//當文本框內(nèi)容改變
let input = inputField.rx.text.orEmpty.asDriver() // 將普通序列轉換為 Driver
.throttle(0.3) //在主線程中操作,0.3秒內(nèi)值若多次改變,取最后一次
//內(nèi)容綁定到另一個輸入框中
input.drive(outputField.rx.text)
.disposed(by: disposeBag)
//內(nèi)容綁定到文本標簽中
input.map{ "當前字數(shù):\($0.count)" }
.drive(label.rx.text)
.disposed(by: disposeBag)
//根據(jù)內(nèi)容字數(shù)決定按鈕是否可用
input.map{ $0.count > 5 }
.drive(button.rx.isEnabled)
.disposed(by: disposeBag)
}
}
3,同時監(jiān)聽多個 textField 內(nèi)容的變化(textView 同理)
(1)效果圖
- 界面上有兩個輸入框分別用于填寫電話的區(qū)號和號碼。
- 無論那一個輸入框內(nèi)容發(fā)生變化,都會將它們拼成完整的號碼并顯示在
label中。

(2)樣例代碼
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)
}
}
4,事件監(jiān)聽
(1)通過 rx.controlEvent 可以監(jiān)聽輸入框的各種事件,且多個事件狀態(tài)可以自由組合。除了各種 UI 控件都有的 touch 事件外,輸入框還有如下幾個獨有的事件:
- editingDidBegin:開始編輯(開始輸入內(nèi)容)
- editingChanged:輸入內(nèi)容發(fā)生改變
- editingDidEnd:結束編輯
-
editingDidEndOnExit:按下
return鍵結束編輯 - allEditingEvents:包含前面的所有編輯相關事件
(2)下面代碼監(jiān)聽輸入框開始編輯事件(獲取到焦點)并做相應的響應。

textField.rx.controlEvent([.editingDidBegin]) //狀態(tài)可以組合
.asObservable()
.subscribe(onNext: { _ in
print("開始編輯內(nèi)容!")
}).disposed(by: disposeBag)
(3)下面代碼我們在界面上添加兩個輸入框分別用于輸入用戶名和密碼:
- 如果當前焦點在用戶名輸入框時,按下
return鍵時焦點自動轉移到密碼輸入框上。 - 如果當前焦點在密碼輸入框時,按下
return鍵時自動移除焦點。

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 獨有的方法
(1)UITextView 還封裝了如下幾個委托回調(diào)方法:
- didBeginEditing:開始編輯
- didEndEditing:結束編輯
- didChange:編輯內(nèi)容發(fā)生改變
- didChangeSelection:選中部分發(fā)生變化
(2)使用樣例


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)
//內(nèi)容發(fā)生變化響應
textView.rx.didChange
.subscribe(onNext: {
print("內(nèi)容發(fā)生改變")
})
.disposed(by: disposeBag)
//選中部分變化響應
textView.rx.didChangeSelection
.subscribe(onNext: {
print("選中部分發(fā)生變化")
})
.disposed(by: disposeBag)
}
}