DEV Community

Alex
Alex

Posted on

How to create custom views programmatically

Hello wonderful community 👋. As a new iOS developer I'm starting to learn how to create views programmatically, it has been tricky to learn how to do this coming from using Storyboards 😅.

In this tutorial, I'll guide you step-by-step on programmatically creating views in iOS. It's an opportunity for me to share what I've learned, with the hope of helping others who are also exploring this area.

Let's start coding! 😀

Starting a new Xcode project

I'm using Xcode version 14.3 and targeting iOS 16

  1. Open Xcode and click on New Project, we will get this window:

new project window

Here we are going to select the simplest template available, iOS > App

  1. Now let's give some basic information:
  2. Product Name: It's the name of our app.
  3. Team: If you have a Apple Developer Account you should select it here, otherwise leave it as None is alright (leave it as None will allow you to test your app only in the simulator).
  4. Interface: Since we are doing an example with UIKit we need to select Storyboard (don't worry we are not actually using them).
  5. Language: Select Swift.

The rest of the options can be unchecked, they won't be used in this simple example.

app basic init

  1. Click on Next and select a location on your computer to save the project, finally click on Create.

We now have a simple app ready for us. 🎉

basic project template

Deleting storyboard references from our project

Now it's time to delete the storyboard file and configurations associated with it.

In this simple project we have two storyboard files, Main and LaunchScreen.

We only need to delete the Main one because this is the one that is loaded once our app starts. We want to provide our own by using code.

storyboard files

  1. Delete Main from the project, when prompt click Move to trash.

deleting main

  1. Delete the references of this storyboard, open Info.plist and delete the value called Storyboard Name by clicking the minus icon.

info deleting storybaord

  1. Delete the storyboard reference in the target app, this is important because we no longer have the storyboard file so if we try to compile we would get an error because of the missing reference.

target storyboard delete reference

Now run your app and you'll get a nice black screen, this mean you deleted the storyboard correctly!.

simulator black screen

Configuring programmatically the root window

Now you have an empty screen, nothing was loaded and that's a problem. The storyboard made all of this configuration automatically for you. Now it's your responsibility to configure the screen that will be show when your app starts.

  1. Open SceneDelegate.swift and configure the root window of your app. Locate the method called func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions).

Once you found it, let's change the method's body with the following:

guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
let viewController = ViewController()
window.rootViewController = viewController
self.window = window
window.makeKeyAndVisible()
Enter fullscreen mode Exit fullscreen mode

