UISearchController を Rx で書けるように
iOS11 だと UISearchController
を UINavigationBar
に入れられて楽しいので、入力した文字列をうまい感じで Observable<String>
にしてくれる DelegateProxy
を作ってみた。
UISearchResultUpdating+Rx の私的実装
なんで RxCocoa にないの?
PR 自体は何度か出てたけど蹴られてたっぽい。
- Creating UISearchResultsUpdating proxy
- create UISearchResultsUpdating proxy
- create UISearchResultsUpdating proxy
./scripts/all-tests.sh
通してないなこの PR …。
ちゃんと RxSwift の作法っぽい感じで書き直したらこんな感じです。
これはちゃんと ./scripts/all-tests.sh
も通ります。
よほどメジャーな delegate じゃない限り、この手のラッパーは各自で作ってねってスタンスだと思うので本家に PR は出さないですが。
UISearchController の使い方が分からない場合
ググってコードを探すよりも、Table Search with UISearchController というサンプルコードを見ると早いです。
なんと、Swift 版があるだけでなく、iOS11 対応済。
hidesSearchBarWhenScrolling が動作しない
UIScrollView
と連動して動作してくれる条件は、以下の通り。
- UIViewController の view が UIScrollView
- UIViewController の view の subviews[0] が UIScrollView
Shrink large title when scrolling (not UITableViewController) iOS 11 に図解入りの超分かりやすい説明があります。
作った経緯とか
最初、UISearchResultsUpdating
に準拠しなくても、searchController.searchBar.rx.value
を subscribe すればいいんじゃ?と思ってたんですけれど、これだとうまくいかなかった。
- キャンセル時に next が流れて来ない
- 検索中に別の VC に push すると、ナビゲーションバーが消え去る
後者について、UISearchController
が内部的に modal を出してるっぽいので、これを消すワークグラウンドとかを思いついたのですけれど、正規の方法で実装した方がよさそうだなぁということで素直に実装しましたとさ。
ついでだったので、SearchBar に入力したテキストを取れる Observable を追加したのですけれど、ちゃんと動いてるっぽい。
var queryText: Observable<String> { return Observable.merge(Observable.just(""), RxSearchResultUpdatingProxy.proxy(for: base).didUpdateSearchResultSubject.map { $0.searchBar.text ?? "" }) .distinctUntilChanged() }
空文字と merge しているのは、combineLatest でフィルタリングしている実装の都合上です。