Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all articles
Browse latest Browse all 4370

RubyMotion Tutorial for Beginners: Part 2

$
0
0
Get Started with Ruby Motion!

Get Started with Ruby Motion!

Welcome back to our RubyMotion Tutorial for Beginners series!

In the first part of the series, you learned the basics of getting started with RubyMotion, and created a view controller with a few styled views.

In this second and final part of the series, you will add the rest of the logic to this app, including making the label count down and getting the app fully wrapped up.

Let’s get back in motion! :]

Building a Countdown Timer

At this point, you have a label and a button, and the label has some static text on it. You want the label to count down from 25 minutes.

It’s clear there should be some sort of object responsible for handling the countdown, but you haven’t defined that object yet. While it’s tempting to add that functionality to MainViewController, that’s not really the controller’s responsibility. The controller’s job is to respond to events and direct what should happen next.

Run the following command in Terminal:

mkdir app/models

You’ll use the models directory to store the models containing the application logic of your app.

Run the following command in Terminal to create a new class that will serve as your countdown timer:

touch app/models/pomodoro_timer.rb

Open PomodoroTimer and add the following lines of code:

class PomodoroTimer
 
end

PomodoroTimer has no superclass; it’s just a plain-old-ruby-object, or PORO for short.

The first thing PomodoroTimer needs is an attribute to store its current value. To do this, add the following lines to app/models/pomodoro_timer.rb:

  attr_accessor :count

The attr_accessor macro declares a getter and setter for count. The equivalent to this in Objective-C is:

@interface PomodoroTimer : NSObject
 
@property NSInteger count;
 
@end

Now, add the following method to app/models/pomodoro_timer.rb:

def initialize
  @count = Time.secsIn25Mins
end

By default count should be set to the number of seconds in 25 minutes, so you use the method you’ve just defined on NSDate to set this in initialize.

Weak References

PomodoroTimer also needs a delegate to report when certain events occur.

Add the following code just below the spot where you declared count:

  attr_reader :delegate

You’re using attr_reader here because the default setter for your delegate isn’t appropriate in this case. Using attr_accessor would create a setter that holds the delegate — in this case, an instance of MainViewController in a strongly referenced instance variable. But since you’re going to define PomodoroTimer as a property of MainViewController, using attr_accessor would create a circular dependency leading to memory leaks and crashes!

To avoid that mess, add the following method to pomodoro_timer.rb:

def delegate=(object)
  @delegate = WeakRef.new(object)
end

Here you define your own setter for delegate and set it as a weak reference. In Ruby, everything is an object, and weak references are no exception.

Add the following property to the PomodoroTimer class:

attr_accessor :ns_timer

This property, as the name suggests, will hold an NSTimer object that handles the countdown by firing once a second for 25 minutes.

Add the following method to the PomodoroTimer class next:

def start
  invalidate if ns_timer
  self.ns_timer = NSTimer.timerWithTimeInterval(1, target: self, 
    selector: 'decrement', userInfo: nil, repeats: true)
  NSRunLoop.currentRunLoop.addTimer(ns_timer, 
    forMode: NSDefaultRunLoopMode)
  delegate.pomodoro_timer_did_start(self) if delegate
end

This handles the creation of a new timer. Here’s what’s going on in the code above:

  1. If the PomodoroTimer already has an ns_timer instance, call invalidate.
  2. Set ns_timer to a new NSTimer instance that calls decrement once per second.
  3. Add the NSTimer to the current run loop, and if the delegate has been set then send pomodoro_timer_did_start to the delegate so it’s aware that the timer started.

You’ve yet to define PomodoroTimer#invalidate and PomodoroTimer#decrement.

Add the following below the start method you just wrote:

def invalidate
  ns_timer.invalidate
  delegate.pomodoro_timer_did_invalidate(self) if delegate
end

This method simply passes invalidate on to ns_timer and then notifies the delegate that the timer has been invalidated as long as a delegate has been set.

Finally, define the decrement method as follows:

private
 
def decrement
  self.count -= 1
  return if delegate.nil?
  if count > 0
    delegate.pomodoro_timer_did_decrement(self)
  else
    delegate.pomodoro_timer_did_finish(self)
  end
end

This simple method decrements the value of count by 1 each time it’s called. If there’s a delegate present and the count is greater than 0, it notifies the delegate that pomodoro_timer_did_decrement. If the count is 0 then it notifies the delegate that pomodoro_timer_did_finish.