This code do several things:

  • It tries to create a windoScene by using the passed scene object, this object will contain the scene of our app.
  • It creates a new window, this will tell your app to create a windowScene, this object will contain your app's user interface.
  • It creates a viewController, this object is responsible for controlling your view objects. This class is already on your template (`ViewController.swift), so there is nothing special about its name.
  • Assigning the new ViewController as the root view controller, i.e., the first view controller we are going to create to start our app UI.
  • We are assigning the new created window to our class window, self.window = window.
  • Making the window visible on the screen, window.makeKeyAndVisible().

After that configuration your app interface is back to life. If you run it at this point you'll still get a back screen, that's ok because we haven't configured anything on our View objects.

Creating our simple interface programmatically

Now it's time to add some code to make our interface show something on the screen!

ViewController life cycle

The UIViewController has a specific life cycle, i.e., certain predefined methods are called at specific moments of the object's life. Since we are creating our interface programmatically we need to understand two methods of this life cycle.

  • func loadView(): This method is used to load our custom views and make some configuration on them before they appear on screen.
  • func viewDidLoad(): This method is called once the ViewController is loaded into memory, here you can create more customization and configuration to your views.

As you can guess, the object first run loadView and then viewDidLoad.

Creating an empty UIView

Each ViewController creates a default empty View, but we want to have our own custom one, so let's start by doing that.

  1. Create a new file for our new custom view, let's call it CustomView.swift and add the following code inside.

`swift
// CustomView.swift
import UIKit

final class CustomView: UIView {

override init(frame: CGRect) {
    super.init(frame: frame)
    backgroundColor = .yellow
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}
Enter fullscreen mode Exit fullscreen mode

}
`

Our view inherits from UIView so we need to define at least these two constructors (init methods). The one we need to focus on is the first one. Here we are adding a background color.

  1. Create the custom view object and show it on the screen, to achieve this lets open the ViewController.swift and inside the class create a new object of our view and assign it as the new view in the loadView. You should end up with a code similar to this:

`swift
// ViewController.swift
import UIKit

class ViewController: UIViewController {

lazy var customView = CustomView()

override func loadView() {
    // we are creating a class property because we may have delegates
    // assign your delegates here, before view
    view = customView
}

override func viewDidLoad() {
    super.viewDidLoad()
}
Enter fullscreen mode Exit fullscreen mode

}
`

One important point to mention, the view property is assigned by default to use all of the phone screen. So we don't need to assign any constrains because of this. The CustomView will take all of the screen.

Now run your app and you'll see a yellow screen.

yellow view

At this point, you should have the following files on your project:

current files

Great, now you loaded an empty custom view to your screen!.

Adding a simple label on the empty View

To finish this tutorial let's add a simple label using auto layout.

  1. Create a UILabel property on the CustomView class and give it some default values like a title and color. You don't need to provide any dimension, we will do this with auto layout. Your CustomView class should look something like this:

`swift
// CustomView.swift
import UIKit

final class CustomView: UIView {

// 1. Creating the new element
lazy var label: UILabel = {
    // internal label, not the same as the external label
    let label = UILabel()
    label.text = "Hello World"
    label.textAlignment = .center
    label.textColor = .white
    label.backgroundColor = .black
    return label
}()

override init(frame: CGRect) {
    super.init(frame: frame)
    backgroundColor = .yellow
    // 2. Adding the new element into the view
    addSubview(label)
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}
Enter fullscreen mode Exit fullscreen mode

}
`

If you run your code the new label won't appear on the screen, that's because the label has a size of 0 and its position in the screen's position (0,0). This happened because we didn't provide a CGRect initializer. It's ok, we are going to fix this with auto layout.

  1. Add auto layout constrains to allow the object to appear on the view (this will give it an area), also remember to set to false the property translatesAutoresizingMaskIntoConstraints if we don't do this our view may present problems rendering with auto layout.

Remember that each ui element is represented as a box, so each edge has a name we can refer in our code to assign the constrains.

ui box

Your code inside CustomView.swift should look like something like this:

`swift
// CustomView.swift
import UIKit

final class CustomView: UIView {

// 1. Creating the new element
lazy var label: UILabel = {
    // internal label, not the same as the external label
    let label = UILabel()
    label.text = "Hello World"
    label.textAlignment = .center
    label.textColor = .white
    label.backgroundColor = .black
    return label
}()

override init(frame: CGRect) {
    super.init(frame: frame)
    backgroundColor = .yellow
    // 2. Adding the new element into the view
    addSubview(label)
    // 3. Add the auto layout constrains
    setUpLabelConstrains()
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

private func setUpLabelConstrains() {
    // Needed to avoid auto layout conflicts
    label.translatesAutoresizingMaskIntoConstraints = false
    // Set the top part of the label to the safe area of the custom view and add a vertical separation of 64 points
    label.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 64).isActive = true
    // Set the left part of the label to the safe area of the custom view and add a horizontal separation of 18 points
    label.leftAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leftAnchor, constant: 18).isActive = true
    // Set the right part of the label to the safe area of the custom view and add a horizontal separation of -18 points
    // note: if this value it's positive the element will be separate from outside the screen 18 points.
    label.rightAnchor.constraint(equalTo: self.safeAreaLayoutGuide.rightAnchor, constant: -18).isActive = true
    // Set the height of the label to be equal to 48 points
    label.heightAnchor.constraint(equalToConstant: 48).isActive = true
}
Enter fullscreen mode Exit fullscreen mode

}
`

Now let's run the app, a custom label should appear on the screen now. This custom label is a child of the custom view, which was created by the ViewController and set as the default view for our ViewController.

final result

Conclusion

Congratulations, you created an app's ui using only code!. I hope this simple tutorial helps you to understand this topic. There is a lot more to cover but this is a good first step, thank you for reading!.

Until next time.

Top comments (2)

Collapse
 
srindrive profile image
Sergey Rolich

@msa_128 Hi,
Is it possible to add multiple CustomViews to one ViewController?

Collapse
 
13warrior profile image
Rostyslav

Hi,
What is the use of this information?
So Xibs, Storyboards and UIKit are old technologies.
If you want talk about the current or future, then this is SwiftUI.