MVVM和MVC模式中的视图创build在哪里?

请原谅,如果其重复的话题。

我通常写我的应用程序没有故事板,并将视图创build到“viewDidLoad”,如:

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模式,只是不知道在哪里把视图创build。 现在我想到了这样的事情:

 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,不确定会不会有一些问题。 还有多丑呢?

也许你应该尝试完全面向对象的path。 一个视图组成看起来像这样:

//可重用的协议集

 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 } } 

//可重用的function(没有uikit依赖)

 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依赖)

 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 } 

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

 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中的用法:

 window?.rootViewController = ScStartScreen().ui 

注意:

  • 它遵循面向对象的编码规则(清晰的编码,优雅的对象,装饰模式,…)
  • 每个class级都非常简单的build造
  • 类通过协议彼此进行通信
  • 所有的依赖关系都是通过dependency injection来尽可能的
  • 一切(除了结束的业务屏幕)是可重用的 – >事实上:可重复使用的代码组合随着你的代码的增长而增长
  • 您的应用程序的业务逻辑集中在Screen对象的实现中
  • unit testing非常简单,当使用协议的虚拟实现(甚至在大多数情况下不需要模拟)
  • 保留周期较less的问题
  • 避免零,零和选项(他们污染你的代码)

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