ProMotion 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -18,4 +18,5 @@ tmp
18
18
  .repl_history
19
19
  build
20
20
  build/*
21
- *.000
21
+ *.000
22
+ .rvmrc
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
1
  source :rubygems
2
2
 
3
3
  # Specify your gem's dependencies in ProMotion.gemspec
4
+ gem "motion-table", :path => "~/Code/motion-table"
4
5
  gemspec
6
+
data/ProMotion.gemspec CHANGED
@@ -4,10 +4,10 @@ require File.expand_path('../lib/ProMotion/version', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["Jamon Holmgren", "Silas Matson", "ClearSight Studio"]
6
6
  gem.email = ["jamon@clearsightstudio.com"]
7
- gem.description = "ProMotion is a new way to organize RubyMotion apps. Currently a proof of concept."
7
+ gem.description = "ProMotion is a new way to organize RubyMotion apps."
8
8
  gem.summary = "
9
9
  ProMotion is a new way to organize RubyMotion apps. Instead of dealing
10
- with UIViewControllers and UIViews, you work with Screens. Screens are
10
+ with UIViewControllers, you work with Screens. Screens are
11
11
  a logical way to think of your app -- similar in some ways to Storyboards.
12
12
  "
13
13
  gem.homepage = "https://github.com/clearsightstudio/ProMotion"
@@ -18,4 +18,5 @@ Gem::Specification.new do |gem|
18
18
  gem.name = "ProMotion"
19
19
  gem.require_paths = ["lib"]
20
20
  gem.version = ProMotion::VERSION
21
+ # gem.add_dependency("motion-table", "~> 0.1.6")
21
22
  end
data/README.md CHANGED
@@ -1,10 +1,7 @@
1
- # ProMotion
1
+ # ProMotion - A new way to organize RubyMotion apps.
2
2
 
3
- **Please note: this is a proof of concept and does not yet work.**
4
-
5
- ProMotion is a new way to organize RubyMotion apps. Instead of dealing
6
- with UIViewControllers and UIViews, you work with Screens. Screens are
7
- a logical way to think of your app.
3
+ ProMotion introduces a new object called "Screens". Screens have a one-to-one relationship
4
+ with your app's screens and can (usually) take the place of view controllers.
8
5
 
9
6
  Typical /app file structure:
10
7
 
@@ -17,273 +14,159 @@ Typical /app file structure:
17
14
  home_screen.rb
18
15
  settings_screen.rb
19
16
  models
17
+ view_controllers
20
18
  views
21
19
  app_delegate.rb
22
20
 
23
- The "views" folder contains custom view components, written in normal RubyMotion. "models" can be whatever ORM you're using.
24
-
25
- ### What about MVC?
26
-
27
- 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
28
- in the ViewControllers. This pattern may be best for simpler, smaller apps.
29
-
30
- This is a proof of concept. I'd really appreciate feedback on it at my email address (jamon@clearsightstudio.com) or Twitter (@jamonholmgren).
31
-
32
- ## Installation
33
-
34
- Add this line to your application's Gemfile:
35
-
36
- gem 'ProMotion'
37
-
38
- And then execute:
39
-
40
- $ bundle
41
-
42
- Or install it yourself as:
43
-
44
- $ gem install ProMotion
45
-
46
21
  ## Usage
47
22
 
48
- It's easy to load your first screen with a navigation bar (the screen is opened in a UINavigationController behind the scenes):
23
+ Loading your home screen:
49
24
 
50
25
  ```ruby
51
- # In /app/app_delegate.rb (note that AppDelegate extends ProMotion::AppDelegate)
52
- class AppDelegate < ProMotion::AppDelegate
53
- def application(application, didFinishLaunchingWithOptions:launchOptions)
54
- open_with_nav_bar HomeScreen
55
-
56
- true
26
+ # In /app/app_delegate.rb (note that AppDelegate extends ProMotion::AppDelegateParent)
27
+ class AppDelegate < ProMotion::AppDelegateParent
28
+ def on_load(options)
29
+ open_screen MyHomeScreen.new(nav_bar: true)
57
30
  end
58
31
  end
59
32
  ```
60
33
 
61
- Screens are pretty straightforward. You extend ProMotion::Screen and provide a title and an on_load method.
34
+ Creating a basic screen:
62
35
 
63
36
  ```ruby
64
- # In /app/screens/home_screen.rb:
65
37
  class HomeScreen < ProMotion::Screen
66
- # Set the title for use in nav bars and other containers
67
38
  title "Home"
68
39
 
69
- # Called when this screen is first "opened" and allows you to set up your view components
70
40
  def on_load
71
- @default_image = add_image(:default_image, src: "default.png", frame: [10, 50, 100, 100])
41
+ # Set up the elements in your view with add_element:
42
+ @label = add_element UILabel.alloc.initWithFrame(CGRectMake(5, 5, 20, 20)), {
43
+ text: "This is awesome!",
44
+ font: UIFont.UIFont.systemFontOfSize(18)
45
+ }
46
+ end
47
+
48
+ def on_appear
49
+ # Refresh the data if you want
72
50
  end
73
51
  end
74
52
  ```
75
53
 
76
- In on_load, you can add images, buttons, labels, custom views to your screen.
54
+ Creating a tabbed bar:
77
55
 
78
56
  ```ruby
79
- # In /app/screens/home_screen.rb:
80
- class HomeScreen < ProMotion::Screen
81
- # Set the title for use in nav bars and other containers
82
- title "Home"
83
-
84
- def on_load
85
- # Add view items as instance vars so you can access them in other methods
86
-
87
- # This adds a right nav bar button. on_tap allows you to set a method to call when it's tapped.
88
- @right_bar_button = add_right_nav_button(label: "Save", on_tap: :save)
89
-
90
- # Helper function for adding a button
91
- @settings_button = add_button(label: "Settings", frame: [10, 10, 100, 30])
92
-
93
- # Helper function for adding an image
94
- @default_image = add_image(:default_image, src: "default.png", frame: [10, 50, 100, 100])
95
-
96
- # You can also add custom UIViews through the add_view method.
97
- @custom_view = add_view(ChatView.alloc.initWithFrame(CGRectMake(10, 300, 40, 40)))
98
- end
57
+ def on_load(options)
58
+ @home = MyHomeScreen.new(nav_bar: true)
59
+ @settings = SettingsScreen.new
60
+ @contact = ContactScreen.new(nav_bar: true)
61
+ open_tab_bar @home, @settings, @contact
99
62
  end
100
63
  ```
101
64
 
102
- View components can be bound to events (like jQuery) and run methods or run a block.
65
+ Any view item (UIView, UIButton, etc) can be used with add_element.
66
+ The second argument is a hash of settings that get applied to the
67
+ element before it is dropped into the view.
103
68
 
104
69
  ```ruby
105
- # settings_pushed is executed when the button is tapped
106
- @settings_button = add_button(label: "Settings", frame: [10, 10, 100, 30])
107
- @settings_button.on(:tap, :settings_pushed)
70
+ @label = add_element UILabel.alloc.initWithFrame(CGRectMake(5, 5, 20, 20)), {
71
+ text: "This is awesome!",
72
+ font: UIFont.UIFont.systemFontOfSize(18)
73
+ }
74
+ ```
108
75
 
109
- # This demonstrates a block
110
- @settings_button.on(:tap) do
111
- # Do something
112
- end
76
+ Add a nav_bar button and a tab_bar icon:
113
77
 
114
- # This button passes in arguments to the method when it's tapped
115
- @edit_button = add_button(label: "Edit", frame: [10, 10, 100, 30])
116
- @edit_button.on(:tap, :edit_pushed, id: 4)
78
+ ```ruby
79
+ add_right_nav_button(label: "Save", action: :save)
80
+ set_tab_bar_item(title: "Contacts", system_icon: UITabBarSystemItemContacts)
117
81
  ```
118
82
 
119
- To open other screens, just call the built-in "open" method on a new instance or class:
83
+ Open a new screen:
120
84
 
121
85
  ```ruby
122
86
  def settings_button_tapped
123
87
  # ...with a class...
124
- open SettingsScreen
88
+ open_screen SettingsScreen
125
89
 
126
90
  # ...or with an instance...
127
- @settings_screen = SettingsScreen.new(some_attr: 4)
128
- open @settings_screen
129
-
130
- # ...or if you like...
131
- open SettingsScreen.new
91
+ @settings_screen = SettingsScreen.new
92
+ open_screen @settings_screen
132
93
  end
133
94
  ```
134
95
 
135
- You can pass in arguments to those screens if they have accessors:
96
+ You can pass in arguments to other screens if they have accessors:
136
97
 
137
98
  ```ruby
138
- # /app/screens/settings_screen.rb
139
- class SettingsScreen < ProMotion::Screen
140
- attr_accessor :user_type
141
-
142
- def on_load
143
- if self.user_type == "Admin"
144
- # Stuff
145
- end
146
- end
147
-
148
- # ...
149
- end
150
-
151
- # /app/screens/home_screen.rb
152
99
  class HomeScreen < ProMotion::Screen
153
100
  # ...
154
101
 
155
102
  def settings_button_tapped
156
- open SettingsScreen.new(user_type: "Admin")
103
+ open_screen ProfileScreen.new(user: some_user)
157
104
  end
158
105
  end
159
- ```
160
106
 
161
- When you're done with a screen, just close it:
107
+ class ProfileScreen < ProMotion::Screen
108
+ attr_accessor :user
162
109
 
163
- ```ruby
164
- def save_and_close
165
- if @model.save
166
- close
110
+ def on_load
111
+ self.user # => some_user instance
167
112
  end
168
113
  end
114
+
169
115
  ```
170
116
 
171
- If you want to pass arguments back to the previous screen, go for it.
117
+ Close a screen, passing back arguments to the previous screen's "on_return" method:
172
118
 
173
119
  ```ruby
174
- class SettingsScreen < ProMotion::Screen
120
+ class ItemScreen
175
121
  # ...
176
-
177
122
  def save_and_close
178
- close(saved_changes: true)
123
+ if @model.save
124
+ close_screen(model_saved: true)
125
+ end
179
126
  end
180
127
  end
181
128
 
182
129
  class MainScreen < ProMotion::Screen
183
130
  # ...
184
-
185
131
  def on_return(args = {})
186
- if args[:saved_changes]
132
+ if args[:model_saved]
187
133
  self.reload_something
188
134
  end
189
135
  end
190
136
  end
191
137
  ```
192
138
 
193
- You can create sectioned table screens easily.
139
+ Use a custom view controller:
194
140
 
195
141
  ```ruby
196
- class HomeScreen < ProMotion::Screen
197
- title "Home"
198
-
199
- # Defaults to :normal. :plain_table, :grouped_table are options.
200
- screen_type :grouped_table
201
-
202
- def on_load
203
- # No need to set anything up, really
204
- end
205
-
206
- # If you define your screen_type as some sort of table, this gets called to get the data.
207
- # You can also refresh the table data manually by calling `self.reload_table_data`
208
- def table_data
209
- # You can create a new table section here and add cells to it like so:
210
- @account_section = add_section(label: "Your Account")
211
- @account_section.add_cell(title: "Edit Profile", action: :edit_profile, arguments: { account_id: @account.id })
212
- @account_section.add_cell(title: "Log Out", action: :log_out)
213
-
214
- # Or just pass back an array with everything defined and we'll build it for you:
215
- [{
216
- title: "Your Account",
217
- cells: [
218
- { title: "Edit Profile", action: :edit_profile },
219
- { title: "Log Out", action: :log_out },
220
- { title: "Notification Settings", action: :notification_settings }
221
- ]
222
- }, {
223
- title: "App Stuff",
224
- cells: [
225
- { title: "About", action: :show_about },
226
- { title: "Feedback", action: :show_feedback }
227
- ]
228
- }]
229
- end
142
+ def on_load
143
+ set_view_controller MyCustomViewController
144
+
145
+ # Note: on_appear will not fire when using a custom
146
+ # view controller.
230
147
  end
231
148
  ```
232
149
 
233
- Here's a full demo of a screen:
150
+ The helper add_element takes a
234
151
 
235
- ```ruby
236
- # In /app/screens/home_screen.rb:
152
+ You can create sectioned table screens easily. TableScreen, SectionedTableScreen, GroupedTableScreen
237
153
 
238
- class HomeScreen < ProMotion::Screen
239
- # Accessors allow screens to set parameters when opening this screen
240
- attr_accessor :foo
241
-
242
- # Set the title for use in nav bars and other containers
243
- title "Home"
244
-
245
- # Defaults to :normal. :plain_table, :grouped_table are options.
246
- screen_type :plain_table
154
+ ```ruby
155
+ class SettingsScreen < ProMotion::GroupedTableScreen
156
+ title "Settings"
247
157
 
248
- # Called when this screen is first "opened" and allows you to set up your view components
249
158
  def on_load
250
- # Add view items as instance vars so you can access them in other methods
251
- # This adds a right nav bar button. on_tap allows you to set a method to call when it's tapped.
252
- @right_bar_button = add_right_nav_button(label: "Save", on_tap: :save)
253
-
254
- # Helper function for adding a button
255
- @settings_button = add_button(label: "Settings", frame: [10, 10, 100, 30])
256
-
257
- # View items can be bound to events (like jQuery) and run methods or run a block.
258
- @settings_button.on(:tap, :settings_pushed)
259
- @settings_button.on(:tapHold) do
260
- # Do something
261
- end
262
-
263
- # Helper function for adding an image
264
- @default_image = add_image(:default_image, src: "default.png", frame: [10, 50, 100, 100])
265
-
266
- # This button passes in arguments to the method when it's tapped
267
- @edit_button = add_button(label: "Edit", frame: [10, 10, 100, 30])
268
- @edit_button.on(:tap, :edit_pushed, id: 4)
269
-
270
- # You can also add custom UIViews through the add_view method.
271
- @custom_view = add_view(ChatView.alloc.initWithFrame(CGRectMake(10, 300, 40, 40)))
159
+ add_right_nav_button(label: "Save", action: :save)
160
+ set_tab_bar_item(title: "Settings", icon: "settings.png")
272
161
  end
273
-
274
- # If you define your screen_type as some sort of table, this gets called to get the data.
275
- # You can also refresh the table data manually by calling `self.reload_table_data`
162
+
163
+ # table_data is automatically called. Use this format in the return value.
164
+ # Grouped tables are the same as plain tables
276
165
  def table_data
277
- # You can create a new table section here and add cells to it like so:
278
- @account_section = add_section(label: "Your Account")
279
- @account_section.add_cell(title: "Edit Profile", action: :edit_profile, arguments: { account_id: @account.id })
280
- @account_section.add_cell(title: "Log Out", action: :log_out)
281
-
282
- # Or just pass back an array with everything defined and we'll build it for you:
283
166
  [{
284
167
  title: "Your Account",
285
168
  cells: [
286
- { title: "Edit Profile", action: :edit_profile },
169
+ { title: "Edit Profile", action: :edit_profile, arguments: { id: 3 } },
287
170
  { title: "Log Out", action: :log_out },
288
171
  { title: "Notification Settings", action: :notification_settings }
289
172
  ]
@@ -296,52 +179,25 @@ class HomeScreen < ProMotion::Screen
296
179
  }]
297
180
  end
298
181
 
299
- # Custom method, invoked when tapping something with this as the action
300
- def save
301
- # Assuming some sort of ORM, like ParseModel
302
- @my_model.save
303
-
304
- # When you want to close the current screen (usually in a navigation controller), just run this.
305
- close
306
-
307
- # You can also pass back arguments to the previous screen as you close.
308
- # If the previous screen has an `on_return` method, this will be passed into that method
309
- close(did_stuff: true)
182
+ # This method allows you to create a "jumplist", the index on the right side of the table
183
+ def table_data_index
184
+ return ["A", "B", "C"]
310
185
  end
311
-
312
- # This is called any time a screen "above" this screen is closed. args = {} is required.
313
- def on_return(args = {})
314
- if args[:did_stuff]
315
- # Refresh?
316
- end
317
- end
318
-
319
- # Custom method
320
- def settings_pushed
321
- # Just open a settings screen
322
- open SettingsScreen
323
-
324
- # If you prefer to pass in an instance, that works too:
325
- open SettingsScreen.new
326
- end
327
-
328
- def close_pushed
329
- close
330
- end
331
-
332
- # Custom method with passed in arguments
333
- def edit_pushed(args)
334
- # Open a screen and set some of its attributes
335
- open EditScreen.new(id: args[:id])
186
+
187
+ # Your table cells, when tapped, will execute the corresponding actions and pass in arguments:
188
+ def edit_profile(arguments)
189
+ # ...
336
190
  end
337
191
  end
338
192
  ```
339
193
 
194
+ ### What about MVC?
195
+
196
+ 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
197
+ in the ViewControllers and views are mainly custom elements. This pattern may be best for simpler, smaller apps.
198
+
199
+ Feedback welcome via twitter @jamonholmgren.
340
200
 
341
201
  ## Contributing
342
202
 
343
- 1. Fork it
344
- 2. Create your feature branch (`git checkout -b my-new-feature`)
345
- 3. Commit your changes (`git commit -am 'Added some feature'`)
346
- 4. Push to the branch (`git push origin my-new-feature`)
347
- 5. Create new Pull Request
203
+ I'm really looking for feedback. Tweet me with your ideas or open a ticket (I don't mind!) and let's discuss.