Note the private directive above; since decrement should only be used internally within the class itself, you make this method private by adding the directive above the class definition.

Run rake to build and launch your app; you can now play around with the new class you defined above. To do this, execute the following command in Terminal (with rake and the Simulator active) to initialize a new PomodoroTimer and assign it to a local variable:

p = PomodoroTimer.new

Inspect the value of p.count using the commands below:

p.count

The value should be 10 as expected:

# => 10

Call start on p to start the countdown sequence as follows:

p.start

To see the countdown timer working, evaluate p.count repeatedly — but don’t wait, you only have 10 seconds! :]

p.count
# => 8
p.count
# => 6
p.count
# => 2

Now that you know your timer is working, you can use it in your app.

Adding a PomodoroTimer to MainViewController

Open main_view_controller.rb and declare the following property on MainViewController:

class MainViewController < UIViewController
 
  attr_accessor :pomodoro_timer
 
  # ...
end

This holds the timer instance for this controller.

In the first part of this tutorial series, you added nextResponder to MainView as the target for touch actions, with the action name of timer_button_tapped. It’s finally time to define that method.

Still in main_view_controller.rb, add the following code below loadView:

def timer_button_tapped(sender)
  if pomodoro_timer && pomodoro_timer.valid?
    pomodoro_timer.invalidate      
  else
    start_new_pomodoro_timer
  end
end

You call the above action when the user taps the timer_button. If pomodoro_timer has a value — i.e. is not nil — and it references a valid PomodoroTimer, then invalidate the PomodoroTimer. Otherwise, create a new PomodoroTimer instance.

Add the private directive just below the method you just added as shown below:

# ...
 
def timer_button_tapped(sender)
  if pomodoro_timer && pomodoro_timer.valid?
    pomodoro_timer.invalidate      
  else
    start_new_pomodoro_timer
  end
end
 
private
 
# ...

This separates the public and private methods.

Finally, add the following method after the private directive:

def start_new_pomodoro_timer
  self.pomodoro_timer = PomodoroTimer.new
  pomodoro_timer.delegate = self
  pomodoro_timer.start
end

start_new_pomodoro_timer assigns a new PomodoroTimer instance to pomodoro_timer, sets its delegate to self, and then starts the timer. Remember, tapping the button calls this method to you need to start the countdown as well.

Run rake to build and launch your app, then tap the Start Timer button to see what happens:

