1
\$\begingroup\$

I am generating View and setting constraints programmatically in UIViewController

import UIKit
import SnapKit

class LoginViewController: UIViewController {
    lazy var topImageView = UIImageView()
    lazy var centerStackView = UIStackView()
    lazy var phoneTextField = UITextField()
    lazy var sendOTPButton = UIButton()
    lazy var bottomStackView = UIStackView()
    lazy var separatorLine = UILabel()
    lazy var signUpButton = UIButton()

    override func viewDidLoad() {
        super.viewDidLoad()
        makeUI()
    }

    private func makeUI() {
        self.view.backgroundColor = .white
        self.view.addSubview(topImageView)
        topImageView.backgroundColor = UIColor.magenta.withAlphaComponent(0.4)
        topImageView.snp.makeConstraints {(make) -> Void in
            make.width.equalToSuperview()
            make.height.equalTo(225)
            make.topMargin.equalToSuperview()
            make.centerX.equalToSuperview()
        }

        centerStackView.translatesAutoresizingMaskIntoConstraints = false
        centerStackView.axis = .vertical
        centerStackView.distribution = .fillEqually
        self.view.addSubview(phoneTextField)
        self.view.addSubview(sendOTPButton)
        self.view.addSubview(centerStackView)
        centerStackView.addArrangedSubview(phoneTextField)
        centerStackView.addArrangedSubview(sendOTPButton)
        phoneTextField.backgroundColor = UIColor.lightGray.withAlphaComponent(0.2)
        phoneTextField.delegate = self
        sendOTPButton.setTitle("Send OTP", for: .normal)
        sendOTPButton.addTarget(self, action: #selector(generateAccessToken), for: .touchUpInside)
        sendOTPButton.backgroundColor = .blue

        centerStackView.snp.makeConstraints { (make) in
            make.center.equalToSuperview()
            make.width.equalToSuperview()
            make.height.equalTo(100)
        }

        bottomStackView.translatesAutoresizingMaskIntoConstraints = false
        bottomStackView.axis = .vertical
        bottomStackView.distribution = .fillProportionally
        self.view.addSubview(separatorLine)
        self.view.addSubview(signUpButton)
        self.view.addSubview(bottomStackView)
        bottomStackView.addArrangedSubview(separatorLine)
        bottomStackView.addArrangedSubview(signUpButton)
        separatorLine.backgroundColor = .white
        signUpButton.backgroundColor = .orange
        bottomStackView.snp.makeConstraints { (make) in
            make.bottomMargin.equalTo(additionalSafeAreaInsets)
            make.width.equalToSuperview()
            make.height.equalTo(80)
        }
    }

The makeUI functions essentially creates the views, adds them to UIController's view (as a subview) and set constraints on them (AutoLayout). But the ViewController become bulky if more UIViews are added.

My question is:

  • Should I move the UI code to another file (say LoginView.swift)? Or is it recommended to keep the UI code in UIViewController as they are tightly coupled?
\$\endgroup\$

3 Answers 3

4
\$\begingroup\$

You can move setting properties to lazy var declarations eg.

lazy var centerStackView: UIStackView = {
  let stackView = UIStackView()
  stackView.translatesAutoresizingMaskIntoConstraints = false
  stackView.axis = .vertical
  stackView.distribution = .fillEqually
  return stackView
}()

This will divide your code nicely. In my opinion, extracting views to separate files has a sense only if they have a more complex structure and you want to reuse them in other places. What is important is keeping functions short and on point. Also, try to use more empty lines to visually divide logic blocks, eg. adding subview and setting constraints to it from another subview.

\$\endgroup\$
5
  • \$\begingroup\$ Thanks a lot. I have done the first part. For not extracting out the UI code, it makes the View Controller massive. Is that okay? \$\endgroup\$
    – saurabh
    Commented Apr 12, 2020 at 16:56
  • \$\begingroup\$ As long as code is readable and divided, so every element can be easily found and modified, shared properties like magic numbers (common paddings, alphas, margins etc.) are extracted - in my opinion, it's not a problem. The golden rule is to extract logic from ViewControllers, but views and constraints has to be defined somewhere. Very minor suggestion; I would rename makeUI to setupUI ;) \$\endgroup\$
    – Kamajabu
    Commented Apr 12, 2020 at 17:05
  • \$\begingroup\$ Thanks. +1 So what would be the downsides of crating a View file? \$\endgroup\$
    – saurabh
    Commented Apr 12, 2020 at 17:07
  • \$\begingroup\$ Basically YAGNI rule ( en.wikipedia.org/wiki/You_aren%27t_gonna_need_it ) and over fragmentation of the code. When you have all connected parts in one file (as long as they conform to rules above) it's easier to comprehend them. If to get full overview of view you need to go through 5 separate files - it gives more problems than advantages. It's something that you start to notice when reading other people's code. Extraction makes sense when it solves redundancy issues, or like mentioned, part's are re-used in multiple places. \$\endgroup\$
    – Kamajabu
    Commented Apr 12, 2020 at 17:18
  • \$\begingroup\$ Aside of that, I think over half of this code are addSubview and constraints methods which need to be in VC anyway. \$\endgroup\$
    – Kamajabu
    Commented Apr 12, 2020 at 17:18
3
\$\begingroup\$

The other obvious alternative, which you haven’t contemplated here, is to design it right in the storyboard or NIB in Interface Builder, which would eliminate all of this code.

The only view creation related code that I would put in the view controller is those just-in-time adjustments/subviews that cannot be determined at design-time, but rather are dictated by the presence or absence of relevant model data.

Anyway, if you do that, then the view controller can focus on its core responsibilities:

A view controller’s main responsibilities include the following:

  • Updating the contents of the views, usually in response to changes to the underlying data.
  • Responding to user interactions with views.
  • Resizing views and managing the layout of the overall interface.
  • Coordinating with other objects—including other view controllers—in your app.
\$\endgroup\$
2
  • \$\begingroup\$ Hi thanks for this perspective! But we have moved away from Storyboards and creating UI programmatically for various reasons \$\endgroup\$
    – saurabh
    Commented Apr 12, 2020 at 22:46
  • \$\begingroup\$ I gathered as much, but wanted to add this for the sake of future readers. Happy coding. \$\endgroup\$
    – Rob
    Commented Apr 12, 2020 at 22:49
2
\$\begingroup\$

It is good practice to extract the UI as smaller components in smaller classes and use UIViewController only to bring them together. You should try to imagine each component as an independent unity which could be reused across your application in multiple places.

Writing code by UI brings more clarity but it is a matter of person choice and takes time getting used to it.

We have been writing apps by writing UI by code from past few years and we don't regret it. :)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.