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 +2 -1
- data/Gemfile +2 -0
- data/ProMotion.gemspec +3 -2
- data/README.md +85 -229
- data/Rakefile +15 -0
- data/app/app_delegate.rb +5 -0
- data/app/screens/home_screen.rb +10 -0
- data/app/screens/test_screen.rb +10 -0
- data/lib/ProMotion.rb +9 -3
- data/lib/ProMotion/AppDelegate.rb +55 -2
- data/lib/ProMotion/Console.rb +19 -0
- data/lib/ProMotion/ProMotion.rb +3 -0
- data/lib/ProMotion/classes/Element.rb +14 -0
- data/lib/ProMotion/classes/Screen.rb +239 -0
- data/lib/ProMotion/classes/TableScreen.rb +47 -0
- data/lib/ProMotion/ext/NavigationController.rb +5 -0
- data/lib/ProMotion/ext/TableViewController.rb +21 -0
- data/lib/ProMotion/ext/ViewController.rb +21 -0
- data/lib/ProMotion/helpers/MeasureHelper.rb +17 -0
- data/lib/ProMotion/helpers/TabBar.rb +30 -0
- data/lib/ProMotion/helpers/motion-table/console.rb +26 -0
- data/lib/ProMotion/helpers/motion-table/first/searchable_table.rb +56 -0
- data/lib/ProMotion/helpers/motion-table/first/sectioned_table.rb +108 -0
- data/lib/ProMotion/helpers/motion-table/grouped_table.rb +12 -0
- data/lib/ProMotion/helpers/motion-table/plain_table.rb +13 -0
- data/lib/ProMotion/version.rb +1 -1
- metadata +25 -8
- data/lib/ProMotion/Screen.rb +0 -11
data/.gitignore
CHANGED
data/Gemfile
CHANGED
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.
|
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
|
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
|
-
|
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
|
-
|
23
|
+
Loading your home screen:
|
49
24
|
|
50
25
|
```ruby
|
51
|
-
# In /app/app_delegate.rb (note that AppDelegate extends ProMotion::
|
52
|
-
class AppDelegate < ProMotion::
|
53
|
-
def
|
54
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
54
|
+
Creating a tabbed bar:
|
77
55
|
|
78
56
|
```ruby
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
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
|
-
|
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
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
83
|
+
Open a new screen:
|
120
84
|
|
121
85
|
```ruby
|
122
86
|
def settings_button_tapped
|
123
87
|
# ...with a class...
|
124
|
-
|
88
|
+
open_screen SettingsScreen
|
125
89
|
|
126
90
|
# ...or with an instance...
|
127
|
-
@settings_screen = SettingsScreen.new
|
128
|
-
|
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
|
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
|
-
|
103
|
+
open_screen ProfileScreen.new(user: some_user)
|
157
104
|
end
|
158
105
|
end
|
159
|
-
```
|
160
106
|
|
161
|
-
|
107
|
+
class ProfileScreen < ProMotion::Screen
|
108
|
+
attr_accessor :user
|
162
109
|
|
163
|
-
|
164
|
-
|
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
|
-
|
117
|
+
Close a screen, passing back arguments to the previous screen's "on_return" method:
|
172
118
|
|
173
119
|
```ruby
|
174
|
-
class
|
120
|
+
class ItemScreen
|
175
121
|
# ...
|
176
|
-
|
177
122
|
def save_and_close
|
178
|
-
|
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[:
|
132
|
+
if args[:model_saved]
|
187
133
|
self.reload_something
|
188
134
|
end
|
189
135
|
end
|
190
136
|
end
|
191
137
|
```
|
192
138
|
|
193
|
-
|
139
|
+
Use a custom view controller:
|
194
140
|
|
195
141
|
```ruby
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
#
|
200
|
-
|
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
|
-
|
150
|
+
The helper add_element takes a
|
234
151
|
|
235
|
-
|
236
|
-
# In /app/screens/home_screen.rb:
|
152
|
+
You can create sectioned table screens easily. TableScreen, SectionedTableScreen, GroupedTableScreen
|
237
153
|
|
238
|
-
|
239
|
-
|
240
|
-
|
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
|
-
|
251
|
-
|
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
|
-
#
|
275
|
-
#
|
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
|
-
#
|
300
|
-
def
|
301
|
-
|
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
|
-
#
|
313
|
-
def
|
314
|
-
|
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
|
-
|
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.
|