簡體   English   中英

將UIViewController視圖屬性設置為不帶故事板/筆尖的自定義UIView類

[英]Set UIViewController view property to custom UIView class without storyboard / nib

我有一個名為LoginViewControllerUIViewController 我想在一個名為LoginView的自定義UIView類中以編程方式完全構建LoginViewController的視圖,而不是在我的LoginViewController中構建所有元素。 這樣,我就可以防止Controller類(MVC)中的“查看”代碼。

在下面的代碼中,我將LoginViewController的視圖設置為LoginView ,為簡單起見,該視圖僅包含2個UILabels

class LoginViewController: UIViewController {

override func loadView() {
    super.loadView()

    self.view = LoginView(frame: CGRect.zero)
}

LoginView類初始化兩個標簽,並應設置一些約束。

class LoginView: UIView {

var usernameLabel: UILabel!
var passwordLabel: UILabel!


override init (frame : CGRect) {
    super.init(frame : frame)

    setupLabels()
}

convenience init () {
    self.init(frame:CGRect.zero)
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

private func setupLabels(){
    //Init labels and set a simple text
    self.usernameLabel = UILabel()
    self.usernameLabel.text = "Username"
    self.passwordLabel = UILabel()
    self.passwordLabel.text = "Password"

    //Set constraints which aren't possible since there is no contentView, perhaps using the frame?
    }
}

由於視圖的邊界為0,因此該方法不起作用。但是,我找不到任何資源可以洞悉是否可行,因此我嘗試了無效的方法。

如何將UIViewController的視圖設置為以編程方式制作的自定義UIView? 還是推薦以上代碼段?

這是基於Jadar的答案的有效解決方案:

 class LoginViewController: UIViewController { override func loadView() { view = LoginView() } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } } class LoginView: UIView { var usernameLabel: UILabel! var passwordLabel: UILabel! override init(frame: CGRect) { super.init(frame: frame) self.usernameLabel = UILabel() self.usernameLabel.text = "Username" self.passwordLabel = UILabel() self.passwordLabel.text = "Password" addSubview(usernameLabel) addSubview(passwordLabel) if let superview = usernameLabel.superview{ //Setting AutoLayout using SnapKit framework usernameLabel.snp.makeConstraints { (make) in make.center.equalTo(superview) } } } 

結果:

結果

看起來這里確實有兩個問題。 第一,以編程方式設置ViewController的最佳方法是什么。 另外,如何以編程方式設置視圖。

首先,讓ViewController以編程方式使用其他UIView子類的最佳方法是在loadView方法中對其進行初始化和分配。 根據Apple的文檔

您可以覆蓋此方法,以便手動創建視圖。 如果選擇這樣做,請將視圖層次結構的根視圖分配給view屬性。 您創建的視圖應該是唯一的實例,並且不應與任何其他視圖控制器對象共享。 您對此方法的自定義實現不應調用super。

看起來像這樣:

class LoginViewController: UIViewController {    
    override func loadView() {
        // Do not call super!
        view = LoginView()
    }
}

這樣,您就不必處理它的大小,因為View Controller本身應該照顧它(就像它自己的UIView一樣)。

請記住,不要調用super.loadView()否則控制器會感到困惑。 另外,第一次嘗試此操作時,我出現黑屏,因為我忘記在我的應用程序委托中調用window.makeKeyAndVisible() 在這種情況下,視圖甚至從未添加到窗口層次結構中。 您始終可以使用Xcode中的view introspecter按鈕查看發生了什么。

其次,您需要在UIView子類中調用self.addSubview(_:)才能使它們出現。 將它們添加為子視圖后,可以使用NSLayoutConstraint添加約束。

private func setupLabels(){
    // Initialize labels and set their text
    usernameLabel = UILabel()
    usernameLabel.text = "Username"
    usernameLabel.translatesAutoresizingMaskIntoConstraints = false // Necessary because this view wasn't instantiated by IB
    addSubview(usernameLabel)

    passwordLabel = UILabel()
    passwordLabel.text = "Password"
    passwordLabel.translatesAutoresizingMaskIntoConstraints = false // Necessary because this view wasn't instantiated by IB
    addSubview(passwordLabel)

    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|-10-[view]", options: [], metrics: nil, views: ["view":usernameLabel]))
    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|-20-[view]", options: [], metrics: nil, views: ["view":passwordLabel]))

    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-10-[view]", options: [], metrics: nil, views: ["view":usernameLabel]))
    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-20-[view]", options: [], metrics: nil, views: ["view":passwordLabel]))
}

有關用於創建約束的視覺格式語言的更多信息,請參見《 VFL指南》。

重寫layoutSubviews方法以更新自定義視圖內子視圖的框架。

而且永遠不要調用super.loadView() 記錄了loadView方法。

您應該在已經加載LoginViewController的布局約束時加載自定義視圖,請嘗試以下操作:

  override func viewDidLoad() {
      super.viewDidLoad()
      let newView = LoginView(frame: view.bounds)
      view.addSubview(newView)
    }

在Viewcontroller的loadView方法中執行以下操作:

class LoginViewController: UIViewController {
    override func loadView() {
        super.view = LoginView()
    }
}

在UIView的自定義類中執行以下操作:

class LoginView: UIView {
    convenience init() {
        self.init(frame: UIScreen.main.bounds)
        setupLabels()
    }
}

現在,您的UIView具有一個frame,您可以通過提供框架來通過代碼設置所有視圖。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM