RedPotion uses ProMotion's screens.

Screens Navigation Bars Tab Bars
ProMotion Screen ProMotion Nav Bar ProMotion Tabs
Table Screens Grouped Tables Searchable Refreshable
ProMotion TableScreen Grouped Table Screen Searchable Refreshable
SplitScreens Map Screens Web Screens
ProMotion SplitScreens MapScreen ProMotion WebScreen

...and much more.


PM::Screen is the primary object in ProMotion and a subclass of UIViewController. It adds some abstraction to make your life easier while still allowing the full power of a UIViewController.

class HomeScreen < PM::Screen
  title "Home"
  tab_bar_item item: "home-screen-tab", title: "Home"
  status_bar :light
  stylesheet HomeScreenStylesheet

  def on_load
    set_nav_bar_button :right, title: "Next", action: :go_to_next

    # set up subviews here
    append UIView, :some_style
  end

  # custom method, triggered by tapping right nav bar button set above
  def go_to_next
    open NextScreen # opens in same navigation controller, assuming we're in one
  end

  def will_appear
    # just before the view appears
  end

  def on_appear
    # just after the view appears
  end

  def will_disappear
    # just before the view disappears
  end

  def on_disappear
    # just after the view disappears
  end

end

Lifecycle Methods

All lifecycle methods are optional, but provide hooks for you to do certain tasks. These usually coincide with Cocoa Touch lifecycle methods but have slightly different semantics.

In ProMotion, will_* methods usually fire before an action, and on_* methods fire just after.

on_init

Fires only once, after the screen has been instantiated and all provided properties set. A good place to do further initialization of instance variables or set your tab bar icon.

def on_init
  @my_car = "Dude, where's my car?"
  set_tab_bar_item item: :favorites
end

screen_setup

Primarily used for setting up reusable PM::Screen subclasses, such as PM::WebScreen. Not recommended for normal app screens. But if you are building a subclass for a gem, this is where you would do your additional setup.

class PM::LaserScreen < UILaserViewController
  include PM::ScreenModule

  def screen_setup
    self.laserView = set_up_view
  end
end

load_view

Used for creating your screen's root view. If you don't implement this method or if you do and you fail to create a view, ProMotion will create a blank one for you.

Only fires when the view property is accessed for the first time.

def load_view
  self.view = UIView.alloc.initWithFrame(UIScreen.mainScreen.bounds)
end

on_load

Fires once, when the root view of the screen is accessed for the first time. This is where you normally add and style your subviews.

Keep in mind this method doesn't necessarily fire right away. For example, if you're creating a tab bar with four screens, the three screens that are not active will not fire on_load until you tap their tab. That's because the view isn't accessed until that moment.

def on_load
end

will_appear

Runs just before the screen appears. Often used to ensure the right information is displayed.

def will_appear
  @name.text = @user.name
end

will_present

Runs just before the screen is pushed onto the navigation controller. Usually fires immediately after will_appear, but only if it is the first time that the screen is added to the navigation controller.

Not used all that often, but can be useful in some cases.

def will_present
  # About to present
end

on_appear

Runs just after the screen has appeared and stopped animating. Sometimes used to kick off other animations or start playing a video.

def on_appear
  @video.startPlaying
end

on_present

Runs just after the screen is pushed onto the navigation controller. Usually fires just after on_appear, but only when the screen is first added to the navigation controller.

def on_present
  # Presented
end

will_disappear

Runs just before the screen disappears from the screen. An example usage would be to stop a video from playing.

def will_disappear
  @video.stopPlaying
end

will_dismiss

Runs just before the screen is removed from its parent. Usually happens when getting popped off a navigation controller stack. Fires right after will_disappear.

def will_dismiss
  # dismissing
end

on_dismiss

Runs just after the screen is removed from its parent.

def on_dismiss
  # dismissed, screen is about to be deallocated probably
end

should_autorotate

(iOS 6+) return true/false if screen should autorotate. Defaults to true.

def should_autorotate
  false
end

should_rotate(orientation)

(iOS 5) Return true/false for rotation to orientation. Tries to resolve this automatically from your UISupportedInterfaceOrientations setting. You normally do not override this method.

def should_rotate(orientation)
  if device.portrait?
    true
  else
    false
  end
end

