Building app without storyboard easier with NSLayoutAnchor

Kent Winder
Nextzy
Published in
3 min readApr 8, 2019

Why not using storyboards? What are the good reasons to build your UI programmatically?

  • First of all: merge conflicts, it is one of the most common occurrence when there is more than one developer working on a storyboard at the same time. Since storyboards are auto generated XML, it’s kind of difficult to diagnose and resolve conflicts.
  • IBOutlet/IBAction is so annoying when you want to change the variable names, the app will crash if you do not reconnect the view to its new outlet/action name
  • Identifiers used for table view cell or storyboard id are in string, so it’s unsafe (easy to make typos) when using in multiple places compared to a static constant in code.
  • For nested views/stacked views: very difficult to adjust views which are placed behind a view, and for adding constraints as well
  • Change layout: when you delete/move views with auto layout, even when you embed views in another view/scroll view, usually you must delete and recreate a lot of constraints
  • Reuse code: it’s easier to share a source file or code for a custom view than a storyboard/xib file
  • Some other minor reasons are: compile time for complicated storyboards and refactoring styles in storyboards takes longer, screen size of laptop is not big enough, easier theme support, animate views,… you name it

So in this post, I will show you how to build an table view controller with layout like this

Setup new project

Because we don’t use the storyboard created by default, we have to do a few things first before the application can run properly

Create a single view application project

  • Delete Main.storyboard file (we don’t need this file anymore)
  • Select your target project, in General tab, scroll to Deployment Info, delete value of Main Interface
  • Tell the application to open which view controller when the it starts, replace code inside didFinishLaunchingWithOptions of AppDelegate file with this, don’t worry we will create GamesViewController file later
window = UIWindow(frame: UIScreen.main.bounds)
let rootViewController = GamesViewController()
let navController = UINavigationController(rootViewController: rootViewController)
window!.rootViewController = navController
window!.makeKeyAndVisible()
return true

Create GamesViewController

  • This view controller display a list of Game, it contains a table view that takes up the whole screen, so in this file we have our tableView declared
var tableView: UITableView!
  • and somewhere in viewDidLoad
tableView = UITableView()
  • We have to set translatesAutoresizingMaskIntoConstraints property to false first so we can apply constraints to the tableView, because by default, the property is set to true for any view you programmatically create (If you add views in Interface Builder, the system automatically sets this property to false)
tableView.translatesAutoresizingMaskIntoConstraints = false
  • Add this table view to current view controller view
view.addSubview(tableView)
  • Now add 4 constraints to this table view so it will take up the whole screen (leading, trailer, top, and bottom)
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true

When we create contraint, it returns an inactive constraint of the form Anchor, so we have to activate it by setting isActive = true

Now, add data source for this table view, try running the app with some fixed values, you can see a screen with one table view

Create view for GameTableViewCell

From the image above, we need some views like these in our cell

fileprivate let coverImageView = UIImageView()
fileprivate let gameNameLabel = UILabel()
fileprivate let developerLabel = UILabel()
fileprivate let descriptionLabel = UILabel()
fileprivate let priceLabel = UILabel()

After we setup properties (font, color, priority, …) for those views, we can add constraints to them, but this time instead of activate each constraint one by one, we can activate a group of constraints like this

NSLayoutConstraint.activate([
// game cover
coverImageView.widthAnchor.constraint(equalToConstant: 60),
coverImageView.heightAnchor.constraint(equalToConstant: 80),
coverImageView.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor),
coverImageView.topAnchor.constraint(equalTo: marginGuide.topAnchor),
coverImageView.bottomAnchor.constraint(lessThanOrEqualTo: marginGuide.bottomAnchor),
// game name
gameNameLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor),
gameNameLabel.leadingAnchor.constraint(equalTo: coverImageView.trailingAnchor, constant: 8),
// developer
developerLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor),
developerLabel.leadingAnchor.constraint(equalTo: gameNameLabel.trailingAnchor, constant: 8),
developerLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor),
// description
descriptionLabel.topAnchor.constraint(equalTo: gameNameLabel.bottomAnchor, constant: 8),
descriptionLabel.leadingAnchor.constraint(equalTo: coverImageView.trailingAnchor, constant: 8),
descriptionLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor),
// price
priceLabel.topAnchor.constraint(greaterThanOrEqualTo: descriptionLabel.bottomAnchor, constant: 8),
priceLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor),
priceLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor)
])

Pretty easy, right?

Source code for this post can be downloaded from here: https://github.com/kentwinder/iOS-NoStoryboard-Demo

--

--

Kent Winder
Nextzy
Editor for

Just a regular guy who loves coding, reading, and getting tattoos