RubyMotion Tutorial for Beginners: Part 1
In this RubyMotion Tutorial for beginners, you’ll learn how to make a simple Pomodoro app for the iPhone. By Gavin Morrice.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
RubyMotion Tutorial for Beginners: Part 1
25 mins
- Getting Started
- Differentiating RubyMotion vs. Ruby
- Introducing Pomotion
- Gemfile
- Rakefile
- Overriding Configuration Options
- The “app” Directory
- The “build” Directory
- The “resources” Directory
- The “spec” Directory
- Hello World Example
- Adding a Main View Controller
- Pixate Freestyle
- Adding Views Programmatically
- Getting the Timer to Count Down
- Importing C Code into Your Project
- Where To Go From Here?
Pixate Freestyle
To build your view you'll use Pixate Freestyle. Pixate, lets you style iOS views using CSS; it's just like styling a webpage.
To install Pixate, open Gemfile in your text editor and add the following line to the bottom:
gem 'motion-pixatefreestyle'
Run the following command in Terminal:
bundle
This updates the Gemfile.lock and installs any missing gems.
Next, create the following directory in your project:
mkdir vendor
Download the latest release of the Pixate Freestyle framework; unzip it and move PixateFreestyle.framework into your newly created vendor folder.
Finally, add the following line to your Rakefile, just before the closing end
:
app.pixatefreestyle.framework = 'vendor/PixateFreestyle.framework'
This simply tells motion-pixatefreestyle
where to find the Pixate framework.
Run rake
to check that everything installed properly; you should see the following:
Build ./build/iPhoneSimulator-8.1-Development
Build vendor/PixateFreestyle.framework
Compile ./app/pixatefreestyle_code.rb
Link ./build/iPhoneSimulator-8.1-Development/Pomotion.app/Pomotion
Create ./build/iPhoneSimulator-8.1-Development/Pomotion.app/PkgInfo
Create ./build/iPhoneSimulator-8.1-Development/Pomotion.app/Info.plist
Create ./build/iPhoneSimulator-8.1-Development/Pomotion.app.dSYM
Simulate ./build/iPhoneSimulator-8.1-Development/Pomotion.app
It's time to dress up the main screen so that it looks presentable.
Adding Views Programmatically
Just like controllers, views should have a directory of their own within the app directory.
Execute the following command in Terminal:
mkdir app/views
Next, execute the following command to create a file to contain the MainView of MainViewController:
touch app/views/main_view.rb
Open main_view.rb and add the following code:
class MainView < UIView
def initWithFrame(frame)
super.tap do
self.styleId = 'main_view'
end
end
end
MainView
is a custom view for the MainViewController which inherits from UIView. Although they share the same name prefix of "Main", this isn't a requirement in RubyMotion; it's just a nice convention to keep things organized in your app.
Pixate Freestyle adds two properties to all UIView subclasses: styleId
and styleClass
. These let you apply styles to any view. In the code above you set styleId
on MainView when you override initWithFrame
.
Note: tap
was introduced in Ruby 1.9; it lets you call methods on an object within a block and returns that object at the end of the block's execution. This comes in really handy with things like initializers in iOS, which should always return self
...which you always remember to do, right? :]
It's a heck of a lot easier than writing the following:
Note: tap
was introduced in Ruby 1.9; it lets you call methods on an object within a block and returns that object at the end of the block's execution. This comes in really handy with things like initializers in iOS, which should always return self
...which you always remember to do, right? :]
It's a heck of a lot easier than writing the following:
def initWithFrame(frame)
super
self.styleId = 'main_view'
return self
end
def initWithFrame(frame)
super
self.styleId = 'main_view'
return self
end
You can now move on to styling MainView using simple CSS!
Add a new CSS stylesheet to the resources directory named default.css as follows:
touch resources/default.css
And add the following code to the new stylesheet:
#main_view {
background-color: white;
}
This is some simple CSS that sets the background color to white for the #main_view id, which will apply to the main view you just created.
Open app/main_view_controller.rb and add the following method to the implementation of MainViewController
:
def loadView
self.view = MainView.alloc.initWithFrame(CGRectZero)
end
This should look familiar; you're re-defining loadView
in the view controller to load a custom view instance. In this case, the frame argument is CGRectZero, which sets the width and height of the view to 0.
Run rake
to see your view in action; MainViewController
now has a white background:
Next you need a label on the screen to show the timer countdown as well as a button to start and stop the timer.
Open app/views/main_view.rb and add the following method:
def timer_label
@timer_label ||= UILabel.alloc.initWithFrame(CGRectZero).tap do |label|
label.styleId = 'timer_label'
label.text = '00:00'
end
end
Just as before, you've defined a getterfor the label and you've used a memoization pattern to cache the UILabel
in an instance variable. The label has a CSS style ID of timer_label
and its text shows 00:00 — just like a real timer set to zero.
You set the frame to CGRectZero upon initialization since you'll use using Pixate and CSS to style the label including setting its size and origin.
While you're at it, you should add the timer button to your view as well.
Add the following code to app/views/main_view.rb:
def timer_button
@timer_button ||= UIButton.buttonWithType(UIButtonTypeCustom).tap do |button|
button.styleId = 'timer_button'
button.setTitle('Start Timer', forState: UIControlStateNormal)
button.setTitle("Interrupt!" , forState: UIControlStateSelected)
button.addTarget(nextResponder, action: 'timer_button_tapped:',
forControlEvents: UIControlEventTouchUpInside)
end
end
In the above method you've set several titles for UIControlStateSelected
; you'll be using the selected
state shortly. nextResponder
is the target object that responds to tap events, which in this case is the MainViewController displaying the view.
You could have just as easily defined a timer_button_tapped:
action in MainView but, strictly speaking, that would have been a violation of MVC. Since it's the controller's job to respond to user input, that's where you should define the action.
Still in app/views/main_view.rb, modify initWithFrame
as follows:
def initWithFrame(frame)
super.tap do
self.styleId = 'main_view'
addSubview(timer_label)
addSubview(timer_button)
end
ends
Here you add the label and button views you just created as subviews of MainView
.
Finally, add the following styles to resources/default.css:
#timer_label {
top: 160px;
left: 60px;
width: 200px;
height: 60px;
font-size: 60px;
text-align: center;
color: #7F7F7F;
}
#timer_button {
top: 230px;
left: 60px;
width: 200px;
height: 40px;
color: white;
background-color: #007F00;
}
This styles the two views you added previously.
Run rake
to launch your app; your app should now look like the following:
Things are starting to look pretty nice! The timer is a bit static — your next job is to get it to count down.
Getting the Timer to Count Down
The Pomodoro Technique states that work should be accomplished in 25 minute blocks, so that's where your timer value will start.
This value is quite important to the application's logic, and might be referenced in more than one place; therefore it makes sense to write a helper method for NSDate so you don't have to hardcode time calculations throughout your code.