ダウンロード サンプルファイル (613.5 KB)
サンプルの各クラスを作成する(2)
Viewの作成
今回のサンプルでは、作成するクラスがビューコントローラーとコレクションのセルの2つあります、先にビューコントローラーから作成します。次のようにUIとViewModelのインスタンスを定義します。
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField! // 入力欄
@IBOutlet weak var searchButton: UIButton! // 検索ボタン
@IBOutlet weak var collectionView: UICollectionView! // コレクション
@IBOutlet weak var indicatorView: UIActivityIndicatorView! // インジゲーター
private let viewModel = SearchViewModel() // ViewModel
private let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
self.bind()
# 略
SearchViewModelクラスの各プロパティとは、bind()メソッドの中で行います。
func bind() {
// 入力欄の値とViewModelのinputs.searchWordをbind
self.textField.rx.text.orEmpty
.bind(to: self.viewModel.inputs.searchWord).disposed(by: self.disposeBag)
// 検索ボタンのタップでViewModelのinputs.searchTriggerを起動
self.searchButton.rx.tap.asDriver()
.drive(self.viewModel.searchTrigger).disposed(by: self.disposeBag)
// ViewModelのoutputs.isSearchButtonEnabledと検索ボタンの押下可否をbind
self.viewModel.outputs.isSearchButtonEnabled.asObservable() // ------(1)
.bind(to: self.searchButton.rx.isEnabled).disposed(by: self.disposeBag)
// ViewModelのoutputs.itemsをコレクションビューにbind
self.viewModel.outputs.items.asObservable()
.bind(to: self.collectionView.rx.items(cellIdentifier: "ImageItemCell", cellType: ImageItemCell.self)) {
(index, element, cell) in
cell.configure(URL(string: element.src)!) // ------(4)セル内で画像を表示
}.disposed(by: self.disposeBag)
// ViewModelのoutputs.isLoadingとインジゲーターのisAnimatingをbind
self.indicatorView.hidesWhenStopped = true // インジゲーターは回っていない時は非表示に
self.viewModel.outputs.isLoading // ------(2)
.bind(to: self.indicatorView.rx.isAnimating).disposed(by: self.disposeBag)
// エラーがあれはアラートで内容を表示
self.viewModel.outputs.error.subscribe(onNext: { [weak self] error in // ------(3)
let alert = UIAlertController(title: "エラー", message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
})disposed(by: self.disposeBag)
}
SearchViewModelクラスの各プロパティは、inputs/outputsのプレフィックスがつくのでわかりやすいです。データバインドを行う際、inputsの場合はViewのUIの後に、outputsの場合はUIより先に、位置します。連載第4回のサンプルと処理がかぶるものは説明を割愛します。
outputs.isSearchButtonEnabledプロパティは検索ボタンのrx.isEnabledプロパティとバインドします(1)。こうすることで、ViewModelの処理からUIの動きを制御できます。同様に、outputs.isLoadingとインジゲーターのisAnimatingプロパティを結合して、Actionクラスの処理中にインジゲーターが回るようにできます(2)。outputs.errorプロパティを監視しておくことで、エラーが発生した場合の処理も他の処理と同様に定義できます(3)。また、Actionクラスを利用することで、処理中/エラー発生時の処理も他のプロパティと同様に行うことができます。
コレクションビューのセルでは、(4)の通りconfigure(_ :)メソッドで画像を表示できるようにします。
import UIKit
import AlamofireImage
class ImageItemCell: UICollectionViewCell {
@IBOutlet weak var imageView: UIImageView!
func configure(_ url: URL) {
self.imageView.af_setImage(withURL: url)
}
}
画像を表示する際には、AlamofireImageライブラリのaf_setImage(withURL:)メソッドで行います。UIImageViewの拡張メソッドで、画像のURLを渡すと非同期に画像を取得して表示できます。
サンプルを実行すると次のように動作を確認できます。
まとめ
連載最終回では、RxSwift+MVVMをよりわかりやすく、より短い処理で機能を実装できるライブラリを利用しました。RxSwiftもMVVMモデルも、アプリ開発の効率をあげる点では非常に優秀です。ですが、両方とも導入しただけではまだ不十分なこともあります。RxSwift Communityなどのライブラリ開発コミュニティでは、既存ライブラリの不足する機能を補うための開発が活発に行われています。こういった派生ライブラリを取り入れることで、より良いアプリ開発を行うこともできます。みなさんもぜひ利用してみてください。
この記事は参考になりましたか?
既に会員の方はを行ってください。
- RxSwiftで一歩進んだiOSアプリ開発連載記事一覧
-
- MVVMモデルをもっと便利に使ってみよう(後編)~ViewModelの変数と具体的な処理方...
- MVVMモデルをもっと便利に使ってみよう(前編)~入力/出力の変数に注目したMVVMモデル...
- RxSwiftの仕組みを利用して、MVVMモデルを導入しよう
- この記事の著者
-
WINGSプロジェクト 片渕 彼富(カタフチ カノトミ)
<WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書、記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
-
山田 祥寛(ヤマダ ヨシヒロ)
静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数。
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
この記事は参考になりましたか?
この記事をシェア