2014-09-11 16:40:58.276 Pomotion[17757:70b] *** Terminating app due to uncaught exception 'NoMethodError', reason: 'pomodoro_timer.rb:22:in `start': undefined method `pomodoro_timer_did_start' for #<MainViewController:0x93780a0> (NoMethodError)

Hmm, something’s wrong with your app. Can you guess what the problem is?

When you start pomodoro_timer, it calls delegate methods on MainViewController — but those methods don’t yet exist. In Ruby, this results in a NoMethodError exception.

Add the following delegate methods above the private keyword in main_view_controller.rb:

def pomodoro_timer_did_start(pomodoro_timer)
  NSLog("pomodoro_timer_did_start")
end
 
def pomodoro_timer_did_invalidate(pomodoro_timer)
  NSLog("pomodoro_timer_did_invalidate")
end
 
def pomodoro_timer_did_decrement(pomodoro_timer)
  NSLog("pomodoro_timer_did_decrement")
end
 
def pomodoro_timer_did_finish(pomodoro_timer)
  NSLog("pomodoro_timer_did_finish")    
end

The NSLog statements will print out a line to the console, just to show you that the methods are in fact being called.

Run rake once again and tap Start Timer; you should see the NSLog statements written out to the console as they’re called:

     Build ./build/iPhoneSimulator-8.1-Development
     Build vendor/PixateFreestyle.framework
     Build vendor/NSDate+SecsIn25Mins
   Compile ./app/controllers/main_view_controller.rb
      Link ./build/iPhoneSimulator-8.1-Development/Pomotion.app/Pomotion
    Create ./build/iPhoneSimulator-8.1-Development/Pomotion.app/Info.plist
(main)> 2014-11-13 13:52:44.778 Pomotion[9078:381797] pomodoro_timer_did_start
2014-11-13 13:52:45.779 Pomotion[9078:381797] pomodoro_timer_did_decrement
2014-11-13 13:52:46.779 Pomotion[9078:381797] pomodoro_timer_did_decrement
2014-11-13 13:52:47.779 Pomotion[9078:381797] pomodoro_timer_did_decrement
2014-11-13 13:52:48.779 Pomotion[9078:381797] pomodoro_timer_did_decrement
(nil)? 2014-11-13 13:52:49.778 Pomotion[9078:381797] pomodoro_timer_did_decrement
2014-11-13 13:52:50.778 Pomotion[9078:381797] pomodoro_timer_did_decrement
(nil)? 2014-11-13 13:52:51.778 Pomotion[9078:381797] pomodoro_timer_did_decrement

If you still get an exception, make sure you followed the instructions above about pasting the methods before the private keyword.

There’s just one more bit of housekeeping before moving on. In timer_button_tapped you ask if pomodoro_timer is valid?, but you haven’t yet defined a valid? method on PomodoroTimer; if you tap the button twice RubyMotion will throw a NoMethodError.

Add the following code just beneath start in pomodoro_timer.rb:

def valid?
  ns_timer && ns_timer.valid?
end

In this case, a valid result means that the PomodoroTimer has an NSTimer and that the timer is valid. Ensure you’ve added this method above the private directive, so that you can call this method on any instance of PomodoroTimer from within other objects.

Updating the Button

In MainView you specified that timer_button should have a different title if the state is UIControlStateSelected, but you aren’t setting that state yet. The button should become selected when the timer starts, and go back to the normal state when the timer is stopped or invalidated for any reason.

Add the following method to main_view_controller.rb:

def timer_button
  view.timer_button
end

This is just a helpful wrapper method that calls timer_button on the view. While it may seem arbitrary to do so, this actually helps the code adhere to The Law Of Demeter.

Replace the pomodoro_timer_did_start and pomodoro_timer_did_invalidate delegate methods with the following:

def pomodoro_timer_did_start(pomodoro_timer)
  NSLog("pomodoro_timer_did_start")
  timer_button.selected = true
end
 
def pomodoro_timer_did_invalidate(pomodoro_timer)
  NSLog("pomodoro_timer_did_invalidate")
  timer_button.selected = false  
end

Run rake, then tap Start Timer; you’ll see the button’s title change as shown below:

Interrupt Button

Updating the Label

The countdown timer isn’t terribly useful at present since the on-screen timer doesn’t count down yet. Fortunately, that’s your very next task!! :]

Add the following method to app/controllers/main_view_controller.rb:

def timer_label
  view.timer_label
end

This updates timer_label in MainView should be updated with the current count of pomodoro_timer each time the timer decrements.

Still in main_view_controller.rb, modify pomodoro_timer_did_decrement to look like the code below:

def pomodoro_timer_did_decrement(pomodoro_timer)
  mins = pomodoro_timer.count / 60
  secs = pomodoro_timer.count % 60
  timer_label.text = "%02d:%02d" % [mins, secs]
end

Here you take the value of pomodoro_timer.count and break it down into separate minutes and seconds values. You then set the text of timer_label to a formatted string so that the minutes and seconds values will always appear as double digits.

Run rake again and tap Start Timer; you should see the timer count down from 00:10 to 00:00 as shown below:

RW_Rubymotion_TimerLabel

It looks good, but watch the console carefully and you’ll see the timer continues to decrement past zero and pomodoro_timer_did_finish executes multiple times.

(main)> 2014-11-13 13:57:37.038 Pomotion[9408:386412] pomodoro_timer_did_start
2014-11-13 13:57:47.039 Pomotion[9408:386412] pomodoro_timer_did_finish
2014-11-13 13:57:48.038 Pomotion[9408:386412] pomodoro_timer_did_finish
2014-11-13 13:57:49.038 Pomotion[9408:386412] pomodoro_timer_did_finish

Ah — you’re not invalidating the timer. To fix this, modify pomodoro_timer_did_finish like so:

def pomodoro_timer_did_finish(pomodoro_timer)
  pomodoro_timer.invalidate
end

Now when the timer reaches zero, you’ll invalidate it from within MainViewController.

Run rake and try the above scenario again; verify that the counter stops decrementing at zero and pomodoro_timer_did_finish is called just once.

Another small issue with the timer right now is that timer_label only changes when the timer has counted down from 10 to 9; the user doesn’t get to see the initial value of 10 seconds.

To solve that, you’ll rewrite a bit of code to set the label as soon as the controller receives pomodoro_timer_did_start.

Still in main_view_controller.rb define a new private method named update_timer_label and move the code from pomodoro_timer_did_decrement into the new method as follows:

def pomodoro_timer_did_decrement(pomodoro_timer)
end
 
# ...
private
 
def update_timer_label
  mins = pomodoro_timer.count / 60
  secs = pomodoro_timer.count % 60
  timer_label.text = "%02d:%02d" % [mins, secs]
end
# ...

Now edit pomodoro_timer_did_decrement, pomodoro_timer_did_invalidate, and pomodoro_timer_did_start so they call update_timer_label as shown below:

# ...
 
def pomodoro_timer_did_start(pomodoro_timer)
  timer_button.selected = true
  update_timer_label
end
 
def pomodoro_timer_did_invalidate(pomodoro_timer)
  timer_button.selected = false
  update_timer_label
end
 
def pomodoro_timer_did_decrement(pomodoro_timer)
  update_timer_label
end
 
# ...

Run rake then tap Start Timer to see that the timer now starts from 00:10:

StartAt10

Making it Look the Part

The timer is working well, but it could do with a bit of dressing up.

Add the following code to app/controllers/main_view_controller.rb, just below loadView:

def viewDidLoad
  super
  self.title = "Pomotion"
end

Run rake and you should see the title appear at the top of the screen:

RW_Rubymotion_Title

That adds a bit of life to the app, but the navigation bar could use a face lift.

Add the following CSS to resources/default.css to brighten up the navigation bar:

navigation-bar {
  background-color: #7F0000;
}
navigation-bar title {
  color: white;
}

These two CSS values give the navigation bar a red tint,and color the title bar text white.

Run rake to see the results:

RW_Rubymotion_Navbar

Extending Core Classes

You can give your app a professional touch by gradually changing the color of the timer’s label from green to red as the timer counts down to 00:00.

To achieve this, you’ll need to create a method that will take two colors and mix them based on a specified proportion. Since this behavior is relevant to UIColor, you should add your new method as a class method there. However, this time around you can extend the class the Ruby way! :]

First, create a new directory under app called core_extensions:

mkdir app/core_extensions

This is a sensible place to define all of the extensions you add to the core iOS and Ruby classes.

Create a file in that directory named ui_color.rb:

touch app/core_extensions/ui_color.rb

The basic design of this effect is that timer_label can have one of four color combinations: all red, all green, a red-green mix, or grey (which will be used when the timer is inactive).

Add the following helper class methods to the UIColor class in app/core_extensions/ui_color.rb:

class UIColor
 
  def self.pomo_grey_color
    @pomo_grey_color ||= UIColor.colorWithRed(0.5, green: 0.5, blue: 0.5, alpha: 1.0)
  end
 
  def self.pomo_green_color
    @pomo_green_color ||= UIColor.colorWithRed(0.0, green: 0.666, blue: 0.0, alpha: 1.0)
  end
 
  def self.pomo_red_color
    @pomo_red_color ||= UIColor.colorWithRed(0.666, green: 0.0, blue: 0.0, alpha: 1.0)
  end
 
end

This makes it easier to reference the custom red, green and grey colors in Pomotion’s color scheme.

Now you need to define a class method that will mix red and green proportionally to return a new UIColor value.

First, define the following method below self.pomo_red_color:

def self.new_from_two_colors(color_1, color_2, proportion)
  # 1
  color_1_r = Pointer.new(:float)
  color_1_g = Pointer.new(:float)
  color_1_b = Pointer.new(:float)
  color_1_a = Pointer.new(:float)
 
  # 2
  color_1.getRed(color_1_r, green: color_1_g, blue: color_1_b, alpha: color_1_a)
 
  # 3
  color_2_r = Pointer.new(:float)
  color_2_g = Pointer.new(:float)
  color_2_b = Pointer.new(:float)
  color_2_a = Pointer.new(:float)
  color_2.getRed(color_2_r, green: color_2_g, blue: color_2_b, alpha: color_2_a)
 
  # 4
  new_red   = color_1_r.value + (color_2_r.value - color_1_r.value) * proportion
 
  # 5
  new_green = color_1_g.value + (color_2_g.value - color_1_g.value) * proportion
  new_blue  = color_1_b.value + (color_2_b.value - color_1_b.value) * proportion
  new_alpha = color_1_a.value + (color_2_a.value - color_1_a.value) * proportion
 
  # 6
  UIColor.colorWithRed(new_red, green: new_green, blue: new_blue, alpha: new_alpha)
end

Taking each commented section in turn, you’ll see the following:

  1. First, you initialize four Pointer objects. Pointers are handled quite differently in RubyMotion than in Objective-C — in Ruby, everything is an object and pointers are no exception. To create a new Pointer, simply initialize a new instance of the Pointer class and pass in the type as a parameter. The type can be either an Objective-C Runtime Type, or a Ruby symbol with the type name. To access the pointer’s value you simply call the value property.
  2. Next, you call the UIColor method getRed:green:blue:alpha to pluck the RGBA values for color_1 and then assign them to their respective pointers.
  3. Now do the same for color_2.
  4. The proportion parameter is a float value between 0 and 1. You can think of this as “What percentage of color_2 do we mix into color_1?”

    The following calculation determines how much red the new color should contain:

    color_1_r.value + (color_2_r.value - color_1_r.value) * proportion

    Here’s some real numbers to help make sense of the calculation above. If color_1 is bright red (RGBA: 255, 0, 0, 1) and color_2 is pure white (RGBA: 0, 0, 0, 1), then color_1_r and color_2_r will be 255 and 0 respectively. The color that’s a 50% blend of color_1 and color_2 will be 255 + (0 - 255) * 0.5 = 127.5.

  5. Perform the same calculation for the green, blue and alpha values.
  6. Finally, return a new UIColor object with the new, proportional values.

Run rake and test your methods from Terminal as below:

UIColor.pomo_red_color
# => #<UIDeviceRGBColor:0x9099e10>
UIColor.pomo_green_color
# => #<UIDeviceRGBColor:0x97b59c0>
UIColor.pomo_grey_color
# => #<UIDeviceRGBColor:0x94ed360>

That looks good — but what about new_from_two_colors?

UIColor.new_from_two_colors(UIColor.pomo_red_color, UIColor.pomo_green_color, 0.5)
# => #<UIDeviceRGBColor:0x909a510>

If you can stand to do the hexadecimal math yourself, you’ll see that the numbers above work out correctly. :]

Back in main_view_controller.rb, add the following lines to the bottom of update_timer_label to change the text color of timer_label as the timer decrements:

if pomodoro_timer.count > 0
  proportion = pomodoro_timer.count / Time.secsIn25Mins.to_f
  color = UIColor.new_from_two_colors(UIColor.pomo_red_color, UIColor.pomo_green_color, 
    proportion)
else
  color = UIColor.pomo_grey_color
end
timer_label.textColor = color

To determine the color above, you first check that pomodoro_timer‘s count is greater than zero; if so, then calculate the color proportion as the result of the time remaining divided by the total starting time.

Then assign color, which is a proportionate mixture of UIColor.pomo_red_color, and UIColor.pomo_green_color, to a new instance ofUIColor.

Finally, set textColor of timer_label to the new color value.

Note: When doing integer division in Ruby, the result is always rounded to the nearest full integer. So 4 / 5 would return 0, while 6 / 5 would return 1. To perform float division instead, simply convert the denominator to a float.

Run rake and tap the Start Timer button to see how your effect looks:

RW_Rubymotion_Green

RW_Rubymotion_Redish

Hey — that adds a lot of polish to your app! :] However, update_timer_label is starting to get a little messy and, strictly speaking, it shouldn’t be up to the controller to calculate the text color.

You can make this code a lot neater, and more MVC compliant, by extending the behavior of timer_label through defining a subclass of UILabel.

Create a new file in app/views and name it timer_label.rb using Terminal:

touch app/views/timer_label.rb

Open timer_label.rb and declare a new class TimerLabelas a subclass of UILabel:

class TimerLabel < UILabel
 
end

Your goal is to extract all of the logic from MainViewController that updates the timer_label, and instead place it in a custom method in TimerLabel. This makes the controller code much easier to follow, easier to maintain — and easier to test.

Still in app/views/timer_label.rb, add the following code to the class implementation:

def update_for_count(count)
  update_text_for_count(count)
  update_color_for_count(count)
end
 
 
private
 
def update_text_for_count(count)
  mins = count / 60
  secs = count % 60
  self.text = "%02d:%02d" % [mins, secs]
end
 
def update_color_for_count(count)
  if count > 0
    proportion = count / Time.secsIn25Mins.to_f
    color = UIColor.new_from_two_colors(UIColor.pomo_red_color, UIColor.pomo_green_color, proportion)
  else
    color = UIColor.pomo_grey_color
  end
  self.color = color
end

This defines a new public method for timer_labelupdate_for_count — which takes the count value from a PomodoroTimer object and calls two private methods: update_text_for_count and update_color_for_count.

This is the exact same update_timer_label code from MainViewController; the only difference is that you set the properties on self, instead of on update_timer_label.

This should help you appreciate how much simpler Ruby code can be, when compared to Objective-C.

Modify the implementation of main_view.rb to use your new TimerLabel class instead of UILabel as shown below:

class MainView < UIView
  # ...
 
  def timer_label
    @timer_label ||= TimerLabel.alloc.initWithFrame(CGRectZero).tap do |label|
      label.styleId = 'timer_label'
      label.text    = '00:00'
    end
  end
 
  # ...
end

Next, update update_timer_label in main_view_controller.rb to use the update_for_count method you defined above:

def update_timer_label
  timer_label.update_for_count(pomodoro_timer.count)
end

Run rake to build and launch your app; test your app to make sure it works as before. The changes you’ve made shouldn’t have any impact on the appearance or performance of your app, but should make the code easier to maintain in the future!

Updating the Start Timer Button

The title of timer_button changes depending on the state of the button. It would be nice to polish your app a little further and change the background color of the button when it’s selected.

To add this custom behavior, you’ll create a new subclass of UIButton that can indicate a selected state. Can you guess where this new custom view class should go?

If you said “the app/views directory”, you’d be right!

Create the following file using Terminal:

touch app/views/selectable_button.rb

Open selectable_button.rb add the following code:

class SelectableButton < UIButton
 
  def selected=(bool)
    super.tap do
      self.backgroundColor = bool ? UIColor.pomo_red_color : UIColor.pomo_green_color
    end
  end
 
end

In the above code, selected= takes a boolean parameter, calls super's selected= method, then sets backgroundColor to red if bool is true, and to green if it’s false.

Note: You’re calling super here, but there’s no method named selected= defined in UIButton. What’s going on?

Naming setters as set[Something] is a C-style idiom. In Ruby, the preference is to name setters like [something]= instead. RubyMotion cleverly offers support for both idioms and aliases them as well so you can choose to use one or the other as you see fit.

Ruby also offers method aliases in the form of [something]? for methods named is[Something].

Modify app/views/main_view.rb to use your new SelectableButton class instead of UIButton as shown below:

def timer_button
  @timer_button ||= SelectableButton.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

Run rake to build and launch your app; tap Start Timer and you’ll see the button background turn to red like so:

RW_Rubymotion_RedButton

Once the timer reaches zero, the button background returns to green.

Alerting the User

Your app is really starting to take shape — but there’s a little more functionality to implement. One of the key principles of the Pomodoro Technique is that you should take a five minute break after your 25 minute block of work is over. You should suggest this to your user so they don’t slip back into their old habits. UIAlertController is the perfect tool for this.

Add the following property method to main_view_controller.rb:

def alert_controller
  @alert_controller ||= UIAlertController.alertControllerWithTitle("Pomodoro Complete!",
    message: "Time to take a short break.",
    preferredStyle: UIAlertControllerStyleAlert).tap do |alert|
      ok_action = UIAlertAction.actionWithTitle("OK",
        style:UIAlertActionStyleDefault,
        handler: nil)
      alert.addAction(ok_action)
  end
end

You’ll want to show this alert once the timer has finished counting down to zero, so pomodoro_timer_did_finish would be a perfect place to call this method.

Add the following line to pomodoro_timer_did_finish:

self.presentViewController(alert_controller, animated: TRUE, completion: nil)

Run rake to build and launch your app, then tap Start Timer; wait ten seconds and you should see your new alert controller make an appearance like so:

RW_Rubymotion_Alert

And that’s it — you’ve completed your first RubyMotion application!

Where to Go From Here?

Hopefully you enjoyed working through this tutorial, and can appreciate the value RubyMotion provides, not just in time saved, but also in the emotional and mental energy you save by working with a friendlier, easier to use syntax.

If you would like to view the completed project, you can download a copy of it here.

To build and launch your RubyMotion app on a physical device, you can use the rake device command which you can learn more about here.

There are loads of great RubyMotion resources online; I’d recommend you check out RubyMotion Dispatch – A weekly newsletter as well as the developer documentation on the RubyMotion website.

If you have any questions or comments, feel free to join the discussion below!

RubyMotion Tutorial for Beginners: Part 2 is a post from: Ray Wenderlich

The post RubyMotion Tutorial for Beginners: Part 2 appeared first on Ray Wenderlich.


Viewing all articles
Browse latest Browse all 4370

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>