Create UIViews Programmatically in Swift

2019-10-21

A good place to start is to read through Apple’s documentation for the UIView class.

Install PureLayout. PureLayout provides a developer friendly API for defining layout constraints for Auto Layout. Writing Auto Layout code from scratch isn’t easy, so PureLayout handles most of the complexity for us.

Use cocoapods to add PureLayout to your project.

platform :ios, "10.0"
use_frameworks!
   
pod "PureLayout"

Now install the dependency.

pod install

The first time you run this it will create a new project file that ends in .xcworkspace. From now on you should open this project in Xcode when working on the project.

Build a custom class. Create a new file called ProfileView.swift and add the following code:

import UIKit
   
class ProfileView: UIView {
   
}

Next, add our initializers.

import UIKit
   
class ProfileView: UIView {
       
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
       
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
       
}

We also want to override the updateConstraints method on the UIView so that PureLayout can set up our auto layout constraints. Since updateConstraints can be called multiple times once it’s initialized, add a boolean variable called shouldSetupConstraints to our class and toggle it off once it’s called once. Note that we also import PureLayout in this step.

import UIKit
import PureLayout
   
class ProfileView: UIView {
    var shouldSetupConstraints = true
               
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
       
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
       
    override func updateConstraints() {
        if (shouldSetupConstraints) {
            // AutoLayout constraints
            shouldSetupConstraints = false
        }
        super.updateConstraints()
    }
}

Now comes the fun part. We want our profile view to contain a mainPhotobannerPhoto, and a callToAction components so we will create them now. First declare the three objects in our ProfileView class.

import UIKit
import PureLayout
       
class ProfileView: UIView {
    var shouldSetupConstraints = true
       
    var bannerPhotoView: UIImageView!
    var mainPhotoView: UIImageView!
    var callToActionButton: UIButton!
               
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
       
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
       
    override func updateConstraints() {
        if (shouldSetupConstraints) {
            // AutoLayout constraints
            shouldSetupConstraints = false
        }
        super.updateConstraints()
    }
}    

Now we’re ready to create our components and add them to our ProfileView view. First set the background color of ProfileView to white. Then configure the bannerPhotoViewmainPhotoView, and callToActionButton and add them to the view.

Notice how we’re initializing each component’s frame to zero. That’s because we’re going to allow Auto Layout to take care of that later.

import UIKit
import PureLayout
   
class ProfileView: UIView {
    var shouldSetupConstraints = true
       
    var bannerPhotoView: UIImageView!
    var mainPhotoView: UIImageView!
    var callToActionButton: UIButton!
       
    let screenSize = UIScreen.main.bounds
    let edgesInset: CGFloat = 10.0let centerOffset: CGFloat = 62.0
       
    override init(frame: CGRect) {
        super.init(frame: frame)
           
        self.backgroundColor = UIColor.white
           
        bannerPhotoView = UIImageView(frame: CGRect.zero)
        bannerPhotoView.backgroundColor = UIColor.gray
        bannerPhotoView.autoSetDimension(.height, toSize: screenSize.width / 3)
           
        mainPhotoView = UIImageView(frame: CGRect.zero)
        mainPhotoView.backgroundColor = UIColor.gray
        mainPhotoView.layer.borderColor = UIColor.white.cgColor
        mainPhotoView.layer.borderWidth = 1.0
        mainPhotoView.layer.cornerRadius = 5.0
        mainPhotoView.autoSetDimension(.width, toSize: 124.0)
        mainPhotoView.autoSetDimension(.height, toSize: 124.0)
           
        callToActionButton = UIButton(frame: CGRect(x: edgesInset, y: screenSize.height - 100, width: screenSize.width - (edgesInset * 2), height: 50))
        callToActionButton.layer.cornerRadius = 5.0
        callToActionButton.backgroundColor = UIColor(red: 10/255, green: 178/255, blue: 89/255, alpha: 1.0) /* #0ab259 */
        callToActionButton.setTitle("Book", for: .normal)
        callToActionButton.addTarget(self, action: #selector(callToActionButtonAction), for: .touchUpInside)
           
        self.addSubview(bannerPhotoView)
        self.addSubview(mainPhotoView)
        self.addSubview(callToActionButton)
    }
       
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
       
    override func updateConstraints() {
        if (shouldSetupConstraints) { 
            // AutoLayout constraints                           
            shouldSetupConstraints = false
        }
        super.updateConstraints()
    }
}

Now we’re ready to start pinning our components and letting Auto Layout take care of the rest.

import UIKit
import PureLayout
   
class ProfileView: UIView {
    var shouldSetupConstraints = true
       
