2017-04-15 30 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