How to use Controller and View in iOS

Issue #37

I like to write UI in code, and with Auto Layout, it is an easy task. However that leaves ViewController with a lots of code. One way we can do is to separate V from C in MVC, by using a dedicated view

We can do that with generic, that initialises a view and replace the view, let’s call it root

1
2
3
4
5
6
7
8
9
import UIKit

class BaseController<T: UIView>: UIViewController {
let root = T()

override func loadView() {
view = root
}
}

Now we can have a UIView subclass, like LoginView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
final class LoginView: UIView {
lazy var textField: UITextField = UITextField().then {
$0.textAlignment = .center
$0.borderStyle = .roundedRect
$0.keyboardType = .phonePad
}

lazy var button: UIButton = UIButton().then {
$0.setTitleColor(.black, for: .normal)
$0.backgroundColor = .lightGray
}

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

addSubviews(
textField,
button
)

Constraint.on(
textField.centerXAnchor.constraint(equalTo: textField.superview!.centerXAnchor),
textField.centerYAnchor.constraint(equalTo: textField.superview!.centerYAnchor),
textField.widthAnchor.constraint(equalTo: textField.superview!.widthAnchor, constant: -20),

button.topAnchor.constraint(equalTo: textField.bottomAnchor, constant: 20),
button.centerXAnchor.constraint(equalTo: button.superview!.centerXAnchor),
button.widthAnchor.constraint(equalTo: textField.widthAnchor, multiplier: 0.8),
button.heightAnchor.constraint(equalToConstant: 44)
)
}

required init?(coder aDecoder: NSCoder) {
fatalError()
}
}

And then the LoginController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
final class LoginController: BaseController<LoginView> {
override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = .white

let gr = UITapGestureRecognizer(target: self, action: #selector(viewTapped))
root.addGestureRecognizer(gr)

root.button.setTitle("Login", for: .normal)
root.button.addTarget(self, action: #selector(loginButtonTouched), for: .touchUpInside)
root.button.isEnabled = false
root.button.showsTouchWhenHighlighted = true

root.textField.placeholder = "Phone number"
root.textField.delegate = self
root.textField.text = dependencyContainer.phoneService.prefix
root.textField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
}
}

And this is how we declare the LoginController

1
2
let loginController = LoginController()
navigationController.viewControllers = [loginController]

Comments