    var bannerPhotoView: UIImageView!
    var mainPhotoView: UIImageView!
    var callToActionButton: UIButton!
       
    let screenSize = UIScreen.main.bounds
    let edgesInset: CGFloat = 10.0let centerOffset: CGFloat = 62.0
       
    override init(frame: CGRect) {
        super.init(frame: frame)
           
        self.backgroundColor = UIColor.white
           
        bannerPhotoView = UIImageView(frame: CGRect.zero)
        bannerPhotoView.backgroundColor = UIColor.gray
        bannerPhotoView.autoSetDimension(.height, toSize: screenSize.width / 3)
           
        mainPhotoView = UIImageView(frame: CGRect.zero)
        mainPhotoView.backgroundColor = UIColor.gray
        mainPhotoView.layer.borderColor = UIColor.white.cgColor
        mainPhotoView.layer.borderWidth = 1.0
        mainPhotoView.layer.cornerRadius = 5.0
        mainPhotoView.autoSetDimension(.width, toSize: 124.0)
        mainPhotoView.autoSetDimension(.height, toSize: 124.0)
           
        callToActionButton = UIButton(frame: CGRect(x: edgesInset, y: screenSize.height - 100, width: screenSize.width - (edgesInset * 2), height: 50))
        callToActionButton.layer.cornerRadius = 5.0
        callToActionButton.backgroundColor = UIColor(red: 10/255, green: 178/255, blue: 89/255, alpha: 1.0) /* #0ab259 */
        callToActionButton.setTitle("Book", for: .normal)
        callToActionButton.addTarget(self, action: #selector(callToActionButtonAction), for: .touchUpInside)
           
        self.addSubview(bannerPhotoView)
        self.addSubview(mainPhotoView)
        self.addSubview(callToActionButton)
    }
       
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
       
    override func updateConstraints() {
        if (shouldSetupConstraints) {
               
            bannerPhotoView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .bottom)
               
            mainPhotoView.autoPinEdge(toSuperviewEdge: .left, withInset: edgesInset)
            mainPhotoView.autoPinEdge(.bottom, to: .bottom, of: bannerPhotoView, withOffset: centerOffset)
               
            callToActionButton.autoPinEdge(toSuperviewEdge: .bottom, withInset: edgesInset)
            callToActionButton.autoPinEdge(toSuperviewEdge: .left, withInset: edgesInset)
            callToActionButton.autoPinEdge(toSuperviewEdge: .right, withInset: edgesInset)
               
            shouldSetupConstraints = false
        }
        super.updateConstraints()
    }
       
    @objc func callToActionButtonAction(sender: UIButton!) {
        print("Button tapped")
    }
} 

Almost done! Last thing we need to do is initialize our ProfileView object when our ViewController calls viewDidLoad. Go to ViewController.swift and add the following to viewDidLoad. Make sure to declare our ProfileView object up top too.

import UIKit
   
class ViewController: UIViewController {
  var profile: ProfileView!
     
  override func viewDidLoad() {
    super.viewDidLoad()
   
    profile = ProfileView(frame: CGRect.zero)
    self.view.addSubview(profile)
       
    // AutoLayout
    profile.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets.zero)
  }
   
  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
}