RxSwiftをやろうかなと思い、チャット風なUIをとりあえず作って見たのでメモがてら投稿
概要
- サーバー等置いてないので、入力した文字を表示していくだけ
- 送信ボタンを押したら、text入力欄に入力されてるtextをリストに追加して表示
これだけしかやってません
Rx風な考え方
なにかActionがあったときに、なにかするという予約を入れておくようなイメージで作りました
クラス説明
要素クラス
struct VoiceItem {
var user: String
var message: String
}
送るuser
と送るmessage
のみのクラスです
特に難しいこともしてないです
Cellクラス
class VoiceCell: UITableViewCell {
@IBOutlet weak var userLabel: UILabel!
@IBOutlet weak var messageLabel: UILabel!
func item(_ newValue: VoiceItem) {
userLabel.text = newValue.user
messageLabel.text = newValue.message
}
}
user
とmessage
を表示するUILabel
を設置しておきます
先ほどの要素クラスを受け取ったら、各ラベルに情報を反映させます
要素管理クラス
class VoiceModel {
let itemsEvent = Variable<[VoiceItem]>([])
var items: Observable<[VoiceItem]> {
return itemsEvent.asObservable()
}
var rx_items: AnyObserver<String> {
return AnyObserver<String>(eventHandler: { (e) in
self.addItem(item: VoiceItem(user: "A", message: e.element!))
self.addItem(item: VoiceItem(user: "B", message: e.element! + "ですか!すごーい!"))
})
}
init() {
}
func addItem(item: VoiceItem) {
itemsEvent.value.append(item)
}
}
ここでRxSwift
がやっと登場します
items
が要素の配列になってます。監視される対象なのでObservable
で定義します
addItem
は要素を追加するメソッドです
rx_items
は、なにかあったときにする処理を書いておきます。これにより監視される対象からこの処理を呼び出してもらうことができます。(監視される対象とrx_items
のつなぎはViewController
クラスで説明します)
ViewControllerクラス
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var send: UIButton!
let disposeBag = DisposeBag()
let model = VoiceModel()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// TableViewと要素の配列
model.items.bindTo(self.tableView.rx.items(cellIdentifier: "Cell", cellType: VoiceCell.self)) { (row, element, cell) in
cell.item(element)
}.disposed(by: disposeBag)
// 送信ボタンを押す
self.send.rx.tap.map{ self.textField.text ?? "" }.bindTo(model.rx_items).disposed(by: disposeBag)
self.send.rx.tap.subscribe(onNext: { _ in
self.textField.text = nil
}).disposed(by: disposeBag)
// text入力された時とボタンの有効
self.textField.rx.text.map { ($0?.characters.count)! > 0 }.bindTo(self.send.rx.isEnabled).disposed(by: disposeBag)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
ここがわかりにくいと思うので、少し詳しく考え方を...
TableViewと要素の配列
分解して説明していきます
model.items.bindTo()
まず、要素の配列items
に変化があったときにbindTo()
の引数に処理をするように予約しておきます
変化があったときとは具体的には、items
に要素が追加されたときなどです
self.tableView.rx.items(cellIdentifier: "Cell", cellType: VoiceCell.self)) { (row, element, cell) in
cell.item(element)
}
self.tableView.rx.items
にはすでに処理が書かれているのでそれを使います
見てわかるかと思いますが、Cell
を作る処理(tablleView
に追加もしてくれます)です
cellIdentifier
、cellType
は普段使ってるかと思います
Closureの引数は、
-
row
がIndex -
element
が要素の配列のIndex番目の要素 -
cell
がcellType
で指定した型のCell
です
今回は、(row: Int, element: VoiceItem, cell: VoiceCell)
という型になります
.disposed(by: disposeBag)
最後に、disposed
ですが、これは処理の予約の破棄をしてくれます
今回は、DisposeBag()
を引数に渡していますので、このクラス(ViewController
)が破棄されるときに破棄してくれます
送信ボタンを押す
self.send.rx.tap.map{ self.textField.text ?? "" }.bindTo(model.rx_items).disposed(by: disposeBag)
送信ボタンを押したときは、self.send.rx.tap
で監視できます
tap
したときにtextがあるかをmap
内で判断してます(filter
でもよいかも...)
先ほど作った、rx_items
をbindすることによって、rx_items
に書いた処理をさせることができます
self.send.rx.tap.subscribe(onNext: { _ in
self.textField.text = nil
}).disposed(by: disposeBag)
こちらは、tap
があったときに、入力していたtextを削除します
text入力された時とボタンの有効
self.textField.rx.text.map { ($0?.characters.count)! > 0 }.bindTo(self.send.rx.isEnabled).disposed(by: disposeBag)
self.textField.rx.text
でtextの入力を監視することができます
textが1文字以上入力された時、map
でbool
の値にして、self.send.rx.isEnabled
に入れるという形になります
これで、textが入力されたら、送信ボタンが押せるようになる処理の予約ができました
ユーザー名はA
固定でやってます
感想
-
UITableViewDelegate
やUITableViewDataSource
といったものを設置しなくてもよい - (最初に処理を予約しておけば)リストの要素を管理しなくてよい
- text入力を自前で監視しなくてよい
と、いいこと(簡単なこと)が多かったです
RxSwiftは奥が深いのでまだまだこれからやっていこうと思います