What are “nibless” apps?
Apps which are designed without the help of Storyboard are called as “Nibless” apps. Normally we design an app with the help of a Storyboard file. Earlier they were called Xib files or Nib files. Hence the term “Nibless”.
Why should we create Apps without storyboard?
There are a number of reasons.
- It makes for a better experience when implementing along with version control.
- Allows us to create UI elements dynamically.
- Makes reusable UI Components easier to distribute and reuse.
How can we create Apps without Storyboard?
There are a couple of things that need to be done. Firstly the Main.storyboard file needs to be removed and the project settings need to be updated to reflect this change.. We are doing this since we won’t be using the storyboard file.
Everything will now have to be started up by us manually. Many of these tasks were taken care of by storyboard, but since that was removed we will have to do it. This means we have to manually create the window, create the view controller set it as a the root view controller.
We also have to manually create each and every component on our own. That is the very thing we were trying to achieve.
This example is implemented on Xcode 10.3 on macOS 10.14.5. We are not implementing auto layout in this article. We will look at implementing that programmatically in the next article.
- Let us start with an empty project. Open Xcode.
- Select File > New > Project
- Give it any name. Select the language as Swift & leave the checkboxes unchecked.
- Once the project loads select the Main.storyboard file and delete it.
- Switch to the Project settings file.
- Remove the entry for the main interface.
- It is a good idea to leave the LaunchScreen.storyboard file. The reason for this is to give the launch process a reference of the screen size it needs to produce. Else it will default down to the 0,0,320,480 which is the old iPhone size.
- Switch to the AppDelegate.swift file.
- Add the following property below the UI Window declaration.
let mainScreenController : ViewController = ViewController()
- Add the code to create the window and set root view controller in the didFinishLaunchingWithOptions method
//1. Create the UIWindow object self.window = UIWindow(frame: UIScreen.main.bounds) //2. Set the root view controller self.window?.rootViewController = self.mainScreenController //3. Make the window key and visible self.window?.makeKeyAndVisible()
- Switch to the ViewController.swift file.
- Declare the following variables
//UI Variables var labelDemo : UILabel? var imageDemo : UIImageView? var buttonDemo : UIButton = UIButton(type: UIButton.ButtonType.roundedRect) var dataField : UITextField?
- Implement the function to create labels. The process of creating a view programmatically is fairly straightforward. Barring a few variations depending on the view component nothing is drastically different.
func createLabel() { //1. Specify the dimensions let labelRect : CGRect = CGRect(x: 100.0, y: 50.0, width: self.view.frame.size.width - 130.0, height: 60.0) //2. Create the view object labelDemo = UILabel(frame: labelRect) //3. Customise the view attributes labelDemo?.text = "This is my first Programmatic App." labelDemo?.textColor = UIColor.yellow labelDemo?.textAlignment = NSTextAlignment.left labelDemo?.numberOfLines = 0 labelDemo?.font = UIFont.boldSystemFont(ofSize: 20.0) //4. Add the view to the subview self.view.addSubview(labelDemo!) }
Let us examine the steps one by one.//1. Specify the dimensions let labelRect : CGRect = CGRect(x: 100.0, y: 50.0, width: self.view.frame.size.width - 130.0, height: 60.0)
This will define the dimensions of the view. As we are not implementing auto layout we will need to do this manually.//2. Create the view object labelDemo = UILabel(frame: labelRect)
Now that we have the dimensions we can go ahead and instantiate an instance of the label object using those dimensions. These 2 parts are the same as dragging a label from the object library onto the storyboard and placing it onto the storyboard per our requirements.//3. Customise the view attributes labelDemo?.text = "This is my first Programmatic App." labelDemo?.textColor = UIColor.yellow labelDemo?.textAlignment = NSTextAlignment.center labelDemo?.numberOfLines = 0 labelDemo?.font = UIFont.boldSystemFont(ofSize: 20.0)
This part is the same as changing the attributes in the attributes inspector. This is where we customise the label.//4. Add the view to the subview self.view.addSubview(labelDemo!)
This last part also forms one part of dragging the label on to the storyboard. When we drag a view on to the storyboard it is placed within the main view that belongs to the ViewController. This statement completes the above process. - Repeat the above steps for showing an image.
func createImage() { //1. Specify the dimensions let imageRect : CGRect = CGRect(x: 30.0, y: 50.0, width: 60.0, height: 60.0) //2. Create the image model let imageModel : UIImage = UIImage(named: "logo.png")! //3. Create the view object imageDemo = UIImageView(frame: imageRect) //4. Customise the view attributes imageDemo?.image = imageModel imageDemo?.contentMode = UIView.ContentMode.scaleAspectFit //5. Add the view to the subview self.view.addSubview(imageDemo!) }
The code above is almost similar to the one created for labels except for the fact that we had to explicitly create a model object for the view. Images being different from strings, require this process to be done explicitly. - Similarly let us implement the code for creating buttons
func createButton() { //1. Specify the dimensions let buttonRect : CGRect = CGRect(x: 30.0, y: 220.0, width: 100.0, height: 50.0) //2. Provide the frame to the button buttonDemo.frame = buttonRect //3. Customise the view attributes buttonDemo.setTitle("Click Me", for: UIControl.State.normal) buttonDemo.addTarget(self, action: #selector(ViewController.clickMeTapped), for: UIControl.Event.touchDown) //4. Add the view to the subview self.view.addSubview(buttonDemo) } @objc func clickMeTapped( { print("Click me tapped!") }
Again just minor variations here. Mainly the step to add a target function to be invoked when the button is tapped. We also need to write the target function itself. - We will also implement the code to create a text field.
func createTextField() { //1. Provide dimensions for the view let tfRect : CGRect = CGRect(x: 30.0, y: 140.0, width: self.view.frame.size.width - 60.0, height: 50.0) //2. Create the view object dataField = UITextField(frame: tfRect) //3. Customise the attributes of the view dataField?.placeholder = "Enter Name" dataField?.borderStyle = UITextField.BorderStyle.roundedRect dataField?.keyboardType = UIKeyboardType.namePhonePad dataField?.keyboardAppearance = UIKeyboardAppearance.dark dataField?.returnKeyType = UIReturnKeyType.go //4. Add the view to the subview self.view.addSubview(dataField!) }
- Next we need to call all these functions. I have implemented a single creator function for that.
func createUIElements() { self.createLabel() self.createImage() self.createButton() self.createTextField() }
- Lastly we will call this function in the viewDidLoad method. Add the following lines to the viewDidLoad method.
self.view.backgroundColor = UIColor.lightGray self.createUIElements()
I have also added code to change the background colour so that we can see the background clearly. - Run the project. Everything should appear normally.
Are there any benefits of creating apps without storyboard?
The points mentioned in the “why should we make programmatic apps?” section are some of the advantages. Beyond that there aren’t too many.
If you are looking at a team based project development then this approach is good.
There is no difference in terms of memory or performance when it comes down to apps design with or without storyboard.
Are there any drawbacks?
As can be seen from the example above, there are a couple of drawbacks
- The main drawback is that you can’t get a quick preview of how your app looks. You have to run the simulation every time you wish to see the end result.
- There is a lot more coding involved. Which can be daunting to those who are overly accustomed to designing with the help of storyboards
Note
A small point. I have left the LaunchScreen.storyboard file. I did not delete it. The reason I did that was to allow the app to allow the system to determine the dimensions on the device. If we do delete the file then the UIScreen.main.bounds return (0.0, 0.0, 320.0, 480.0) which are the old iPhone screen size settings.
While you can go ahead and make changes programmatically it is a lot easier to just leave the LaunchScreen.storyboard file there.
Carrying on from the previous point. It actually is okay if you leave the Main.storyboard file as is too. In which case you will have to skip steps 5,6,8,9,10. The code is still running programmatically but you do not have to create the main ViewController manually.
Download the Source Code
You can download the Xcode Project from this link.