在谈架构之前,其实苹果是建议我们开启用户的
1. MVC
这个比较简单
预期:
现实:
view 和 model 还是 没有解耦;
- view事件 (比如点击 + )
- 响应view的事件(data页面显示数字+1)
- 业务逻辑,网络请求 =》 影响UI 也有不影响UI的
- 影响UI就需要抽离出来
2. MVP
V: View + controller;
P: View的 一个 处理 和 响应的 管理者,所以XCTest的时候就不需要View的逻辑,可以通过Present来模拟;
交互: V会持有P, P 持有V的弱引用
import UIKit
// MVP
// data
struct Person { // Model
let firstName: String
let lastName: String
}
// view 的协议
protocol GreetingView : AnyObject {
func setGreeting(greeting: String)
}
// present 持有view的协议
protocol GreetingViewPresenter {
init(view: GreetingView, person: Person)
func showGreeting()
}
// 实例化prsent
class GreetingPresenter: GreetingViewPresenter {
// P 弱持有 V
weak var view: GreetingView!
let person: Person
required init(view: GreetingView, person: Person) {
self.view = view
self.person = person
}
func showGreeting() {
// 处理逻辑事件
let greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
// 处理好了回传给view
self.view.setGreeting(greeting: greeting)
}
}
// VC实现 Present的协议
class MVPGreetingViewController: UIViewController, GreetingView {
// V 持有 P
var presenter: GreetingViewPresenter!
let showGreetingButton = UIButton()
let greetingLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
self.setupUI()
self.showGreetingButton.addTarget(self, action: #selector(didTapButton(_:)), for: .touchUpInside)
}
func setGreeting(greeting: String) {
self.greetingLabel.text = greeting
}
@objc func didTapButton(_ btn: UIButton) -> Void {
self.presenter.showGreeting()
}
// layout code go here
func setupUI() {
self.view.addSubview(self.showGreetingButton)
self.view.addSubview(self.greetingLabel)
self.showGreetingButton.frame = CGRect.init(x: 10, y: 100, width: 100, height: 50);
self.showGreetingButton.backgroundColor = UIColor.blue
self.greetingLabel.frame = CGRect.init(x: 20, y: 250, width: 200, height: 60);
self.greetingLabel.backgroundColor = UIColor.gray
}
}
调用过程:
let model = Person(firstName: "David", lastName: "Blaine")
let vc = MVPGreetingViewController()
let presenter = GreetingPresenter(view: vc, person: model)
vc.presenter = presenter
self.navigationController?.pushViewController(vc, animated: true)
3. VIPER
V: View + Controller
I: Interactor (逻辑业务处理者)
P: Present
E: model
R: 路由
暂时不考虑路由,我们可以看到 和 MVP 相比,多了一个 Interactor,其实就是 防止Present 过于庞大,而把以前在Present 处理的业务逻辑 细分到 不同的 Interactor 里面;
import UIKit
struct Person2 { // 实体 Model
let firstName: String
let lastName: String
}
struct GreetingData { // 传递数据结构(不是实体)
let greeting: String
let subject: String
}
/**
Interactor
*/
protocol GreetingProvider {
func provideGreetingData()
}
class GreetingInteractor: GreetingProvider {
weak var output: GreetingOutput!
func provideGreetingData() {
let person = Person2(firstName: "David", lastName: "Blaine")// 通常来自于数据访问层
let subject = person.firstName + " " + person.lastName
let greeting = GreetingData(greeting: "Hello", subject: subject)
self.output.receiveGreetingData(greetingData:greeting)
}
}
protocol GreetingView2EventHandler {
func didTapShowGreetingButton()
}
protocol GreetingOutput: AnyObject {
func receiveGreetingData(greetingData: GreetingData)
}
/**
present 还是直接和V 交互,只是抽离了 业务逻辑
分类出
【UI点击 的 业务逻辑】
【更新UI的业务】
*/
class GreetingPresenter2: GreetingView2EventHandler, GreetingOutput {
weak var view: GreetingView2!
// P 只有 Interactor , 在V 中就可以 拿着 Interactor 去调用业务
var greetingProvider: GreetingProvider!
// UI 点击 =》 业务逻辑(抽离出 Interactor)
func didTapShowGreetingButton() {
// Interactor(解析数据) => output (更新UI)
self.greetingProvider.provideGreetingData()
}
//UI 更新 ,上面的 Interactor 处理完了 =》 更新 UI (更新present)
func receiveGreetingData(greetingData: GreetingData) {
let greeting = greetingData.greeting + " " + greetingData.subject
self.view.setGreeting(greeting: greeting)
}
}
// V 暴露给外部的回调事件;
protocol GreetingView2: AnyObject {
func setGreeting(greeting: String)
}
class VIPERGreetingViewController: UIViewController, GreetingView2 {
var eventHandler: GreetingView2EventHandler!
let showGreetingButton = UIButton()
let greetingLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
self.showGreetingButton.addTarget(self, action: #selector(didTapButton(_:)), for: .touchUpInside)
}
@objc func didTapButton(_ button: UIButton) {
// 通过Interactor 去触发业务逻辑
// 和MVP 使用就不用直接使用P 去触发逻辑
self.eventHandler.didTapShowGreetingButton()
}
func setGreeting(greeting: String) {
self.greetingLabel.text = greeting
}
// layout code go here
func setupUI() {
self.view.addSubview(self.showGreetingButton)
self.view.addSubview(self.greetingLabel)
self.showGreetingButton.frame = CGRect.init(x: 10, y: 100, width: 100, height: 50);
self.showGreetingButton.backgroundColor = UIColor.blue
self.greetingLabel.frame = CGRect.init(x: 20, y: 250, width: 200, height: 60);
self.greetingLabel.backgroundColor = UIColor.gray
}
}
外界调用:
let vc = VIPERGreetingViewController()
let presenter = GreetingPresenter2()
let interactor = GreetingInteractor()
interactor.output = presenter //weak , 需要回调 业务逻辑 给P
presenter.greetingProvider = interactor
presenter.view = vc;
vc.eventHandler = presenter
self.navigationController?.pushViewController(vc, animated: true)
4. MVVM
和 MVP 很相似;
但是不一样的是,viewModel 想达到 model的改变 => View ; View 的改变马上影响model,并且 view 和 model 不耦合在一起;
所以就有个 bind的概念;
类似vue 或者小程序 里面一样的;
- view 点击事件 (比如 让viewModel 当做target) =》 事件就传给了 viewModel =》 viewModel,greeting = "xxxx" 去更改 数据
||
V - viewModel.greeting (属性改变) =》 触发 view的改变 (UI 展示);