ProMotion 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. data/.gitignore +0 -1
  2. data/Gemfile +1 -1
  3. data/README.md +336 -92
  4. data/Rakefile +1 -1
  5. data/lib/ProMotion.rb +14 -0
  6. data/lib/ProMotion/app_delegate.rb +63 -0
  7. data/lib/ProMotion/cocoatouch/NavigationController.rb +4 -0
  8. data/lib/ProMotion/cocoatouch/TableViewCell.rb +16 -0
  9. data/lib/ProMotion/cocoatouch/TableViewController.rb +50 -0
  10. data/lib/ProMotion/cocoatouch/ViewController.rb +52 -0
  11. data/lib/ProMotion/helpers/console.rb +24 -0
  12. data/lib/ProMotion/helpers/measure_helper.rb +20 -0
  13. data/lib/ProMotion/helpers/system_helper.rb +29 -0
  14. data/lib/ProMotion/helpers/tab_bar.rb +115 -0
  15. data/lib/ProMotion/helpers/view_helper.rb +39 -0
  16. data/lib/ProMotion/pro_motion.rb +3 -0
  17. data/lib/ProMotion/screen_helpers/_tables/_searchable_table.rb +66 -0
  18. data/lib/ProMotion/screen_helpers/_tables/_sectioned_table.rb +265 -0
  19. data/lib/ProMotion/screen_helpers/_tables/grouped_table.rb +13 -0
  20. data/lib/ProMotion/screen_helpers/_tables/plain_table.rb +14 -0
  21. data/lib/ProMotion/screen_helpers/screen_elements.rb +43 -0
  22. data/lib/ProMotion/screen_helpers/screen_navigation.rb +106 -0
  23. data/lib/ProMotion/screen_helpers/screen_tabs.rb +92 -0
  24. data/lib/ProMotion/screens/_screen_module.rb +222 -0
  25. data/lib/ProMotion/screens/_table_screen_module.rb +30 -0
  26. data/lib/ProMotion/screens/screen.rb +7 -0
  27. data/lib/ProMotion/screens/table_screen.rb +15 -0
  28. data/lib/ProMotion/version.rb +3 -0
  29. metadata +43 -63
  30. data/app/app_delegate.rb +0 -5
  31. data/app/screens/home_screen.rb +0 -21
  32. data/app/screens/test_screen.rb +0 -26
