Stop Using Storyboard, Start Building UI 100% Programmatically

   Raja Tamil • Apr 10 •

In this iOS 100% programmatically tutorial, I am going start talking about my views on why you should stop using Storyboard and consider building your UI design for your iOS App 100% Programmatically.

After that, I will show you how to build a simple login screen user interface 100% programmatically in 4 steps.

Let’s talk about why I think you need to stop using Storyboard.

I used Storyboard for one of my recent iOS projects because I did not have a lot of experience with building complex iOS apps.

I thought that was the right thing to do for an app.

However, I ran into a few issues working with Storyboard:

1. IBAction/IBOutlet Issues: When I refactored the code by changing the IBOutlet/IBAction variable name after a few days of work, the app crashed for no apparent reason and I had to reconnect it every time that happened.

2. Nested Views: When I added a few nested subviews under a view, it was hard to adjust when I wanted two subviews to be placed one behind other. On top of that, adding constraints to them was one of the most frustrating things to do.

3. Compile Time: I had an opportunity to build an app with around 35 Storyboard scenes. After switching to complete code I noticed that the compile-time was lightning fast. Before I had an issue that, as I add more scenes to my storyboard, I had to wait a little longer even though I have a pretty fast machine with 2GB graphics card and 16 GB memory.

I can’t insist on how much more reliable that code was when I wrote everything in code without using Storyboard. 🙂 I like to use Storyboard for quick mockups and prototypes, but not for the production.

I did find that it took more time than using Storyboard in some cases, such as adding things to the view etc.

However, overall I think I saved a lot of time without using Storyboard.

I want to tell any new iOS Developer to stop using Storyboard and design UI programmatically in your iOS app.

Eventually, you will be building UI Design programmatically anyway, so why not do it now?

I hope I have convinced you to go straight to building UI programmatically.

Let’s jump right in…

At the end of this tutorial, you will build this simple login screen 100% programmatically like the screenshot below.

Step #1: Get Rid of Storyboard

Step #2: Make A Root View Controller

Step #3: Add Subviews

Step #4: Enable Auto Layout Constraints

Step #1: Get Rid of Storyboard

• Go ahead and create a project in Xcode.
• Next, go to Project Navigator → Main.Storyboard →  Click anywhere inside ViewController Scene, then…
• Hit Delete Key to get rid of it…Yep, you heard it right 🙂

Also, get rid of the reference from the Deployment Info Section that points to the Main.Storyboard.

• Choose Project Navigator → top-level project folder icon. 
• Then, select the General Tab at the top →  Deployment Info section which is in the middle  → clear the text Main from the drop-down input field, which is next to the Main Interface label, like in the image below.

► Run the app and you will see the black screen and Xcode will give you the following message at the output console:

UniversalLoginScreenApp[2038:296725] [Application] Failed to instantiate the default view controller for UIMainStoryboardFile ‘Main’ — perhaps the designated entry point is not set?

To fix the issue, make the ViewController.swift as your root view controller again, but this time programmatically.

Step #2: Make A Root View Controller Programmatically

• Go to AppDelegate.swift file.
• Add the following code to the didFinishLaunchingWithOptions() method:

