Set UIViewController view property to custom UIView class without storyboard / tip

I have a UIViewController called LoginViewController . I want to create a view of this LoginViewController completely programmatically in a custom UIView class called LoginView instead of creating all the elements in my LoginViewController . This way I prevent the "View" code in the Controller (MVC) class.

In the code below, I set the view of my LoginViewController to my LoginView , which for simplicity only contains 2 UILabels

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

The LoginView class initializes both labels and should set some restrictions.

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

This does not work, since the viewing restrictions are 0. However, I could not find any resource that gives an idea of ​​whether this is possible, so I tried my approach, which did not work.

How do you set the UIViewController view to a custom UIView that is done programmatically? Or the snippet recommended above?

This is a working solution based on Jadar's answer:

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

Result:

Result

+5
source share
3 answers

There seem to be two questions here. One of them is the best way to programmatically configure the ViewController. Another is how to set up the program view.

First, the best way to have a ViewController programmatically using another subclass of UIView is to initialize and assign it in the loadView method. Per Apple docs :

You can override this method to manually create your views. If you decide to do this, assign the view property to the root view of your view hierarchy. The views you create must be unique instances and should not be used in conjunction with any other view controller object. Your custom implementation of this method should not be called super.

It will look something like this:

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

This way, you don’t have to deal with its calibration, since the view controller itself needs to take care of this (as its own UIView does).

Remember, do not call super.loadView() , or the controller will be confused. Also, the first time I tried this, I got a black screen because I forgot to call window.makeKeyAndVisible() in the App Delegate app. In this case, the view was never added to the window hierarchy. You can always use the preview introspector button in Xcode to find out what happens.

Secondly, you will need to call self.addSubview(_:) in your UIView subclass to display them. When you add them as subzones, you can add constraints using 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])) } 

For more information about the visual format language used to create constraints, see the VFL Guide

+3
source

Override the layoutSubviews method to update subzone frames inside your custom view.

And never call super.loadView() . This is described for the loadView method.

+2
source

You should load the custom view when the limitations of the LoginViewController layout are already loaded, try the following:

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

Source: https://habr.com/ru/post/1262109/


All Articles