目的
iOS開発における、MVCアーキテクチャを理解すること
筆者の備忘録
流れ
- MVCの誕生
- MVCの目的
- 各レイヤーの役割
- MVCの課題
- (補足)Presentation Modelパターン
- 画像表示アプリでMVCの実装 ← コードつき, コピペでOK
1. MVCの誕生
ここではMVCが誕生するまでの流れを説明していきます。
コード(:ViewControllerのようなもの) を ウィジェット(:ボタンやテーブル等のUIコンポーネント) に全て記載していた
↓
メンタルモデル
と違うものになってしまう
↓
1979年にUIの表示をEditor、メンタルモデルをModelと名付けて、責務を分けた。
↓
その後、Editorを「入力に属するもの(Controller)」「出力に属するもの(View)」に分けた。
↓
View-Controller-Model:MVC
の誕生
補足
メンタルモデル
とは
人間が無自覚のうちに持っている、思い込みや価値観
(例:画面は画面のプログラム、処理は処理のプログラムがあるはずだ)
2. MVCの目的
開発時に、ドメインとプレゼンテーションを分けて考えること
→ アーキテクチャ全般に言えること
補足
ドメイン =Model
システム本来の関心領域
(例:足し算の処理)
プレゼンテーション =View
UIに関係するもの
(例:足し算の結果を画面に描画)
3. 各レイヤーの役割
ここでは、MVCのModel
View
Controller
について、それぞれ役割の説明をしていく
レイヤー | 役割 |
---|---|
Model | コマンドを受けて処理を行い、変数を更新 |
View | Modelの変更を監視し、画面の更新 |
Controller | ユーザーの入力を受けて、Modelにコマンドを送る |
ControllerとViewによって構成されるウィジェットが、保持するModelにコマンドを送り、その変更をウィジェットに反映する
というもの。
Modelは、複数のViewやController 共有される。
4. MVCの課題
MVCモデルの課題としては以下の3点が挙げられます。
① プレゼンテーションロジックの表現ができない
年齢を表すModelを考える。あるViewでは「未成年なら赤文字、青年なら青文字」という処理が行われる。また、別のViewでは「還暦以上なら緑文字、還暦未満なら黄色文字」という処理。この「年齢で文字色を変更する という処理」はどこで表現すれば良いのか?
② プレゼンテーション(View)状態を保持できない
「表の選択状態」や「全てのフォームを埋めることで変化するボタン」等の、Viewから得られた情報の管理。
③ テストが難しい
課題の解決策
上で挙げた課題に対して 以下のような解決策 が考えられます。
この3つの解決策の中でも、Presentation Model
は、良い意味で大きな影響を与えました。
継承
プレゼンテーションロジックごとに新しいウィジェットのサブクラスを作る
Pluggable Views
様々な画面を詰め込んだViewクラスを用意して、そこに大量の引数を与えて表示する画面の振る舞いを変える
Presentation Model
View と Model の間にViewの状態を管理する層を追加する
(補足)5. Presentation Modelパターン
Presentation Modelパターン は、 MVC は異なるアーキテクチャである。
ViewとModelの間に Presentation Model というレイヤーを置く。そこに プレゼンテーションロジックやUIの状態管理
を担わせる。
Presentation Modelパターン のModel
View
Presentation Model
Controller
について、それぞれ役割を以下の表に示す。また、ここではMVCでの役割との比較も行えるよう表を作成した。
レイヤー | Presentation Modelパターン | MVC |
---|---|---|
Model | MVCと同様 | コマンドを受けて処理を行い、変数を更新 |
View | Presentation Modelの変更を監視 | Modelの変更を監視し、画面の更新 |
Presentation Model | Modelにコマンドを送る・Modelの変更を監視・プレゼンテーションロジックやUIの状態を管理 | - |
Controller | Presentation Modelにコマンドを送る | ユーザーの入力を受けて、Modelにコマンドを送る |
6. 画像表示アプリでMVCの実装
swift で、画像表示アプリを作成しながら、 MVC の説明を行います。
作成する画像表示アプリ
準備
■ ファイル構成
Modle
:Model.swift
View
:Cell.swift
Controller
:ViewController.swift
■ Storyboard
・CollectionViewのCell の identifier をCell
にしておく
プログラム
Model
import Foundation
import UIKit
// Viewで使用する変数の定義
// 【流れ】
// ①: ViewController.swift(Controller) からの命令で変数の値が更新されていく
// ②: 変数が更新されると View が更新される
struct Model {
let image: UIImage!
let imageName: String!
}
View
import UIKit
// 画面(storyboard) で使用する UIコンポーネント を定義
// 【流れ】
// ⓪: ViewController.swift が Data.swift からViewで使用するデータを取得する
// ①: ViewController.swift で image,label(Cell.swift) の値が更新される → View が Controller を監視している状態
// ②: 画面を再描画
class Cell: UICollectionViewCell {
@IBOutlet weak var image: UIImageView!
@IBOutlet weak var label: UILabel!
}
Controller
import UIKit
// Model(Model.swift)で定義した変数を使い、View(Cell.swift)を更新する
// 【流れ】
// ①: Model(Model.swift) で定義した変数を使用して、Data.swift から画像データを取得する
// ②: ①で取得したデータを、View(Cell.swift) へ伝える
class ViewController: UIViewController {
var dataList = [Model]()
override func viewDidLoad() {
super.viewDidLoad()
// Model 型のデータを受け取る
dataList = Data.getAllData()
}
}
// View を生成する, Model 型のデータを使用して
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
// セルの個数
// 設置必須な関数 (CollectionViewを使用する場合)
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataList.count
}
// セルの作成
// 設置必須な関数 (CollectionViewを使用する場合)
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// Cell.swift の UIコンポーネント(image, label)に代入していく
// 画像を表示する際に詰まったこと: https://qiita.com/yoshikoba/items/044925f554b75c53d27c
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
cell.image.image = dataList[indexPath.row].image // image の更新
cell.image.contentMode = .scaleAspectFill
cell.image.clipsToBounds = true
cell.label.text = dataList[indexPath.row].imageName // label の更新
return cell
}
}
参考文献
[1], iOSアプリ設計パターン入門 (電子版: 税込3240円)
[2], Swift4[Part-2] :- Model View Controller(MVC) With Example in iOS Hindi. - YouTube