window = UIWindow(frame:UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = ViewController()

Make sure the ViewController() which is assigned to the window’s rootViewController in the last line matches with the file name ViewController.Swift.

One more thing I want to do before running the app is to add a background color to the ViewController() class so that I can make sure that it’s working properly.

• Go to ViewController.swift class file→ viewDidLoad() method.
• Add the following code to that method, as you can see I set it to .green color.

view.backgroundColor = .green

If everything goes well, you should be able to see the background color similar to the screenshot above when you run the app.

Step #3: Add Subviews Programmatically

I am going to need four views in my ViewController.swift in order to match the original sample design.

1 — UIView

2 — TextFields

1 — UIButton

Note: I could use UIStackView for arranging views, however, for simplicity sake, I am using UIView.

The first thing you need to do is create UIView which is the wrapper for the login form.

• Create a property called loginContentView in the ViewController.swift class, (normally above the ViewDidLoad() method).

private let loginContentView:UIView = {
let view = UIView()
view.backgroundColor = .grey
return view
}()

• Assign an anonymous function that will be called immediately with () parenthesis at the end, which will return the view object that I have initialized inside the function.
• Add the backgroundColor property to the view object and set its value to grey.

Note: All of the views will go to a separate file to follow the MVC pattern, for simplicity sake, I am creating views inside my ViewController.swift (which is the C part of MVC).

Create the other three views as properties of ViewController class similar to loginContentView.

Username:

private let unameTxtField:UITextField = {
let txtField = UITextField()
txtField.backgroundColor = .white
txtField.borderStyle = .roundedRect
return txtField
}()

Password:

private let pwordTxtField:UITextField = {
let txtField = UITextField()
txtField.borderStyle = .roundedRect
return txtField
}()

Login Button:

 let btnLogin:UIButton = {
 let btn = UIButton(type:.system)
 btn.backgroundColor = .blue
 btn.setTitle("Login", for: .normal)
 btn.tintColor = .white
 btn.layer.cornerRadius = 5
 btn.clipsToBounds = true
 btn.translatesAutoresizingMaskIntoConstraints = false
 return btn
 }()

Once all the views are created it is time to add them to the main view.

• Go to ViewController.swift → ViewDidLoad() method.
• Add unameTxtField, pwordTxtField, and btnLogin as subviews of the loginContentView using the addSubView method, like the code below.

loginContentView.addSubview(unameTxtField)
loginContentView.addSubview(pwordTxtField)
loginContentView.addSubview(btnLogin)

Then…

• Add loginContentView as a subview of the main view, like so:

view.addSubview(loginContentView)

Run it and see nothing…😕

This is because I have not given dimensions to any of the views. I could use frame property on each view object,

However, the better way is…

to use Auto Layout Constraints.

Step #4: Add Auto Layout Constraints Programmatically

• Add translatesAutoresizingMaskIntoConstraints property and set it’s value to false for all of the views inside their anonymous functions.

private let loginContentView:UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

Make sure to add a translatesAutoresizingMaskIntoConstraints property to unameTxtField, pwordTxtField and btnLogin function declarations similar to the one above.

Note: without this property, AutoLayout will NOT work… I have made mistakes by not adding them and wondered why nothing showed up on the viewscreen.

Then…

• Create a method called setUpAutoLayout() so that I do not have to write constraints code inside ViewDidLoad function.

Inside setUpAutoLayout(), I am going to add an auto layout constraint to each view one by one.

loginContentView Constraints

1. First, I am going to set left margin of loginContentView equal to the left margin of its parent view and set the isActive property to true, like so.

loginContentView.leftAnchor.constraint(equalTo:view.leftAnchor).isActive = true

2. Do the same with the right anchor.

loginContentView.rightAnchor.constraint(equalTo:view.rightAnchor).isActive = true

These two constraints will stretch the width of loginContentView to fit the left and right margin of its parent main view.

3. heightAnchor: Set height of loginContentView using hightAnchor constraint.  Only then you can see the loginContentView appearing on the screen.

loginContentView.heightAnchor.constraint(equalToConstant: view.frame.height/3).isActive = true

As you can see, I set the loginContentView’s height to match one-third the height of its parent main view.

If you run (►) the app at this point, you can see the loginContentView appearing at the top.

Finally, I need to move the loginContentView to the center of the screen vertically. You can achieve this by using another constraint called CenterYAnchor.

loginContentView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

Run it and you should be able to see the loginContentView vertically centered to its parent view.

Nice.

Let’s move on to the next one.

unameTxtField Constraints

1.  I am going to add the topAnchor constraint to unameTxtField, which will determine where the top margin of unameTxtField should be in its parent view (loginContentView).

I am setting the top margin of unameTxtField to match the top margin of loginContentView with the equalTo argument variable.

unameTxtField.topAnchor.constraint(equalTo:loginContentView.topAnchor).isActive = true

Also, I can use constant as a second argument variable to push the unameTxtField a little down from its parent view in this case loginContentView so that you have some space between them.

unameTxtField.topAnchor.constraint(equalTo:loginContentView.topAnchor, constant:40).isActive = true

Next,

2. Add leftAnchor and rightAnchor to unameTxtField.

Now, I am going to stretch the unameTxtField to match its parent left and right margins using leftAnchor and rightAnchor constraints similar to the previous one.

unameTxtField.leftAnchor.constraint(equalTo:loginContentView.leftAnchor).isActive = true
unameTxtField.rightAnchor.constraint(equalTo:loginContentView.rightAnchor).isActive = true

Let’s add some space between both sides so that unameTxtField is not touching the screen margins on either side. To do that, again use constant as a second argument, like the code below.

unameTxtField.leftAnchor.constraint(equalTo:loginContentView.leftAnchor, constant:20).isActive = true
unameTxtField.rightAnchor.constraint(equalTo:loginContentView.rightAnchor, constant:-20).isActive = true

I could use the main view’s constraints rather than using loginContentView’s constraints.

There is one problem with using the main view. What if you want to change the width of the loginContentView in the future, the subviews inside will NOT adjust properly.

That’s why I use loginContentView’s constraints to its children views.

3.  Let’s change the height of unameTxtField using heightAnchor constraint.

unameTxtField.heightAnchor.constraint(equalToConstant:50).isActive = true

Perfect.

Two more views to go…

pwordTxtField

1. First, I am going to add the width of the text field using left and right anchors similar to the unameTxtField.

pwordTxtField.leftAnchor.constraint(equalTo:loginContentView.leftAnchor, constant:20).isActive = true
pwordTxtField.rightAnchor.constraint(equalTo:loginContentView.rightAnchor, constant:-20).isActive = true

2. Next, add the height.

pwordTxtField.heightAnchor.constraint(equalToConstant:50).isActive = true

3. I am going to set the top margin of pwordTxtField to the bottom margin of unameTxtField using bottomAnchor constraint.

pwordTxtField.topAnchor.constraint(equalTo:unameTxtField.bottomAnchor, constant:20).isActive = true

Login Button Constraints

This one is very similar to pwordTxtField 🙂

btnLogin.topAnchor.constraint(equalTo:pwordTxtField.bottomAnchor, constant:20).isActive = true 
btnLogin.leftAnchor.constraint(equalTo:loginContentView.leftAnchor, constant:20).isActive = true 
btnLogin.rightAnchor.constraint(equalTo:loginContentView.rightAnchor, constant:-20).isActive = true 
btnLogin.heightAnchor.constraint(equalToConstant:50).isActive = true

If you want to know more about adding constraints programmatically, you can check out Apple’s documentation here.

In conclusion, I hope you understand why I stopped using storyboard and started building UI design programmatically.  Also, thank you for joining me in building a login screen design PROgrammatically.

What challenges do you think you are going to face if you switch to building the UI 100% programmatically? I am looking forward to hearing your feedback…👍

Sharing is caring!