16 July 2012

This post shows you how to make an app that has the following flow:

  • Enter text your name in an input field.
  • Click a button and an alert box opens up to greet you.

Here’s a video of it

Stuff you need to know

  • I’m assuming that you are comfortable with Ruby.

  • RubyMotion doesn’t support
    • require
    • autoload
    • define_method

    and a few more. So most of your rubygems turnout to be useless, unless you hack them bit.

  • UI elements in iOS have a frame. Think of it as the frame for a photograph on your wall. When you create elements, you have to specify the frame, in the following format:

    [[x, y], [width, height]]

  • You’ll be using the following UI elements

    • UIWindow
    • UITextField
    • UIButton
    • UIAlertView
  • iOS documentation is available here - http://developer.apple.com/library/ios/navigation/

  • Don’t hesitate to copy-paste code to try out things. That is exactly why code snippets are for. Go even further and make your own modifications.

  • I learnt RubyMotion a few hours before making this app and have never written an iOS app as of this blog post. So if you find something can be done in a better way, share your suggestions.

  • This post is in code-commentary style, so read the code too.

Here’s how we do it

This post is a bit lengthy because of the basics. So just hang on and the pastures on your side will turn green. Let’s create a new application using the motion command

$ motion create click_to_greet
    Create click_to_greet
    Create click_to_greet/.gitignore
    Create click_to_greet/Rakefile
    Create click_to_greet/app
    Create click_to_greet/app/app_delegate.rb
    Create click_to_greet/resources
    Create click_to_greet/spec
    Create click_to_greet/spec/main_spec.rb

cd into the click-to-greet directory. All relative paths mentioned from now on will be relative to this directory.

AppDelegate

All your code must go into the app directory. This is how your app/app_delegate.rb looks like when your app is first generated:

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    true
  end
end

The pattern is similar to the MVC pattern. And this app_delegate.rb is like the Rails routes file. It’s used to set the root controller for your application.

This the order of elements in the iOS UI.

UIScreen (the screen)
|
|__UIWindow (the window)
    |
    |__UIViewController
       |
       |__UIView (generally UI elements)

First, create a window for the application. And also set the root controller for it. Right now it’s just some class name and we’ll call it MainViewControler. So your app_delegate.rb should look like the following.

class AppDelegate

  def application(application, didFinishLaunchingWithOptions:launchOptions)

    # Get the frame for the window
    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)

    # Instantiate a new object of the MainViewController
    # and assign it as the root controller.
    @window.rootViewController = MainViewController.new

    # This makes the window a receiver of events (for now we are using touch).
    @window.makeKeyAndVisible

    # Because this method must return true
    true
  end

end

Just like in Ruby, to instantiate an object of a class, you use the Class.new method. In iOS, you have to allocate memory to the object first and then initiate it. In RubyMotion, the Class.new method does the same as Class.alloc.init

iOS provides some helper class methods that allows you to do some tasks in a shorter way. So instead of instantiating an object and then assigning a frame to it like below.

@window = UIWindow.new
@window.frame = UIScreen.mainScreen.bounds

iOS provides the initWithFrame on UI elements. But this must be used with the alloc method, which is why you see:

@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)

UIScreen.mainScreen.bounds returns the frame, that the main screen provides for applications.

MainViewController

Now for the MainViewController, create the file app/main_view_conroller.rb.

# You have to inherit from UIViewController
class MainViewController < UIViewController

  # Called to load the view
  # This is where you instantiate your view and set it as the controller's view.
  def loadView
    # Set the view for the controller
    # We don't need anything special for now. So we'll directly instantiate an object of UIView
    self.view = UIView.new
  end

  # Called after the view is loaded
  def viewDidLoad
    # A text input field instantiated with initWithFrame
    @input_field = UITextField.alloc.initWithFrame([[0, 0], [200, 40]])

    # Set the text color using the UIColor class which offers named colors
    @input_field.textColor = UIColor.blackColor

    # Set the background color for the text field
    @input_field.backgroundColor = UIColor.whiteColor

    # Set the border style of the text field to rounded rectangle
    # We need a rounded border, defined by the constant UITextBorderStyleRoundedRect
    @input_field.setBorderStyle UITextBorderStyleRoundedRect

    # Add the text field to the controller's view
    self.view.addSubview @input_field


    # Initiate button with button type
    @action_button = UIButton.buttonWithType UIButtonTypeRoundedRect

    # Set the title for the default UI state, which is normal
    # Normal UI state is defined by the constant UIControlStateNormal
    @action_button.setTitle "Greet me", forState: UIControlStateNormal

    # Set the frame for the button
    @action_button.frame = [[100, 100], [100, 50]]

    # Add an event for the button when touched
    # 'self' refers to the handler class for the action in which the callback is defined
    # greet_user is the method is that'll be called when the event happens
    # The touch state is defined by the constant UIControlEventTouchUpInside
    @action_button.addTarget(self,
      action: :greet_user,
      forControlEvents: UIControlEventTouchUpInside)

    # Add the button to the view
    self.view.addSubview @action_button
  end

  # The touch callback for the button
  def greet_user
    # Instantiate an alert box with the title and a greeting message
    # And a text for the cancel button
    # Which will say "ok"
    # The arguments from the second to last, are not Ruby hash arguments
    # They are Objective-C style arguments.
    # You can't randomize the order or skip them if they are nil
    @alert_box = UIAlertView.alloc.initWithTitle("Greeting",
        message:"Hi #{@input_field.text}",
        delegate: nil,
        cancelButtonTitle: "ok",
        otherButtonTitles:nil)

    # Show it to the user
    @alert_box.show
  end
end

Run the app

Now that you’ve typed out the stuff required, run the rake command in the project’s directory. The iOS simulator should popup and showcase your app in all glory.

blog comments powered by Disqus