@@ -0,0 +1,3 @@
1
+ module ProMotion
2
+ end
3
+ ::PM = ProMotion unless defined?(::PM)
@@ -0,0 +1,66 @@
1
+ module ProMotion::MotionTable
2
+ module SearchableTable
3
+ def make_searchable(params={})
4
+ params[:content_controller] ||= params[:contentController]
5
+ params[:data_source] ||= params[:searchResultsDataSource]
6
+ params[:search_results_delegate] ||= params[:searchResultsDelegate]
7
+
8
+ params[:frame] ||= CGRectMake(0, 0, 320, 44) # TODO: Don't hardcode this...
9
+ params[:content_controller] ||= self
10
+ params[:delegate] ||= self
11
+ params[:data_source] ||= self
12
+ params[:search_results_delegate] ||= self
13
+
14
+ search_bar = UISearchBar.alloc.initWithFrame(params[:frame])
15
+ search_bar.autoresizingMask = UIViewAutoresizingFlexibleWidth
16
+
17
+ if params[:search_bar] && params[:search_bar][:placeholder]
18
+ search_bar.placeholder = params[:search_bar][:placeholder]
19
+ end
20
+
21
+ @contacts_search_display_controller = UISearchDisplayController.alloc.initWithSearchBar(search_bar, contentsController: params[:content_controller])
22
+ @contacts_search_display_controller.delegate = params[:delegate]
23
+ @contacts_search_display_controller.searchResultsDataSource = params[:data_source]
24
+ @contacts_search_display_controller.searchResultsDelegate = params[:search_results_delegate]
25
+
26
+ self.table_view.tableHeaderView = search_bar
27
+ end
28
+ alias :makeSearchable :make_searchable
29
+
30
+ ######### iOS methods, headless camel case #######
31
+
32
+ def searchDisplayController(controller, shouldReloadTableForSearchString:search_string)
33
+ @mt_filtered_data = nil
34
+ @mt_filtered_data = []
35
+
36
+ @mt_table_view_groups.each do |section|
37
+ new_section = {}
38
+ new_section[:cells] = []
39
+
40
+ section[:cells].each do |cell|
41
+ if cell[:title].to_s.downcase.strip.include?(search_string.downcase.strip)
42
+ new_section[:cells] << cell
43
+ end
44
+ end
45
+
46
+ if new_section[:cells] && new_section[:cells].length > 0
47
+ new_section[:title] = section[:title]
48
+ @mt_filtered_data << new_section
49
+ end
50
+ end
51
+ true
52
+ end
53
+
54
+ def searchDisplayControllerWillEndSearch(controller)
55
+ @mt_filtered = false
56
+ @mt_filtered_data = nil
57
+ self.table_view.setScrollEnabled true
58
+ end
59
+
60
+ def searchDisplayControllerWillBeginSearch(controller)
61
+ @mt_filtered = true
62
+ @mt_filtered_data = []
63
+ self.table_view.setScrollEnabled false
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,265 @@
1
+ module ProMotion::MotionTable
2
+ module SectionedTable
3
+ def table_setup
4
+ ProMotion::Console.log(" - #table_data method needed in TableScreen #{self.class.to_s}.", with_color: ProMotion::Console::RED_COLOR) unless self.respond_to?(:table_data)
5
+
6
+ self.view = self.create_table_view_from_data(self.table_data)
7
+ if self.class.respond_to?(:get_searchable) && self.class.get_searchable
8
+ self.make_searchable(content_controller: self, search_bar: self.class.get_searchable_params)
9
+ end
10
+ end
11
+
12
+ # @param [Array] Array of table data
13
+ # @returns [UITableView] delegated to self
14
+ def create_table_view_from_data(data)
15
+ set_table_view_data data
16
+ return table_view
17
+ end
18
+ alias :createTableViewFromData :create_table_view_from_data
19
+
20
+ def update_table_view_data(data)
21
+ set_table_view_data data
22
+ self.table_view.reloadData
23
+ end
24
+ alias :updateTableViewData :update_table_view_data
25
+
26
+ def set_table_view_data(data)
27
+ @mt_table_view_groups = data
28
+ end
29
+ alias :setTableViewData :set_table_view_data
30
+
31
+ def section_at_index(index)
32
+ if @mt_filtered
33
+ @mt_filtered_data.at(index)
34
+ else
35
+ @mt_table_view_groups.at(index)
36
+ end
37
+ end
38
+
39
+ def cell_at_section_and_index(section, index)
40
+ return section_at_index(section)[:cells].at(index) if section_at_index(section) && section_at_index(section)[:cells]
41
+ end
42
+ alias :cellAtSectionAndIndex :cell_at_section_and_index
43
+
44
+ def trigger_action(action, arguments)
45
+ if self.respond_to?(action)
46
+ expected_arguments = self.method(action).arity
47
+ if expected_arguments == 0
48
+ self.send(action)
49
+ elsif expected_arguments == 1 || expected_arguments == -1
50
+ self.send(action, arguments)
51
+ else
52
+ ProMotion::Console.log("MotionTable warning: #{action} expects #{expected_arguments} arguments. Maximum number of required arguments for an action is 1.", with_color: MotionTable::ProMotion::Console::RED_COLOR)
53
+ end
54
+ else
55
+ ProMotion::Console.log("Action not implemented: #{action.to_s}", with_color: ProMotion::Console::RED_COLOR)
56
+ end
57
+ end
58
+
59
+ def set_cell_attributes(element, args = {})
60
+ args.each do |k, v|
61
+ if v.is_a? Hash
62
+ v.each do
63
+ sub_element = element.send("#{k}")
64
+ set_cell_attributes(sub_element, v)
65
+ end
66
+ else
67
+ element.send("#{k}=", v) if element.respond_to?("#{k}=")
68
+ end
69
+ end
70
+ element
71
+ end
72
+
73
+ def accessory_toggled_switch(switch)
74
+ table_cell = switch.superview
75
+ indexPath = table_cell.superview.indexPathForCell(table_cell)
76
+
77
+ data_cell = cell_at_section_and_index(indexPath.section, indexPath.row)
78
+ data_cell[:arguments] = {} unless data_cell[:arguments]
79
+ data_cell[:arguments][:value] = switch.isOn if data_cell[:arguments].is_a? Hash
80
+ data_cell[:accessory_action] ||= data_cell[:accessoryAction] # For legacy support
81
+
82
+ trigger_action(data_cell[:accessory_action], data_cell[:arguments]) if data_cell[:accessory_action]
83
+ end
84
+
85
+ ########## Cocoa touch methods, leave as-is #################
86
+ def numberOfSectionsInTableView(table_view)
87
+ if @mt_filtered
88
+ return @mt_filtered_data.length if @mt_filtered_data
89
+ else
90
+ return @mt_table_view_groups.length if @mt_table_view_groups
91
+ end
92
+ 0
93
+ end
94
+
95
+ # Number of cells
96
+ def tableView(table_view, numberOfRowsInSection:section)
97
+ return section_at_index(section)[:cells].length if section_at_index(section) && section_at_index(section)[:cells]
98
+ 0
99
+ end
100
+
101
+ def tableView(table_view, titleForHeaderInSection:section)
102
+ return section_at_index(section)[:title] if section_at_index(section) && section_at_index(section)[:title]
103
+ end
104
+
105
+ # Set table_data_index if you want the right hand index column (jumplist)
106
+ def sectionIndexTitlesForTableView(table_view)
107
+ if self.respond_to?(:table_data_index)
108
+ self.table_data_index
109
+ end
110
+ end
111
+
112
+ def remap_data_cell(data_cell)
113
+ # Re-maps legacy data cell calls
114
+ mappings = {
115
+ cell_style: :cellStyle,
116
+ cell_identifier: :cellIdentifier,
117
+ cell_class: :cellClass,
118
+ masks_to_bounds: :masksToBounds,
119
+ background_color: :backgroundColor,
120
+ selection_style: :selectionStyle,
121
+ cell_class_attributes: :cellClassAttributes,
122
+ accessory_view: :accessoryView,
123
+ accessory_type: :accessoryType,
124
+ accessory_checked: :accessoryDefault,
125
+ remote_image: :remoteImage,
126
+ subviews: :subViews
127
+ }
128
+ mappings.each_pair do |n, old|
129
+ if data_cell[old]
130
+ warn "[DEPRECATION] `:#{old}` is deprecated in TableScreens. Use `:#{n}`"
131
+ data_cell[n] = data_cell[old]
132
+ end
133
+ end
134
+ if data_cell[:styles] && data_cell[:styles][:textLabel]
135
+ warn "[DEPRECATION] `:textLabel` is deprecated in TableScreens. Use `:label`"
136
+ data_cell[:styles][:label] = data_cell[:styles][:textLabel]
137
+ end
138
+ data_cell
139
+ end
140
+
141
+ def tableView(table_view, cellForRowAtIndexPath:indexPath)
142
+ # Aah, magic happens here...
143
+
144
+ data_cell = cell_at_section_and_index(indexPath.section, indexPath.row)
145
+ return UITableViewCell.alloc.init unless data_cell
146
+
147
+ data_cell = self.remap_data_cell(data_cell)
148
+
149
+ data_cell[:cell_style] ||= UITableViewCellStyleDefault
150
+ data_cell[:cell_identifier] ||= "Cell"
151
+ cell_identifier = data_cell[:cell_identifier]
152
+ data_cell[:cell_class] ||= ProMotion::TableViewCell
153
+
154
+ table_cell = table_view.dequeueReusableCellWithIdentifier(cell_identifier)
155
+ unless table_cell
156
+ table_cell = data_cell[:cell_class].alloc.initWithStyle(data_cell[:cell_style], reuseIdentifier:cell_identifier)
157
+
158
+ # Add optimizations here
159
+ table_cell.layer.masksToBounds = true if data_cell[:masks_to_bounds]
160
+ table_cell.backgroundColor = data_cell[:background_color] if data_cell[:background_color]
161
+ table_cell.selectionStyle = data_cell[:selection_style] if data_cell[:selection_style]
162
+ table_cell.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin
163
+ end
164
+
165
+ if data_cell[:cell_class_attributes]
166
+ set_cell_attributes table_cell, data_cell[:cell_class_attributes]
167
+ end
168
+
169
+ if data_cell[:accessory_view]
170
+ table_cell.accessoryView = data_cell[:accessory_view]
171
+ table_cell.accessoryView.autoresizingMask = UIViewAutoresizingFlexibleWidth
172
+ end
173
+
174
+ if data_cell[:accessory_type]
175
+ table_cell.accessoryType = data_cell[:accessory_type]
176
+ end
177
+
178
+ if data_cell[:accessory] && data_cell[:accessory] == :switch
179
+ switch_view = UISwitch.alloc.initWithFrame(CGRectZero)
180
+ switch_view.addTarget(self, action: "accessory_toggled_switch:", forControlEvents:UIControlEventValueChanged);
181
+ switch_view.on = true if data_cell[:accessory_checked]
182
+ table_cell.accessoryView = switch_view
183
+ end
184
+
185
+ if data_cell[:subtitle]
186
+ table_cell.detailTextLabel.text = data_cell[:subtitle]
187
+ table_cell.detailTextLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth
188
+ end
189
+
190
+ table_cell.selectionStyle = UITableViewCellSelectionStyleNone if data_cell[:no_select]
191
+
192
+ if data_cell[:remote_image]
193
+ if table_cell.imageView.respond_to?("setImageWithURL:placeholderImage:")
194
+ url = data_cell[:remote_image][:url]
195
+ url = NSURL.URLWithString(url) unless url.is_a?(NSURL)
196
+ placeholder = data_cell[:remote_image][:placeholder]
197
+ placeholder = UIImage.imageNamed(placeholder) if placeholder.is_a?(String)
198
+
199
+ table_cell.image_size = data_cell[:remote_image][:size] if data_cell[:remote_image][:size] && table_cell.respond_to?("image_size=")
200
+ table_cell.imageView.setImageWithURL(url, placeholderImage: placeholder)
201
+ table_cell.imageView.layer.masksToBounds = true
202
+ table_cell.imageView.layer.cornerRadius = data_cell[:remote_image][:radius]
203
+ else
204
+ ProMotion::Console.log("ProMotion Warning: to use remote_image with TableScreen you need to include the CocoaPod 'SDWebImage'.", with_color: ProMotion::Console::RED_COLOR)
205
+ end
206
+ elsif data_cell[:image]
207
+ table_cell.imageView.layer.masksToBounds = true
208
+ table_cell.imageView.image = data_cell[:image][:image]
209
+ table_cell.imageView.layer.cornerRadius = data_cell[:image][:radius] if data_cell[:image][:radius]
210
+ end
211
+
212
+ if data_cell[:subviews]
213
+ tag_number = 0
214
+ data_cell[:subviews].each do |view|
215
+ # Remove an existing view at that tag number
216
+ tag_number += 1
217
+ existing_view = table_cell.viewWithTag(tag_number)
218
+ existing_view.removeFromSuperview if existing_view
219
+
220
+ # Add the subview if it exists
221
+ if view
222
+ view.tag = tag_number
223
+ table_cell.addSubview view
224
+ end
225
+ end
226
+ end
227
+
228
+ if data_cell[:details]
229
+ table_cell.addSubview data_cell[:details][:image]
230
+ end
231
+
232
+ if data_cell[:styles] && data_cell[:styles][:label] && data_cell[:styles][:label][:frame]
233
+ ui_label = false
234
+ table_cell.contentView.subviews.each do |view|
235
+ if view.is_a? UILabel
236
+ ui_label = true
237
+ view.text = data_cell[:styles][:label][:text]
238
+ end
239
+ end
240
+
241
+ unless ui_label == true
242
+ label ||= UILabel.alloc.initWithFrame(CGRectZero)
243
+ set_cell_attributes label, data_cell[:styles][:label]
244
+ table_cell.contentView.addSubview label
245
+ end
246
+ # hackery
247
+ table_cell.textLabel.textColor = UIColor.clearColor
248
+ else
249
+ cell_title = data_cell[:title]
250
+ cell_title ||= ""
251
+ table_cell.textLabel.text = cell_title
252
+ end
253
+
254
+ return table_cell
255
+ end
256
+
257
+ def tableView(table_view, didSelectRowAtIndexPath:indexPath)
258
+ cell = cell_at_section_and_index(indexPath.section, indexPath.row)
259
+ table_view.deselectRowAtIndexPath(indexPath, animated: true);
260
+ cell[:arguments] ||= {}
261
+ cell[:arguments][:cell] = cell if cell[:arguments].is_a?(Hash)
262
+ trigger_action(cell[:action], cell[:arguments]) if cell[:action]
263
+ end
264
+ end
265
+ end
@@ -0,0 +1,13 @@
1
+ module ProMotion::MotionTable
2
+ module GroupedTable
3
+ include SectionedTable
4
+
5
+ def table_view
6
+ @table_view ||= UITableView.alloc.initWithFrame(self.view.frame, style:UITableViewStyleGrouped)
7
+ @table_view.dataSource = self;
8
+ @table_view.delegate = self;
9
+ return @table_view
10
+ end
11
+ alias :tableView :table_view
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module ProMotion::MotionTable
2
+ module PlainTable
3
+ include SectionedTable
4
+ include SearchableTable
5
+
6
+ def table_view
7
+ @table_view ||= UITableView.alloc.initWithFrame(self.view.frame, style:UITableViewStylePlain)
8
+ @table_view.dataSource = self;
9
+ @table_view.delegate = self;
10
+ return @table_view
11
+ end
12
+ alias :tableView :table_view
13
+ end
14
+ end
@@ -0,0 +1,43 @@
1
+ module ProMotion
2
+ module ScreenElements
3
+ include ProMotion::ViewHelper
4
+
5
+ def add(v, attrs = {})
6
+ if attrs && attrs.length > 0
7
+ set_attributes(v, attrs)
8
+ end
9
+ self.view.addSubview(v)
10
+ v
11
+ end
12
+ alias :add_element :add
13
+ alias :add_view :add
14
+
15
+ def remove(v)
16
+ v.removeFromSuperview
17
+ v = nil
18
+ end
19
+ alias :remove_element :remove
20
+ alias :remove_view :remove
21
+
22
+ def bounds
23
+ return self.view.bounds
24
+ end
25
+
26
+ def frame
27
+ return self.view.frame
28
+ end
29
+
30
+ def content_height(view)
31
+ height = 0
32
+ view.subviews.each do |subview|
33
+ next if subview.isHidden
34
+ y = subview.frame.origin.y
35
+ h = subview.frame.size.height
36
+ if (y + h) > height
37
+ height = y + h
38
+ end
39
+ end
40
+ height
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,106 @@
1
+ module ProMotion
2
+ module ScreenNavigation
3
+ # TODO: De-uglify this method.
4
+ def open_screen(screen, args = {})
5
+ # Instantiate screen if given a class
6
+ screen = screen.new if screen.respond_to?(:new)
7
+
8
+ screen.parent_screen = self if screen.respond_to?("parent_screen=")
9
+
10
+ screen.title = args[:title] if args[:title] && screen.respond_to?("title=")
11
+
12
+ screen.modal = args[:modal] if args[:modal] && screen.respond_to?("modal=")
13
+
14
+ screen.hidesBottomBarWhenPushed = args[:hide_tab_bar] unless args[:hide_tab_bar].nil?
15
+
16
+ screen.add_nav_bar if args[:nav_bar] && screen.respond_to?(:add_nav_bar)
17
+
18
+ unless args[:close_all] || args[:modal]
19
+ screen.navigation_controller ||= self.navigation_controller if screen.respond_to?("navigation_controller=")
20
+ screen.tab_bar ||= self.tab_bar if screen.respond_to?("tab_bar=")
21
+ end
22
+
23
+ screen.send(:on_load) if screen.respond_to?(:on_load)
24
+
25
+ animated = args[:animated]
26
+ animated ||= true
27
+
28
+ if args[:close_all]
29
+ open_root_screen(screen)
30
+ elsif args[:modal]
31
+ vc = screen
32
+ vc = screen.main_controller if screen.respond_to?("main_controller=")
33
+ self.presentModalViewController(vc, animated:animated)
34
+ elsif args[:in_tab] && self.tab_bar
35
+ vc = open_tab(args[:in_tab])
36
+ if vc
37
+ if vc.is_a?(UINavigationController)
38
+ screen.navigation_controller = vc if screen.respond_to?("navigation_controller=")
39
+ push_view_controller(screen, vc)
40
+ else
41
+ self.tab_bar.selectedIndex = vc.tabBarItem.tag
42
+ end
43
+ else
44
+ Console.log("No tab bar item '#{args[:in_tab]}'", with_color: Console::RED_COLOR)
45
+ end
46
+ elsif self.navigation_controller
47
+ push_view_controller screen
48
+ elsif screen.respond_to?(:main_controller)
49
+ open_view_controller screen.main_controller
50
+ else
51
+ open_view_controller screen
52
+ end
53
+ end
54
+ alias :open :open_screen
55
+
56
+ def open_root_screen(screen)
57
+ app_delegate.open_root_screen(screen)
58
+ end
59
+ alias :fresh_start :open_root_screen
60
+
61
+ def app_delegate
62
+ UIApplication.sharedApplication.delegate
63
+ end
64
+
65
+ # TODO: De-uglify this method.
66
+ def close_screen(args = {})
67
+ args ||= {}
68
+ args[:animated] = true
69
+
70
+ # Pop current view, maybe with arguments, if in navigation controller
71
+ previous_screen = self.parent_screen
72
+ if self.is_modal?
73
+ self.parent_screen.dismissModalViewControllerAnimated(args[:animated])
74
+ elsif self.navigation_controller
75
+ if args[:to_screen] && args[:to_screen].is_a?(UIViewController)
76
+ self.navigation_controller.popToViewController(args[:to_screen], animated: args[:animated])
77
+ previous_screen = args[:to_screen]
78
+ else
79
+ self.navigation_controller.popViewControllerAnimated(args[:animated])
80
+ end
81
+ else
82
+ Console.log("Tried to close #{self.to_s}; however, this screen isn't modal or in a nav bar.", withColor: Console::PURPLE_COLOR)
83
+ end
84
+
85
+ if previous_screen && previous_screen.respond_to?(:on_return)
86
+ if args
87
+ previous_screen.send(:on_return, args)
88
+ else
89
+ previous_screen.send(:on_return)
90
+ end
91
+ ProMotion::Screen.current_screen = previous_screen
92
+ end
93
+ end
94
+ alias :close :close_screen
95
+
96
+ def open_view_controller(vc)
97
+ UIApplication.sharedApplication.delegate.load_root_view vc
98
+ end
99
+
100
+ def push_view_controller(vc, nav_controller=nil)
101
+ Console.log(" You need a nav_bar if you are going to push #{vc.to_s} onto it.", withColor: Console::RED_COLOR) unless self.navigation_controller
102
+ nav_controller ||= self.navigation_controller
103
+ nav_controller.pushViewController(vc, animated: true)
104
+ end
105
+ end
106
+ end