Yet Another iOS Post About Creating Views Programmatically!

Julio Miguel
7 min readNov 21, 2016

--

I know, I know. There are numerous similar (read: identical) posts about why creating views programmatically is superior or obsolete (depending on the author) versus making them via Storyboards/Xibs.

Luckily for you, I won’t go over why programmatic views are superior to Storyboards or Xibs. Why?

Because all three methods accomplish the same thing: creating the user interface of an iOS application.

There will be endless debates on the topic. Each method has its own strengths/weaknesses, and everyone has a preference. But more importantly, each method will be around for as long as iOS is alive.

As my finance professor used to say: “There is more than one way to get to Nassau Street.”

Why I like Programmatic Views

Personally, I learned more about UIView and its instance/class methods when I switched to creating views programmatically more than I ever did using Storyboards or Xibs. Storyboards/Xibs didn’t really teach me about the inner workings of UIKit, like when it calls certain UIView methods. Everything was magic. As a software developer, not knowing how things work is a HUGE no-no. Personally, it annoys me to no end if I don’t know how things work in software development. I lose sleep over it.

After scouring the Apple Documentation, Google, StackOverflow, and the iOSProgramming subreddit, I really improved my understanding of:

  1. UIKit/UIView
  2. AutoLayout
  3. Swift
  4. Object Oriented Programming

In addition, you have finer control over what you can do with the user interface if you create views programmatically. (Who doesn’t like absolute control over what they make?)

Lastly (and also what I think is most important), every single property of the UIView subclasses you create are all visible in one .swift file. You don’t have to click each UIView in Interface Builder to see what the properties are set to. Everything you modified is explicit.

Why did I bother making a post about a topic that already has numerous articles about it?

I’ve read many articles/tutorials on programmatic views in iOS but none really adhered to the Model-View-Controller pattern.

The tutorials were brief, and the UIView code was located in the view controller (you should never be making views in the view controller). I basically had to guess what to do to separate the UIView code from the view controller but luckily after searching far and wide on Google, I found a bit of advice that helped me get started:

A single UIView subclass should be created for each view controller, essentially acting as a Xib that’s written in Swift instead of XML.

The Goal Of This Post

To give the reader (you) an idea of how to write views programmatically (using SnapKit) without breaking the Model-View-Controller design pattern and preventing the creation of the Massive View Controller.

This isn’t a beginner friendly tutorial

I’m assuming you know the basic project structure of a Single View Application in Xcode for Swift and know what Cocoapods is and how to install pods using it. You should have some familiarity with retain cycles and an understanding of value types vs. reference types.

With that said, let’s get started!

The Tutorial! — Basic Setup

Let’s start our journey into programmatic views with everyone’s favorite view: the Login Authentication screen!

But before we get started, let’s start with the basic setup.

  1. Create a new project: Single View Application
  2. Delete Main.storyboard (put in trash and permanently delete)
  3. Go to info.plist and delete “Main storyboard file base name”
  4. Rename ViewController.swift to LoginVC.swift
  5. Rename class ViewController to LoginVC

6. Go to your AppDelegate.swift file, and add the following in the didFinishLaunchingWithOptions method

Run the simulator. It shouldn’t crash, and you should see a green screen

7. Initialize your Podfile using pod init in the terminal at the directory of the project you’re using to follow along, and install SnapKit

After installing SnapKit, open up the .xcworkspace file of the project, and build the project using cmd + b. That should index and compile the project, and take out any false errors when interacting with Snapkit pod

8. Create a new .swift file called LoginView, and type the following boilerplate code. (When you start to override initWithFrame (Objective-C jargon) initializer this is the boilerplate code the Swift compiler types for you)

Now maybe you maybe thinking at this point, I’ll start doing something like this:

You’re half correct. I’m actually going to use closures to set these subviews up.

The setUpKeyboard method is a custom method I have that speeds up modifying a UITextField’s keyboard properties. It is an extension to UITextField:

I also have another helper function called set(cornerRadius:_). All it does is set the cornerRadius properly on a UIView instance

Now you might be wondering about the closures.

The reason why I use them is because they perfectly encapsulate each subview’s properties away from other subviews’ properties. This awesome separation of logic is what we need to make our code readable.

They are also set once and only once when the subview is instantiated in LoginView. How do I know this? From this.

