LoginSignup
8
3

More than 5 years have passed since last update.

TableViewでチャット風な何か(RxSwift)

Last updated at Posted at 2017-05-08

RxSwiftをやろうかなと思い、チャット風なUIをとりあえず作って見たのでメモがてら投稿

概要

  • サーバー等置いてないので、入力した文字を表示していくだけ
  • 送信ボタンを押したら、text入力欄に入力されてるtextをリストに追加して表示

これだけしかやってません:cry:

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        
    }
}

usermessageを表示する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に追加もしてくれます)です
cellIdentifiercellTypeは普段使ってるかと思います
Closureの引数は、

  • rowがIndex
  • elementが要素の配列のIndex番目の要素
  • cellcellTypeで指定した型の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文字以上入力された時、mapboolの値にして、self.send.rx.isEnabledに入れるという形になります
これで、textが入力されたら、送信ボタンが押せるようになる処理の予約ができました

スクリーンショット 2017-05-08 16.20.38.png
スクリーンショット 2017-05-08 16.20.47.png
スクリーンショット 2017-05-08 16.20.51.png

ユーザー名はA固定でやってます

感想

  • UITableViewDelegateUITableViewDataSourceといったものを設置しなくてもよい
  • (最初に処理を予約しておけば)リストの要素を管理しなくてよい
  • text入力を自前で監視しなくてよい

と、いいこと(簡単なこと)が多かったです
RxSwiftは奥が深いのでまだまだこれからやっていこうと思います

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3