MVCモデルとは
簡単な例を使ってMVCモデルを説明してみます。
MVCについては以下のページが解り易く説明されています。
以下、「表示する」ボタンをタップすると、定数の値を足し算した値をラベルに表示する簡単なアプリで説明します。
View層
View層はUIに関わる処理をする層です。
今回で言うと、ラベルやボタンを生成、レイアウトし、ボタンが押された時の処理を記述しています。
import Foundation
import UIKit
//controll層とのやり取りをする為のメソッドがセットされたデリゲートプロトコル
protocol CustomViewDelegate: class {
//渡された値を保持するメソッド
func setTotalVal(_ num1:Int, _ num2:Int)
//保持した値の合計値を取得する為のメソッド
func getTotalVal() -> Int
}
class CustomView: UIView{
//合計値を表示するラベル
var textLavel: UILabel!
//合計値を計算し、ラベルに表示させる為のアクションのボタン
var button: UIButton!
//以下の2つの値を計算(足算)
let num1 = 100
let num2 = 200
//上記2つの値の合計値
var totalNum:Int?
//デリゲート
weak var delegate: CustomViewDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
//ラベル生成
textLavel = UILabel()
textLavel!.backgroundColor = .red
self.addSubview(textLavel!)
//ボタン生成
button = UIButton()
button!.setTitle("表示する", for: .normal)
button!.setTitleColor(UIColor.black, for: .normal)
button!.addTarget(self, action: #selector(btTap(_:)),
for: .touchUpInside)
self.addSubview(button!)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//ボタンをタップした時の処理
@objc func btTap(_ sender:UIButton){
//2つの定数値をcontroll層のデリゲートメソッドを介してmodel層に渡します
delegate?.setTotalVal(self.num1, self.num2)
//controll層のデリゲートメソッドを介してmodel層の計算結果を取得します
totalNum = delegate?.getTotalVal()
//取得した計算結果をラベルに表示します。
textLavel?.text = totalNum?.description
}
//レイアウト
override func layoutSubviews() {
super.layoutSubviews()
textLavel!.frame = CGRect(x: bounds.origin.x + 30,
y: bounds.origin.y + 30,
width: bounds.size.width - 60,
height: 50
)
let buttonSize = CGSize(width: 100, height: 50)
button!.frame = CGRect(x: (bounds.size.width - buttonSize.width)/2,
y: textLavel.frame.maxY + 30,
width: buttonSize.width,
height: buttonSize.height
)
}
}
今回は定数num1、num2の値を足し算しますが、通常のアプリだと、ここの定数の値はユーザーがテキストフィールドに入力した値などになるかと思いますが、今回はMVCの簡単な説明なのでこの様な形にしています。
delegateプロパティ
weak var delegate: CustomViewDelegate?
delegateプロパティが循環参照を防ぐ為に、weakキーワードを指定しています。
また、weakキーワードを指定した場合は型のCustomViewDelegateプロトコルにclassプロトコルを継承し、準拠する型が参照型(クラス)であることを想定します。
CustomViewDelegate
定数の値をModel層に渡したり、Model層の計算結果をView層に渡したりのやり取りはControll層が行うのでその処理をデリゲートとして記述します。
protocol CustomViewDelegate: class {
//渡された値を保持するメソッド
func setTotalVal(_ num1:Int, _ num2:Int)
//保持した値の合計値を取得する為のメソッド
func getTotalVal() -> Int
}
委譲先はViewControllerクラスで、委譲元はCustomViewクラスなのでプロトコルはCustomView.swiftに記述します。
Controll層
Controller層はModel層とView層の仲介(値の受け渡し)に関する処理をする層です。
今回で言うと、View層の2つの定数の値をModel層に渡し、Model層の計算結果(2つの定数値の加算)をView層に渡します。
import UIKit
class ViewController: UIViewController {
//view層とmodel層
//この2つの層の仲介役をする
var customView: CustomView!
var customModel = CustomModel()
override func viewDidLoad() {
super.viewDidLoad()
customView = CustomView()
//view層からの委譲先を自らで指定
customView.delegate = self
view.addSubview(customView)
}
//レイアウト
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
customView.frame = CGRect(x: view.safeAreaInsets.left,
y: view.safeAreaInsets.top,
width: view.frame.size.width - view.safeAreaInsets.left - view.safeAreaInsets.right,
height: view.frame.size.height - view.safeAreaInsets.bottom
)
}
}
//view層のデリゲートプロトコルを準拠します。
//以下の2つのメソッドでview層とmodel層の仲介をする
extension ViewController: CustomViewDelegate{
//view層から渡された定数の値をmodel層に渡す
func setTotalVal(_ num1: Int, _ num2: Int){
customModel.set(num1, num2)
}
//model層からの値(定数の加算値)を受け取る
func getTotalVal() -> Int{
let totalVal = customModel.get()
return totalVal
}
}
Model層
Model層はデータを処理したりなど、データを扱う層です。
View層のデータ(ユーザーが入力した値等)をControll層を介して取得します。
今回で言うと、view層の2つの定数の値をcontroll層を介して受け取りプロパティにセット。
受け取った値を加算。
class CustomModel{
private var num1:Int?
private var num2:Int?
//view層の定数の値を受け取り、プロパティにセット
func set(_ num1:Int, _ num2:Int){
self.num1 = num1
self.num2 = num2
}
//受け取ったview層の値を加算し返す
func get() -> Int{
let totalVal = self.num1! + self.num2!
return totalVal
}
}
値を受け取ったり(取得したり)、渡したりするのはcontroll層の役目なので、model層では値を保持(セット)したり、計算したりする記述のみ。
今回は簡単なものなので、受け取った値はプロパティにセットしただけですが、
受け取った値を保存したりする場合はuserDefaults等を使用します。