README.md in ProMotion-0.0.2 vs README.md in ProMotion-0.1.0
- old
+ new
@@ -1,13 +1,10 @@
-# ProMotion
+# ProMotion - A new way to organize RubyMotion apps.
-**Please note: this is a proof of concept and does not yet work.**
+ProMotion introduces a new object called "Screens". Screens have a one-to-one relationship
+with your app's screens and can (usually) take the place of view controllers.
-ProMotion is a new way to organize RubyMotion apps. Instead of dealing
-with UIViewControllers and UIViews, you work with Screens. Screens are
-a logical way to think of your app.
-
Typical /app file structure:
app
screens
photos
@@ -15,277 +12,163 @@
show_photo_screen.rb
edit_photo_screen.rb
home_screen.rb
settings_screen.rb
models
+ view_controllers
views
app_delegate.rb
-The "views" folder contains custom view components, written in normal RubyMotion. "models" can be whatever ORM you're using.
-
-### What about MVC?
-
-I'm a big believer in MVC (I'm a Rails developer, too). I found that most of the time working in RubyMotion seems to happen
-in the ViewControllers. This pattern may be best for simpler, smaller apps.
-
-This is a proof of concept. I'd really appreciate feedback on it at my email address (jamon@clearsightstudio.com) or Twitter (@jamonholmgren).
-
-## Installation
-
-Add this line to your application's Gemfile:
-
- gem 'ProMotion'
-
-And then execute:
-
- $ bundle
-
-Or install it yourself as:
-
- $ gem install ProMotion
-
## Usage
-It's easy to load your first screen with a navigation bar (the screen is opened in a UINavigationController behind the scenes):
+Loading your home screen:
```ruby
-# In /app/app_delegate.rb (note that AppDelegate extends ProMotion::AppDelegate)
-class AppDelegate < ProMotion::AppDelegate
- def application(application, didFinishLaunchingWithOptions:launchOptions)
- open_with_nav_bar HomeScreen
-
- true
+# In /app/app_delegate.rb (note that AppDelegate extends ProMotion::AppDelegateParent)
+class AppDelegate < ProMotion::AppDelegateParent
+ def on_load(options)
+ open_screen MyHomeScreen.new(nav_bar: true)
end
end
```
-Screens are pretty straightforward. You extend ProMotion::Screen and provide a title and an on_load method.
+Creating a basic screen:
```ruby
-# In /app/screens/home_screen.rb:
class HomeScreen < ProMotion::Screen
- # Set the title for use in nav bars and other containers
title "Home"
- # Called when this screen is first "opened" and allows you to set up your view components
def on_load
- @default_image = add_image(:default_image, src: "default.png", frame: [10, 50, 100, 100])
+ # Set up the elements in your view with add_element:
+ @label = add_element UILabel.alloc.initWithFrame(CGRectMake(5, 5, 20, 20)), {
+ text: "This is awesome!",
+ font: UIFont.UIFont.systemFontOfSize(18)
+ }
end
+
+ def on_appear
+ # Refresh the data if you want
+ end
end
```
-In on_load, you can add images, buttons, labels, custom views to your screen.
+Creating a tabbed bar:
```ruby
-# In /app/screens/home_screen.rb:
-class HomeScreen < ProMotion::Screen
- # Set the title for use in nav bars and other containers
- title "Home"
-
- def on_load
- # Add view items as instance vars so you can access them in other methods
-
- # This adds a right nav bar button. on_tap allows you to set a method to call when it's tapped.
- @right_bar_button = add_right_nav_button(label: "Save", on_tap: :save)
-
- # Helper function for adding a button
- @settings_button = add_button(label: "Settings", frame: [10, 10, 100, 30])
-
- # Helper function for adding an image
- @default_image = add_image(:default_image, src: "default.png", frame: [10, 50, 100, 100])
-
- # You can also add custom UIViews through the add_view method.
- @custom_view = add_view(ChatView.alloc.initWithFrame(CGRectMake(10, 300, 40, 40)))
- end
+def on_load(options)
+ @home = MyHomeScreen.new(nav_bar: true)
+ @settings = SettingsScreen.new
+ @contact = ContactScreen.new(nav_bar: true)
+ open_tab_bar @home, @settings, @contact
end
```
-View components can be bound to events (like jQuery) and run methods or run a block.
+Any view item (UIView, UIButton, etc) can be used with add_element.
+The second argument is a hash of settings that get applied to the
+element before it is dropped into the view.
```ruby
-# settings_pushed is executed when the button is tapped
-@settings_button = add_button(label: "Settings", frame: [10, 10, 100, 30])
-@settings_button.on(:tap, :settings_pushed)
+@label = add_element UILabel.alloc.initWithFrame(CGRectMake(5, 5, 20, 20)), {
+ text: "This is awesome!",
+ font: UIFont.UIFont.systemFontOfSize(18)
+}
+```
-# This demonstrates a block
-@settings_button.on(:tap) do
- # Do something
-end
+Add a nav_bar button and a tab_bar icon:
-# This button passes in arguments to the method when it's tapped
-@edit_button = add_button(label: "Edit", frame: [10, 10, 100, 30])
-@edit_button.on(:tap, :edit_pushed, id: 4)
+```ruby
+add_right_nav_button(label: "Save", action: :save)
+set_tab_bar_item(title: "Contacts", system_icon: UITabBarSystemItemContacts)
```
-To open other screens, just call the built-in "open" method on a new instance or class:
+Open a new screen:
```ruby
def settings_button_tapped
# ...with a class...
- open SettingsScreen
+ open_screen SettingsScreen
# ...or with an instance...
- @settings_screen = SettingsScreen.new(some_attr: 4)
- open @settings_screen
-
- # ...or if you like...
- open SettingsScreen.new
+ @settings_screen = SettingsScreen.new
+ open_screen @settings_screen
end
```
-You can pass in arguments to those screens if they have accessors:
+You can pass in arguments to other screens if they have accessors:
```ruby
-# /app/screens/settings_screen.rb
-class SettingsScreen < ProMotion::Screen
- attr_accessor :user_type
-
- def on_load
- if self.user_type == "Admin"
- # Stuff
- end
- end
-
- # ...
-end
-
-# /app/screens/home_screen.rb
class HomeScreen < ProMotion::Screen
# ...
def settings_button_tapped
- open SettingsScreen.new(user_type: "Admin")
+ open_screen ProfileScreen.new(user: some_user)
end
end
-```
-When you're done with a screen, just close it:
+class ProfileScreen < ProMotion::Screen
+ attr_accessor :user
-```ruby
-def save_and_close
- if @model.save
- close
+ def on_load
+ self.user # => some_user instance
end
end
+
```
-If you want to pass arguments back to the previous screen, go for it.
+Close a screen, passing back arguments to the previous screen's "on_return" method:
```ruby
-class SettingsScreen < ProMotion::Screen
+class ItemScreen
# ...
-
def save_and_close
- close(saved_changes: true)
+ if @model.save
+ close_screen(model_saved: true)
+ end
end
end
class MainScreen < ProMotion::Screen
# ...
-
def on_return(args = {})
- if args[:saved_changes]
+ if args[:model_saved]
self.reload_something
end
end
end
```
-You can create sectioned table screens easily.
+Use a custom view controller:
```ruby
-class HomeScreen < ProMotion::Screen
- title "Home"
-
- # Defaults to :normal. :plain_table, :grouped_table are options.
- screen_type :grouped_table
-
- def on_load
- # No need to set anything up, really
- end
-
- # If you define your screen_type as some sort of table, this gets called to get the data.
- # You can also refresh the table data manually by calling `self.reload_table_data`
- def table_data
- # You can create a new table section here and add cells to it like so:
- @account_section = add_section(label: "Your Account")
- @account_section.add_cell(title: "Edit Profile", action: :edit_profile, arguments: { account_id: @account.id })
- @account_section.add_cell(title: "Log Out", action: :log_out)
-
- # Or just pass back an array with everything defined and we'll build it for you:
- [{
- title: "Your Account",
- cells: [
- { title: "Edit Profile", action: :edit_profile },
- { title: "Log Out", action: :log_out },
- { title: "Notification Settings", action: :notification_settings }
- ]
- }, {
- title: "App Stuff",
- cells: [
- { title: "About", action: :show_about },
- { title: "Feedback", action: :show_feedback }
- ]
- }]
- end
+def on_load
+ set_view_controller MyCustomViewController
+
+ # Note: on_appear will not fire when using a custom
+ # view controller.
end
```
-Here's a full demo of a screen:
+The helper add_element takes a
+You can create sectioned table screens easily. TableScreen, SectionedTableScreen, GroupedTableScreen
+
```ruby
-# In /app/screens/home_screen.rb:
+class SettingsScreen < ProMotion::GroupedTableScreen
+ title "Settings"
-class HomeScreen < ProMotion::Screen
- # Accessors allow screens to set parameters when opening this screen
- attr_accessor :foo
-
- # Set the title for use in nav bars and other containers
- title "Home"
-
- # Defaults to :normal. :plain_table, :grouped_table are options.
- screen_type :plain_table
-
- # Called when this screen is first "opened" and allows you to set up your view components
def on_load
- # Add view items as instance vars so you can access them in other methods
- # This adds a right nav bar button. on_tap allows you to set a method to call when it's tapped.
- @right_bar_button = add_right_nav_button(label: "Save", on_tap: :save)
-
- # Helper function for adding a button
- @settings_button = add_button(label: "Settings", frame: [10, 10, 100, 30])
-
- # View items can be bound to events (like jQuery) and run methods or run a block.
- @settings_button.on(:tap, :settings_pushed)
- @settings_button.on(:tapHold) do
- # Do something
- end
-
- # Helper function for adding an image
- @default_image = add_image(:default_image, src: "default.png", frame: [10, 50, 100, 100])
-
- # This button passes in arguments to the method when it's tapped
- @edit_button = add_button(label: "Edit", frame: [10, 10, 100, 30])
- @edit_button.on(:tap, :edit_pushed, id: 4)
-
- # You can also add custom UIViews through the add_view method.
- @custom_view = add_view(ChatView.alloc.initWithFrame(CGRectMake(10, 300, 40, 40)))
+ add_right_nav_button(label: "Save", action: :save)
+ set_tab_bar_item(title: "Settings", icon: "settings.png")
end
-
- # If you define your screen_type as some sort of table, this gets called to get the data.
- # You can also refresh the table data manually by calling `self.reload_table_data`
+
+ # table_data is automatically called. Use this format in the return value.
+ # Grouped tables are the same as plain tables
def table_data
- # You can create a new table section here and add cells to it like so:
- @account_section = add_section(label: "Your Account")
- @account_section.add_cell(title: "Edit Profile", action: :edit_profile, arguments: { account_id: @account.id })
- @account_section.add_cell(title: "Log Out", action: :log_out)
-
- # Or just pass back an array with everything defined and we'll build it for you:
[{
title: "Your Account",
cells: [
- { title: "Edit Profile", action: :edit_profile },
+ { title: "Edit Profile", action: :edit_profile, arguments: { id: 3 } },
{ title: "Log Out", action: :log_out },
{ title: "Notification Settings", action: :notification_settings }
]
}, {
title: "App Stuff",
@@ -294,54 +177,27 @@
{ title: "Feedback", action: :show_feedback }
]
}]
end
- # Custom method, invoked when tapping something with this as the action
- def save
- # Assuming some sort of ORM, like ParseModel
- @my_model.save
-
- # When you want to close the current screen (usually in a navigation controller), just run this.
- close
-
- # You can also pass back arguments to the previous screen as you close.
- # If the previous screen has an `on_return` method, this will be passed into that method
- close(did_stuff: true)
+ # This method allows you to create a "jumplist", the index on the right side of the table
+ def table_data_index
+ return ["A", "B", "C"]
end
-
- # This is called any time a screen "above" this screen is closed. args = {} is required.
- def on_return(args = {})
- if args[:did_stuff]
- # Refresh?
- end
+
+ # Your table cells, when tapped, will execute the corresponding actions and pass in arguments:
+ def edit_profile(arguments)
+ # ...
end
-
- # Custom method
- def settings_pushed
- # Just open a settings screen
- open SettingsScreen
-
- # If you prefer to pass in an instance, that works too:
- open SettingsScreen.new
- end
-
- def close_pushed
- close
- end
-
- # Custom method with passed in arguments
- def edit_pushed(args)
- # Open a screen and set some of its attributes
- open EditScreen.new(id: args[:id])
- end
end
```
+### What about MVC?
+I'm a big believer in MVC (I'm a Rails developer, too). I found that most of the time working in RubyMotion seems to happen
+in the ViewControllers and views are mainly custom elements. This pattern may be best for simpler, smaller apps.
+
+Feedback welcome via twitter @jamonholmgren.
+
## Contributing
-1. Fork it
-2. Create your feature branch (`git checkout -b my-new-feature`)
-3. Commit your changes (`git commit -am 'Added some feature'`)
-4. Push to the branch (`git push origin my-new-feature`)
-5. Create new Pull Request
+I'm really looking for feedback. Tweet me with your ideas or open a ticket (I don't mind!) and let's discuss.