will_rotate(orientation, duration)

Runs just before the device is rotated.

def will_rotate(orientation, duration)
  # about to rotate
end

on_rotate

Runs just after the device is rotated.

def on_rotate
  # we've rotated
end

Methods

try(method, *args)

Sends method(*args) to the current screen if the current screen will respond_to?(method)

Returns if the screen was opened in a modal window.

m = ModalScreen.new
open_modal m
m.modal? # => true

Returns if the screen is currently contained in a navigation controller.

open s = HomeScreen.new(nav_bar: true)
s.nav_bar? # => true

will_rotate(orientation, duration)

Runs just before the screen rotates.

set_nav_bar_button(side, args = {})

Set a nav bar button. args can be image:, title:, system_item:, button:, custom_view:.

You can also set arbitrary attributes in the hash and they'll be applied to the button.

set_nav_bar_button :left, {
  title: "Button Title",
  image: UIImage.imageNamed("left-nav"),
  system_item: :reply,
  tint_color: UIColor.blueColor,
  button: UIBarButtonItem.initWithTitle("My button", style: UIBarButtonItemStyleBordered, target: self, action: :tapped_button) # for custom button
}

system_item can be a UIBarButtonSystemItem or one of the following symbols:

:done,:cancel,:edit,:save,:add,:flexible_space,:fixed_space,:compose,
:reply,:action,:organize,:bookmarks,:search,:refresh,:stop,:camera,
:trash,:play,:pause,:rewind,:fast_forward,:undo,:redo,:page_curl

custom_view can be any custom UIView subclass you initialize yourself

Another example with arbitrary attributes:

set_nav_bar_button :right, {
  system_item: :add,
  action: :add_item,
  accessibility_label: "add item",
  background_color: image.resource("some-image")
}

And finally, you can also set the :back nav bar image on a screen and it will render a back arrow icon in the upper left part of the navigation. However, note that doing so will change the back button for all descendants of the screen you set the button on. This behavior is a little unintuitive, but is a result of the underlying Cocoa Touch APIs. For example:

class MyScreen < PM::Screen
  def on_init
    set_nav_bar_button :back, title: 'Cancel', style: :plain, action: :back
  end

  def go_to_next_screen
    open MyScreenChild
  end
end

The code above will add a "cancel" back button to MyScreenChild when it is opened as a descendant of MyScreen.

set_nav_bar_buttons(side, button_array)

Allows you to set multiple buttons on one side of the nav bar with a single method call. The second parameter should be an array of any mixture of UIBarButtonItem instances and hash constructors used in set_nav_bar_button

set_nav_bar_buttons :right, [{
  custom_view: my_custom_view_button
},{
  title: "Tasks",
  image: image.resource("whatever"),
  action: nil
}]

set_toolbar_items(buttons = [], animated = true)

Uses an array to set the navigation controllers toolbar items and shows the toolbar. Uses the same hash formatted parameters as set_nav_bar_button. When calling this method, the toolbar will automatically be shown (even if the screen was created without a toolbar). Use the animated parameter to specify if the toolbar showing should be animated or not.

# Will arrange one button on the left and another on the right
set_toolbar_items [{
    title: "Button Title",
    action: :some_action
  }, {
    system_item: :flexible_space
  }, {
    title: "Another ButtonTitle",
    action: :some_other_action,
    target: some_other_object
  }]

You can also pass your own initialized UIBarButtonItem as part of the array (instead of a hash object).

EDGE FEATURE: You can pass tint_color with a UIColor to change the tint color of the button item.

title

Returns title of current screen.

class MyScreen < PM::Screen
  title "Mine"
  def on_load
    self.title # => "Mine"
  end

title=(title)

Sets the text title of current screen instance. Note that you must declare self as the receiver.

class SomeScreen
  def on_load
    # This sets this instance's title
    self.title = "Something else"
  end
end

close(args = {})

Closes the current screen, passes args back to the previous screen's on_return method (if it exists).

class ChildScreen < PM::Screen
  def save_and_close
    save
    close({ saved: true })
  end
end

class ParentScreen < PM::Screen
  def on_return(args={})
    if args[:saved]
      reload_data
    end
  end
end

If you want to close back to the root screen or any other screen in the navigation stack, use to_screen::

