In iOS, avoiding Interface Builder is nearly impossible. IB is old – as old as the Xcode SDK itself. Interface builder was one of the components of NextStep, and thus one of the reasons Steve Jobs got his old job back, who then developed NextStep into the platform for all Apple products. In more recent versions of Xcode and Cocoa, Storyboards organized IB views into whole apps without a lot of code. Outlets, segues and delegates might sometimes be a headache, but dragging and dropping most of the user interface made life easy for many developers, including me.

Coding a UI in Xcode
There are times even in Xcode where interface builder is not an option. I had one situation like this placing buttons and labels into UIImagePickerController's
property cameraOverlayView
when I was writing MiPlatform for Scientific Device Laboratory. I had to code the user interface. For example I could set up a label like this:
UILabel *timeLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 60, 40, 40)]; timeLabel.text = @"Time"; [self.view addSubview:timeLabel];
I can set up a button like this:
//make the on/off button onOffButton=[UIButton buttonWithType:UIButtonTypeSystem]; onOffButton.frame = CGRectMake(10,10, 100, 50); [onOffButton setTitle:@"OFF" forState:UIControlStateNormal]; [onOffButton addTarget:self action:@selector(OnOffButtonPressed) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:onOffButton];
In general, setting up a control in Objective-C is this:
- Make an instance of a
UIButton
orUILabel
using aCGRect
to define the frame of the control. - Set attributes for the Label or Button
- For a button, use the
addtarget:action:forControlEvents:
method to set up one or more event handlers for the button. - Add the control to the view or subview
Reviewing Tkinter Buttons and Labels
Of course in Python with Tkinter, there is no such thing as a storyboard or Interface Builder. There is a lot of differences between Xcode and Tkinter when it comes to setting up user interfaces, but there are similiarities when working in code. For comparision, a label would look like this:
time_value.set(2400.0) time_value = DoubleVar() time_value_label = Label(frame, textvariable = time_value, justify = LEFT) time_value_label.grid(row =0, column= 1, rowspan = 1, sticky = W)
A button would look like this:
onOff_button = Button(frame, command = onOffButtonPressed) onOff_button.grid(row = 2, column = 0, rowspan=2, columnspan = 2,sticky = SW + NE) onOff_button.configure(textvariable = onOff_button_title)
In the case of Buttons and Labels in Tkinter, the setup for the widget goes like this:
- Define and initialize your variables
- Make an instance of a Button or Label
- Use the
grid
method to position the widget - Set attributes for the Label or Button
- For a button, use the
command
attribute to set a event handler for the button. - If the button title or label text needs to be dynamic, use the
variabletext
attribute to bind a defined Tkinter variable
Using Thumbnail Sketches
When we are doing all the work by hand it is nice to plan it out first, otherwise we waste a lot of time adjusting positions in code. I usually just take a plain piece of paper and do a few thumbnail sketches, until I find one version I like. This page took five minutes to do, but from it, I learn most of what I need to know to put the UI together.