Another thing you might be wondering about is the lazy declaration in the stackView property. The lazy keyword allows you to use self in the closure. Without it we can’t add other subviews in the stackView closure. Also, the capture list with unowned self is important because of a possible retain cycle since closures are reference types. The unowned declaration ensures that doesn’t happen.

Also you may be wondering about the various properties I’m modifying in each subview, and how that might violate the DRY principle. My answer is each of these properties also exist in Interface Builder, and I probably am violating DRY considering these two textfields are identical aside from the placeholder text.

You can create a helper function that takes in a string for the placeholder text and sets all the other properties to identical values but my personal preference is to show and explicitly modify each subview’s properties within the closure so a teammate looking at the code knows what each property is being set to.

You can probably abstract the “AvenirNext-Regular” magic string I created to a global string enum called Font which has all of your application’s fonts but for the sake of convenience I didn’t do that.

Finally, before we can start constraining the subviews with AutoLayout we have to do one more thing: add the subviews as subviews to LoginView.

We haven’t done that yet! :)

I typically use these helper functions to make this easy for myself:

So in LoginView’s initWithFrame initializer add the following:

What about the other subview? They’re already subviews of stackView so by that logic they’re in LoginView’s view hierarchy already :)

If you don’t add stackView as LoginView’s subview you’ll get an instant crash.

Also I’m well aware that SnapKit takes care of the translatesAutoresizingMaskIntoConstraints property for you. But I think it’s good practice to change it yourself.

Now for the constraints!

9. In LoginView’s initWithFrame initializer put this code in:

SnapKit makes it easy to create constraints. What constraints did we put?

  1. The stackView is centered in the screen
  2. The stackView’s leading edge (left edge) is constrained to the superview (LoginView’s left edge) and has an offset of 40.0 points (which means it’s 40.0 points away from LoginView’s left edge)
  3. The stackView’s trailing edge (right edge) is constrained to the superview (LoginView’s right edge) has an inset of 40.0 points (which means it’s 40.0 points away LoginView’s right edge)

The emailTextField’s height is equal to 7.5% of the superview’s height and because the stackView’s distribution property is fillEqually. passwordTextField and loginButton’s heights are also 7.5% of the superview’s height

You may be wondering why we’re laying out the constraints code in the initializer. The answer is because most of the time we need to only do this once and to make sure it’s only done once, do it in the initializer.

If you have more than one of the same constraint, your application will build up its memory footprint causing you problems in the future. This is especially true if it involves a table or collection view full of cells with duplicate constraints.

If you want to be extra safe use remakeConstraints instead. (It’s the same as makeConstraints except it removes all existing constraints of the UIView before creating any.

We can’t see what the view looks like yet because we haven’t connected it to LoginVC. Let’s do that now.

10. In LoginVC, add the following code:

LoginVC’s loadView method is the key to connecting LoginView to LoginVC.

This method is also one of the few methods in UIKit where you don’t call the super’s implementation.

Now we can run simulator. Your simulator should look something like this:

11. Making the equivalent of IBOutlets.

So if you used storyboards/xibs you would have had to make code like this at one point:

@IBOutlet weak var emailTextField: UITextField!

and connected it from the storyboard/xib

Programmatic Views can do something similar and more akin to Object Oriented Programming :)

Add this to inside LoginVC (The equivalent of putting IBOutlets in the view controller):

Oh no! A forced downcast!

Don’t worry.

We instantiated LoginVC’s view property as an instance of LoginView in the loadView method therefore, this forced downcast is safe and will succeed. We also make them weak references to avoid any retain cycles.

What about IBActions? Can we make those programmatically? The answer: Yup.

IBActions are just Target Action mechanisms

12. Create a function to be associated with loginButton within LoginVC

So loginButtonPressed will be the action that is invoked when the loginButton is pressed by the user. How do we connect this function to the loginButton?

Doing this in viewDidLoad:

Congrats! You connected a function to a UIControl programmatically!

Now run it in the simulator and it should work :)

So the final code in LoginVC should be this:

Conclusion!

I hope you found this tutorial enjoyable and informative. I definitely had fun making it.

Here’s the link to the repo for future reference!

If you have any questions please comment away. This was my first attempt at making an iOS tutorial. Please let me know on what I can improve on.

--

--

Julio Miguel

Finance analyst turned iOS Developer. Trying to learn anything and everything about software development in this lifetime.