0
1

More than 1 year has passed since last update.

【iOS × MVC】MVC アーキテクチャとは【コードあり コピペでOK】

Posted at

目的

iOS開発における、MVCアーキテクチャを理解すること
筆者の備忘録

流れ

  1. MVCの誕生
  2. MVCの目的
  3. 各レイヤーの役割
  4. MVCの課題
  5. (補足)Presentation Modelパターン
  6. 画像表示アプリで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のModelViewControllerについて、それぞれ役割の説明をしていく

レイヤー 役割
Model コマンドを受けて処理を行い、変数を更新
View Modelの変更を監視し、画面の更新
Controller ユーザーの入力を受けて、Modelにコマンドを送る


↑[1]より参考

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の状態管理を担わせる。


↑[1]より参考

↑[1]より参考

Presentation Modelパターン のModelViewPresentation ModelControllerについて、それぞれ役割を以下の表に示す。また、ここでは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

Model.swift
import Foundation
import UIKit

// Viewで使用する変数の定義
// 【流れ】
// ①: ViewController.swift(Controller) からの命令で変数の値が更新されていく
// ②: 変数が更新されると View が更新される
struct Model {
    let image: UIImage!
    let imageName: String!
}

View

Cell.swift
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

ViewController.swift
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

0
1
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
0
1