On the bottom left of the thumbnail page, I added a grid to the thumbnail sketch. Now I have an idea of where to place everything neatly.
I could place them in Xcode now by figuring a few points and widths. I often do something like this to start:
_topLeft.x = CGRectGetMinX(self.view.frame); _topLeft.y = CGRectGetMinY(self.view.frame); _bottomRight.x = CGRectGetMaxX(self.view.frame); _bottomRight.y =CGRectGetMaxY(self.view.frame);
By multiplying by the percentage from the _bottomRight point in x and y, I can then position anything relative to the size of the frame. Like grid
in TKinter this will resize on its own for any screen. This is a technique I use frequently with Sprite Kit as well. A better way in Xcode for UIViews is to use Auto Layout, which you can access from code and IB, but that is another topic — if not a series of blog entries.
Coding the Power Panel in Python
iPhones do not have GPIO pins. That is a Raspberry Pi feature. If this is to become a control panel for a device, it makes a lot more sense to write this code in Python for the Raspberry Pi.
From the thumbnail alone I can often figure out the grid. Notice the bottom right of the photo above. I drew a grid over the UI. We have three rows and two columns. All the elements fit into this structure, with the button spanning both columns. Though you don’t have to do this, I drew this up quickly, but a little more formally for clarity:
Let’s make a Tkinter UI. As covered in other posts, I’d start by making a window and frame:
from tkinter import * #make the window root=Tk() root.title('Power Panel #1') #make the frame frame = Frame(root) frame.grid(row=0, column=0) #start the main loop mainloop()
After making the frame I can add the two labels for Time and Voltage. These will go in Column 0, rows 0 and 1 respectively:
#render Labels voltage_label = Label(frame, text = 'Voltage') voltage_label.grid(row = 1,column = 0, sticky = E) time_label = Label(frame, text = 'Time') time_label.grid(row = 0, column = 0, sticky = E)
Note the use sticky=E
. It’s good style to align the labels to the right side of the grid, and the sticky
does this. Now for a dynamic label for the time in row 0, column 1:
time_value = DoubleVar() time_value.set(2400.0) time_value_label = Label(frame, textvariable = time_value, justify = LEFT) time_value_label.grid(row =0, column= 1, rowspan = 1, sticky = W)
To keep this label aligned correctly, we used sticky = W
. Finally we add the button spanning the bottom:
onOff_button_title = StringVar() onOff_button_title.set('Off') onOff_button = Button(frame, command = onOffButtonPressed) onOff_button.grid(row = 2, column = 0, rowspan=2, columnspan = 2,sticky = SW + NE) onOff_button.configure(textvariable = onOff_button_title)
We will need an event handler as well for the button to work. For this exercise, this handler will toggle the button title:
#event Handlers def onOffButtonPressed(): if onOff_button_title.get() == 'Off': onOff_button_title.set('On') else: onOff_button_title.set('Off')
Adding the Entry Widget
In Xcode, we have UITextField
for keyboard entry of data. The Python equivalent is Entry
. We will set up the desired Voltage as an Entry, which codes like a label with a textvariable
:
#render text fields voltage_value = DoubleVar() voltage_value.set(120.0) voltage_value_entry = Entry(frame, textvariable = voltage_value) voltage_value_entry.grid(row=1, column = 1)
Run and build the application. We should get a window like this — which looks nice and neat.
When we press the button, it toggles on and off.
User interfaces can get messy. Planning them out on paper certainly helps no matter if you are working on a Raspberry Pi or an iPhone. I often do paper thumbnails even when working in Interface Builder. A lot of my Interface builder Auto Layout headaches disappear if I do that simple paper and pencil exercise first.
Python Code for the Power Panel
Here’s the full Python code. Note a change here. While above I kept variables with the widgets as they were being created I usually group variable declarations and initializations together, as I’ve done below. This is a style preference, and the merits of each style can be debated forever. If you’ve worked with a static-typed language like C, you might have a similar style.
#Power_Panel_01 #code By Steven Lipton 04 19 14 from tkinter import * #make the window root=Tk() root.title('Power Panel #1') #make the frame frame = Frame(root) frame.grid(row=0, column=0) #make our variables voltage_value = DoubleVar() time_value = DoubleVar() onOff_button_title = StringVar() #set initial values for the variables voltage_value.set(120.0) time_value.set(2400.0) onOff_button_title.set('Off') #event Handlers def onOffButtonPressed(): if onOff_button_title.get() == 'Off': onOff_button_title.set('On') else: onOff_button_title.set('Off') #render button onOff_button_title = StringVar() onOff_button_title.set('Off') onOff_button = Button(frame, command = onOffButtonPressed) onOff_button.grid(row = 2, column = 0, rowspan=2, columnspan = 2,sticky = SW + NE) onOff_button.configure(textvariable = onOff_button_title) #render Labels voltage_label = Label(frame, text = 'Voltage') voltage_label.grid(row = 1,column = 0, sticky = E) time_label = Label(frame, text = 'Time') time_label.grid(row = 0, column = 0, sticky = E) time_value_label = Label(frame, textvariable = time_value, justify = LEFT) time_value_label.grid(row =0, column= 1, sticky = W) #render text fields voltage_value_entry = Entry(frame, textvariable = voltage_value) voltage_value_entry.grid(row=1, column = 1) #start the main loop mainloop()
A Power Panel in Objective-C
Just in case you want to see equivalent code in Objective-C, here’s a control panel set up for iPhone. I self-contained this into private and instance variables merely for compactness. In a true Model-View-Controller pattern however, it is likely you would need the UIButton to be a public property, so you can make one class a view and another its view controller.
// // SLViewController.m // HandCodedUI // // Created by Steven Lipton on 4/19/14. // Copyright (c) 2014 Steven Lipton. All rights reserved. // #import "SLViewController.h" @interface SLViewController () @end @implementation SLViewController{ BOOL isPowerOn; UIButton *onOffButton; } -(void)OnOffButtonPressed{ if (isPowerOn){ isPowerOn=NO; [onOffButton setTitle:@"OFF" forState:UIControlStateNormal]; }else{ isPowerOn=YES; [onOffButton setTitle:@"ON" forState:UIControlStateNormal]; } } -(void)makeViewsOnController{ //an example of coding our own buttons, labels and text fields //make the static time label UILabel *timeLabel = [[UILabel alloc]initWithFrame:CGRectMake(10, 60, 95, 40)]; timeLabel.text = @"Time"; [self.view addSubview:timeLabel]; //make the Static voltage label UILabel *voltageLabel = [[UILabel alloc]initWithFrame:CGRectMake(10, 100, 95, 40)]; voltageLabel.text = @"Voltage"; [self.view addSubview:voltageLabel]; //make the TIME text field UITextField *timeText = [[UITextField alloc]initWithFrame:CGRectMake(105,60,95,40)]; timeText.placeholder = @"00"; [self.view addSubview:timeText]; //make the voltage text field UITextField *voltageText = [[UITextField alloc]initWithFrame:CGRectMake(105,100, 95, 40)]; voltageText.placeholder = @"150"; [self.view addSubview:voltageText]; //make the on/off button onOffButton=[UIButton buttonWithType:UIButtonTypeSystem]; onOffButton.frame = CGRectMake(10,150, 190, 40); [onOffButton setTitle:@"OFF" forState:UIControlStateNormal]; [onOffButton addTarget:self action:@selector(OnOffButtonPressed) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:onOffButton]; } - (void)viewDidLoad { [super viewDidLoad]; isPowerOn=NO; [self makeViewsOnController]; } @end
Leave a Reply