Using RubyMotion With Xcode's Interface Builder
18 Oct 2012Executive Summary
RubyMotion is a Mac application that lets developers write iOS apps in Ruby. It’s possible to create the user interface for the app entirely within RubyMotion or with a Ruby gem like Teacup. But what about devs who prefer Interface Builder?
This article will show how to use Xcode’s Interface Builder to create a basic UI for a RubyMotion application.
Our Sample App: FizzBuzz
For this example we will build an iOS app that calculates and displays the fizzbuzz function. As a refresher, here’s the fizzbuzz algorithm:
- Count integers starting with 1 and incrementing as high as the user wants to go.
- If the integer to be displayed is a multiple of 3, display “fizz” instead.
- If the integer to be displayed is a multiple of 5, display “buzz” instead.
- If the integer to be displayed is a multiple of both 3 and 5 (i.e. a multiple of 15) display “fizzbuzz”.
The bare-bones UI appears at right. The plus sign increments the counter, minius decrements it, and the label area shows “Begin” at the beginning.
First, Build the RubyMotion App
We start by building the fizzbuzz app in RubyMotion.
$ motion create fizzbuzzrm
The code for the finished app can be found on GitHub.
Build the UI in Interface Builder
Next, build the UI in Xcode’s Interface Builder.
After completing the interface, we will need to asign tags to each element so that the UI knows how to communicate with RubyMotion. Scroll down to View|Tag in the rightmost colum (screenshot below). In this example, I assigned the tags 1, 2, 3, and 4 to the label, plus button, minus button, and reset, respectively.
Save the IB file in the /resources
directory of your RubyMotion project. In my example, I called the file
fbib.xib
. RubyMotion will compile the .xib file next time you run the rake command to build the app.
Connecting the .xib file to the RubyMotion App
Let’s head back to the RubyMotion app so we can tell RubyMotion how to interact with the IB file. Note that we specify the name of the IB file on the second line of the loadView
method.
def loadView
views = NSBundle.mainBundle.loadNibNamed "fbib", owner:self, options:nil
self.view = views[0]
@counter = 0
@view_handle = self.view
end
The viewDidLoad
method is where we assign buttons to their corresponding methods, and we specify how the app should behave when each button is pressed.
def viewDidLoad
@label = view.viewWithTag 1
plus_button = view.viewWithTag 2
minus_button = view.viewWithTag 3
reset_button = view.viewWithTag 4
# background_area = view.viewWithTag 5
plus_button.addTarget(self, action:'plusTapped:', forControlEvents:UIControlEventTouchUpInside)
minus_button.addTarget(self, action:'minusTapped:', forControlEvents:UIControlEventTouchUpInside)
reset_button.addTarget(self, action:'resetTapped:', forControlEvents:UIControlEventTouchUpInside)
# background_area.addTarget(self, action:'backgroundTapped:', forControlEvents:UIControlEventTouchUpInside)
end
And finally, let’s define a method for each button.
def plusTapped(sender)
@counter += 1
@label.text = FizzBuzzViewController.fbcalc(@counter).to_s
end
def minusTapped(sender)
@counter -= 1
@label.text = FizzBuzzViewController.fbcalc(@counter).to_s
end
def resetTapped(sender)
@counter = 0
@label.text = "Begin"
rotate_background(@view_handle)
end
def rotate_background(view_handle)
@color_index ||= 0
case @color_index
when 0
@view_handle.backgroundColor = UIColor.redColor
when 1
@view_handle.backgroundColor = UIColor.greenColor
when 2
@view_handle.backgroundColor = UIColor.blueColor
when 3
@view_handle.backgroundColor = UIColor.yellowColor
end
@color_index = (@color_index +1) % 4
end
Rake
Run $ rake
to compile and execute the app.
Note the surprise when you hit the Reset button in the app. This surprise is one reason why you should always have a designer on your dev team!
Conclusion
The more familiar I become with RubyMotion, the more I learn that there are multiple ways to do things, especially when it comes to UI. If you have ideas or suggestions for a different approach, feel free to dash me a message through the Contact Form. Thanks!