class ChildScreen < PM::Screen
  def close_to_root
    close to_screen: self.navigation_controller.viewControllers.first
    # For the rootViewController, you can just use `:root`
    close to_screen: :root
  end
end

open_root_screen(screen)

Closes all other open screens and opens screen as the root view controller.

def reset_this_app
  open_root_screen HomeScreen
end

open(screen, args = {})

Opens a screen, intelligently determining the context.

Examples:

# In app delegate
open HomeScreen.new(nav_bar: true)

# In tab bar
open HomeScreen.new(nav_bar: true), hide_tab_bar: true

# `modal: true` is the same as `open_modal`.
open ModalScreen.new(nav_bar: true), modal: true, animated: true

# Opening a modal screen with transition or presentation styles
open_modal ModalScreen.new(nav_bar: true,
    transition_style: UIModalTransitionStyleFlipHorizontal,
    presentation_style: UIModalPresentationFormSheet)

# From any screen (same as `open_root_screen`)
open HomeScreen.new(nav_bar: true), close_all: true

# Opening a screen in a different tab or split view screen
open DetailScreen.new, in_tab: "Tab name" # if you're in a tab bar
open MasterScreen, in_master: true # if you're in a split view (opened in current navigation controller if not)
open DetailScreen, in_detail: true # if you're in a split view (opened in current navigation controller if not)

# Opening a screen with a custom navigation_controller class. (Defaults to PM::NavigationController)
class MyNavigationController < PM::NavigationController; end
open HomeScreen.new(nav_bar: true, nav_controller: MyNavigationController), close_all: true

# Opens a screen with a navigation controller but with the navigation bar hidden
open HomeScreen.new(nav_bar: true, hide_nav_bar: true) # Edge feature
Setting screen accessors

Any writable attribute (accessor, setter methods etc.) in screen can also be set in the new hash argument.

class HomeScreen < PM::Screen
  def profile_button_tapped
    open ProfileScreen.new(user: @current_user)
  end
end

class ProfileScreen < PM::Screen
  attr_accessor :user

  def on_load
    puts user # => @current_user object
  end
end

open_modal(screen, args = {})

Opens a modal screen. Same as open HomeScreen, modal: true

supported_orientation?(orientation)

Returns whether UISupportedInterfaceOrientations includes the given orientation.

supported_orientation?(UIInterfaceOrientationMaskPortrait)
supported_orientation?(UIInterfaceOrientationMaskLandscapeLeft)
supported_orientation?(UIInterfaceOrientationMaskLandscapeRight)
supported_orientation?(UIInterfaceOrientationMaskPortraitUpsideDown)

supported_orientations

Returns the value for UISupportedInterfaceOrientations.

supported_device_families

Returns either :iphone or :ipad. Should probably be named current_device_family or something.

first_screen?

Boolean representing if this is the first screen in a navigation controller stack.

def on_appear
  self.first_screen? # => true | false
end

Class Methods

title(new_title)

Sets the default text title for all of the instances of this screen.

class MyScreen < PM::Screen
  title "Some screen"
  # ...
end

title_view(new_title_view)

Sets an arbitrary view as the nav bar title.

class MyScreen < PM::Screen
  title_view UILabel.new
  # ...
end

title_image(new_image)

Sets an arbitrary image as the nav bar title.

class MyScreen < PM::Screen
  title_image "image.png"
  # ...
end

status_bar(style=nil, args={animation: UIStatusBarAnimationSlide})

Set the properties of the applications' status bar. Options for style are: :none, :light and :default. The animation argument should be a UIStatusBarAnimation (or :none / :fade / :slide) and is used to hide or show the status bar when appropriate and defaults to :slide.

class MyScreen < PM::Screen
  status_bar :none, {animation: :fade}
  # ...
end

class MyScreenWithADarkColoredNavBar < PM::Screen
  status_bar :light
  # ...
end

Creates a nav bar button in the specified position with the given options

class HomeScreen < PM::Screen
  nav_bar_button :left, title: "Back", style: :plain, action: :back
  # ...
end

Accessors

parent_screen

References the screen immediately before this one in a navigation controller or the presenting screen for modals. You should set this yourself if you're doing something funky like addChildViewController.

def on_appear
  self.parent_screen # => PM::Screen instance
end

The main view for this screen.

find.root_view