2017-04-15 52 views
1

请原谅,如果它的反复话题。将MVVM和MVC模式中的视图创建置于何处?

我平时写我的应用程序没有故事板,并把意见创建成“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模式,只是不知道放在哪里视图创作。 现在我想起类似的东西:

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不太熟悉,不确定会不会有一些问题。 它有多丑?

+0

我认为这个实现是正确的,因为UI的任何更新都必须根据MVVM在View部分下完成。我猜想需要添加哪些视图的业务逻辑可以在View-Model –

回答

3

也许你应该尝试完全面向对象的路径。视图组合物看上去类似的东西:

//可重复使用的协议设置

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

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 

注:

  • 它遵循的规则面向对象编码(清洁编码,优雅对象,装饰图案,...)
  • 每一类非常简单的构造
  • 类的协议进行通信相互
  • 所有依赖由依赖注入尽可能
  • 一切(除了在文末商家屏幕)给出的是可重复使用 - >其实:可重用代码的产品组合,每天的增长你的代码
  • 您的应用程序的业务逻辑集中在屏幕的实施对象使用伪造实现的协议(甚至嘲讽不需要在大多数时候
  • 单元测试很简单案件)
  • 较小的问题,保留周期
  • 避免零,零和选配(它们污染你的代码)
  • ...

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

+0

中决定,有趣的是,在哪里放置TableView Delegate/DataSource? –

+0

如果需要通过VrComposedView方法与一些视图元素交互? –

+0

UITableView具有派生自ViewRepresentation的等价类,例如SimpleTableViewRepresentation。这个类在init中依赖注入获取数据源和委托对象(两个协议实现都是自己的对象),并在构建UITableView时将它们设置为get实现。委托和数据源的实现再次非常简单,并再次从ScStartScreen实现中获得它们的依赖关系。注意:TableView通常具有更复杂的业务规则,所以当ScStartScreen产生超过100行代码时不要感到震惊。 – ObjectAlchemist