在MVVM和MVC模式中放置视图创建的位置? [英] Where to put views creation in MVVM and MVC patterns?

查看:132
本文介绍了在MVVM和MVC模式中放置视图创建的位置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请原谅我的重复主题。

我通常在没有故事板的情况下编写我的应用程序,并将视图创建放入viewDidLoad,如:

I usually write my apps without storyboards, and put views creation into "viewDidLoad", like:

class LoginVC: UIViewController {

    var view1: UIView!
    var label1: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        loadStaticViews()
    }

    func loadStaticViews() {
        view1 = UIView()
        label1 = UILabel()
        view.addSubview(view1)
        view1.addSubview(label1)
        // constraints...
    }
}

现在我想在我的下一个尝试MVVM模式应用程序,只是不知道在哪里创建视图。
现在我想到这样的事情:

And now I want to try MVVM pattern in my next app, and just not sure where to put views creation. Now I think about something like that:

class LoginVCViews {
    static func loadViews<T, T1, T2>(superview: UnsafeMutablePointer<T>, view: UnsafeMutablePointer<T1>, label: UnsafeMutablePointer<T2>) {
        guard let superview = superview.pointee as? UIView else { return }
        let v = UIView()
        let l = UILabel()
        superview.addSubview(v)
        v.addSubview(l)

        // constraints...

        view.pointee = v as! T1
        label.pointee = l as! T2
    }
}

class LoginVC: UIViewController {

    private var view1: UIView!
    private var label1: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        LoginVCViews.loadViews(superview: &view, view: &view1, label: &label1)
    }
}

您如何看待?我不熟悉UnsafeMutablePointer并且不确定不会出现一些问题。
它有多丑?

What do you think ? I'm not familiar with UnsafeMutablePointer very well and don't sure there wont be a some problems. And how much it's ugly ?

推荐答案

也许你应该尝试完全面向对象的路径。视图合成看起来像这样:

Maybe you should try the fully object oriented path. A view composition looks something like that:

//可重用协议集

protocol OOString: class {
    var value: String { get }
}

protocol Executable: class {
    func execute()
}

protocol Screen: class {
    var ui: UIViewController { get }
}

protocol ViewRepresentation: class {
    var ui: UIView { get }
}

//可重复使用的功能(没有uikit依赖)

// reusable functionality (no uikit dependency)

final class ConstString: OOString {

    init(_ value: String) {
        self.value = value
    }

    let value: String

}

final class ExDoNothing: Executable {

    func execute() { /* do nothing */ }

}

final class ExObjCCompatibility: NSObject, Executable {

    init(decorated: Executable) {
        self.decorated = decorated
    }

    func execute() {
        decorated.execute()
    }

    private let decorated: Executable

}

//可重复使用的UI(uikit依赖)

// reusable UI (uikit dependency)

final class VrLabel: ViewRepresentation {

    init(text: OOString) {
        self.text = text
    }

    var ui: UIView {
        get {
            let label = UILabel()
            label.text = text.value
            label.textColor = UIColor.blue
            return label
        }
    }

    private let text: OOString

}

final class VrButton: ViewRepresentation {

    init(text: OOString, action: Executable) {
        self.text = text
        self.action = ExObjCCompatibility(decorated: action)
    }

    var ui: UIView {
        get {
            let button = UIButton()
            button.setTitle(text.value, for: .normal)
            button.addTarget(action, action: #selector(ExObjCCompatibility.execute), for: .touchUpInside)
            return button
        }
    }

    private let text: OOString
    private let action: ExObjCCompatibility

}

final class VrComposedView: ViewRepresentation {

    init(first: ViewRepresentation, second: ViewRepresentation) {
        self.first = first
        self.second = second
    }

    var ui: UIView {
        get {
            let view = UIView()
            view.backgroundColor = UIColor.lightGray
            let firstUI = first.ui
            view.addSubview(firstUI)
            firstUI.translatesAutoresizingMaskIntoConstraints = false
            firstUI.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
            firstUI.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
            firstUI.widthAnchor.constraint(equalToConstant: 100).isActive = true
            firstUI.heightAnchor.constraint(equalToConstant: 40).isActive = true
            let secondUI = second.ui
            view.addSubview(secondUI)
            secondUI.translatesAutoresizingMaskIntoConstraints = false
            secondUI.topAnchor.constraint(equalTo: firstUI.topAnchor).isActive = true
            secondUI.leadingAnchor.constraint(equalTo: firstUI.trailingAnchor, constant: 20).isActive = true
            secondUI.widthAnchor.constraint(equalToConstant: 80).isActive = true
            secondUI.heightAnchor.constraint(equalToConstant: 40).isActive = true
            return view
        }
    }

    private let first: ViewRepresentation
    private let second: ViewRepresentation

}

//视图控制器

final class ContentViewController: UIViewController {

    convenience override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)   {
        self.init()
    }

    convenience required init(coder aDecoder: NSCoder) {
        self.init()
    }

    convenience init() {
        fatalError("Not supported!")
    }

    init(content: ViewRepresentation) {
        self.content = content
        super.init(nibName: nil, bundle: nil)
    }

    override func loadView() {
        view = content.ui
    }

    private let content: ViewRepresentation

}

//现在屏幕的业务逻辑(不可重复使用)

// and now the business logic of a screen (not reusable)

final class ScStartScreen: Screen {

    var ui: UIViewController {
        get {
            return ContentViewController(
                content: VrComposedView(
                    first: VrLabel(
                        text: ConstString("Please tap:")
                    ),
                    second: VrButton(
                        text: ConstString("OK"),
                        action: ExDoNothing()
                    )
                )
            )
        }
    }

}

AppDelegate中的用法:

Usage in AppDelegate:

window?.rootViewController = ScStartScreen().ui

注:


  • 它遵循面向对象编码的规则(干净的编码,优雅的对象,装饰模式......)

  • 每个类都构造得非常简单

  • 类通过协议相互通信

  • 尽可能通过依赖注入给出所有依赖项

  • 所有内容(最后的业务屏幕除外)是可重用 - >实际上:可重用代码的组合随着你每天的代码而增长

  • 您的应用程序的业务逻辑集中在Screen对象的实现中
  • 使用伪实现协议时单元测试非常简单(在大多数情况下甚至不需要模拟)

  • 保留周期的问题较少

  • 避免Null,nil和Optionals(他们污染您的代码)

  • ...

  • it follows the rules of object oriented coding (clean coding, elegant objects, decorator pattern, ...)
  • every class is very simple constructed
  • classes communicate by protocols with each other
  • all dependencies are given by dependency injection as far as possible
  • everything (except the business screen at end) is reusable -> in fact: the portfolio of reusable code grows with every day you code
  • the business logic of your app is concentrated in implementations of Screen objects
  • unittesting is very simple when using fake implementations for the protocols (even mocking is not needed in most cases)
  • lesser problems with retain cycles
  • avoiding Null, nil and Optionals (they pollute your code)
  • ...

在我看来,这是最好的编码方式,但是大多数人都没有这样做。

In my opinion it's the best way to code, but most people don't do it like that.

这篇关于在MVVM和MVC模式中放置视图创建的位置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