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.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

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.

Note: If you encounter issues running version 2.1.4 with motion-pixatefreestyle 2.1, you might try using version 2.1.3 of the framework instead.

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:

RW_Rubymotion_WhiteBG

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:

RW_Rubymotion_Timer1

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.

Gavin Morrice

Contributors

Gavin Morrice

Author

Over 300 content creators. Join our team.