RubyMotion - custom delegate methods


In this post I'm going to extend the previous post to show how to delegate a button press in a UIView to a UIViewController. The button will simply push another instance of my UIViewController onto stack and is a simple example of a custom delegate method in RubyMotion.

(Click on the link to read my last post about a basic app in RubyMotion)

To start with, my AppDelegate class is exactly the same as before:

class AppDelegate

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

  def navigation_controller
    @navigation_controller ||= UINavigationController.alloc.initWithRootViewController(main_view_controller)
  end

  def main_view_controller
    @main_view_controller ||= MainViewController.alloc.initWithNibName(nil, bundle: nil)
  end

  def application(application, didFinishLaunchingWithOptions:launchOptions)  
    window.rootViewController = navigation_controller    
    window.makeKeyAndVisible

    true
  end

end

Next I'll show the code for my UIView which I've changed a bit from the last post. There is no longer a UILabel and UIView added as subviews but instead a UIButton:

class MainView < UIView

  attr_accessor :delegate

  def initWithFrame(frame)

    super.tap do
      self.backgroundColor = UIColor.whiteColor
      addSubview(button)
    end

  end

  def button
    @button ||= UIButton.new.tap do |button|
      button.setTitle("Say Hello Again", forState: UIControlStateNormal)
      button.setTitleColor(UIColor.blueColor, forState: UIControlStateNormal)

      button.addTarget(self, action: :delegate_button_press, forControlEvents: UIControlEventTouchUpInside)

      button_width = 280
      screen_center = UIScreen.mainScreen.bounds.size.width/2
      button.frame = [[screen_center - button_width/2, 100], [button_width, 30]]
    end
  end

  def delegate_button_press
    if self.delegate.respond_to?("say_hello_again")
      self.delegate.say_hello_again
    end
  end

end

The first thing to notice is that I create getter and setter methods for delegate by using attr_accessor :delegate. Within the button method I set the method for the action that happens when a button is pressed. delegate_button_press will check to see if the view controller for this view responds to a method called say_hello_again and if so then it calls the method. This will make sense when you see the code for the UIViewController:

class MainViewController < UIViewController

  def loadView
    @main_view = MainView.alloc.initWithFrame(CGRectZero).tap do |view|
      view.delegate = self
    end

    self.view = @main_view
  end

  def viewDidLoad
    self.title = 'Hello'
  end

  # delegate method
  def say_hello_again
    main_view_controller = MainViewController.alloc.initWithNibName(nil, bundle: nil)
    self.navigationController.pushViewController(main_view_controller, animated: true)
  end

end

You can see in the loadView method that I have set the delegate of the view to be this UIViewController and then you can see where I have declared the say_hello_again method that the button in the UIView calls.

This is a simple example of how to create a custom delegate method but this is a technique that will be used time and time again so it's really useful to understand how to implement it. Happy delegating!