ProMotion 0.6.5 → 0.7.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 (56) hide show
  1. data/README.md +79 -502
  2. data/Rakefile +30 -0
  3. data/app/app_delegate.rb +3 -1
  4. data/lib/ProMotion.rb +1 -1
  5. data/lib/ProMotion/cocoatouch/TableViewCell.rb +5 -1
  6. data/lib/ProMotion/delegate/delegate.rb +16 -0
  7. data/lib/ProMotion/delegate/delegate_helper.rb +88 -0
  8. data/lib/ProMotion/delegate/delegate_notifications.rb +61 -0
  9. data/lib/ProMotion/helpers/console.rb +3 -3
  10. data/lib/ProMotion/helpers/logger.rb +15 -15
  11. data/lib/ProMotion/helpers/view_helper.rb +7 -5
  12. data/lib/ProMotion/push_notifications/push_notification.rb +51 -0
  13. data/lib/ProMotion/screen_helpers/screen_elements.rb +2 -2
  14. data/lib/ProMotion/screen_helpers/screen_navigation.rb +5 -1
  15. data/lib/ProMotion/screen_helpers/screen_tabs.rb +2 -2
  16. data/lib/ProMotion/screens/_compatibility/formotion_screen.rb +65 -0
  17. data/lib/ProMotion/screens/_screen_module.rb +33 -20
  18. data/lib/ProMotion/screens/_table_screen_module.rb +4 -4
  19. data/lib/ProMotion/{screen_helpers → screens}/_tables/_refreshable_table.rb +2 -2
  20. data/lib/ProMotion/{screen_helpers → screens}/_tables/_searchable_table.rb +27 -34
  21. data/lib/ProMotion/screens/_tables/_sectioned_table.rb +5 -0
  22. data/lib/ProMotion/screens/_tables/_table.rb +149 -0
  23. data/lib/ProMotion/screens/_tables/grouped_table.rb +16 -0
  24. data/lib/ProMotion/screens/_tables/plain_table.rb +17 -0
  25. data/lib/ProMotion/screens/_tables/table_data.rb +148 -0
  26. data/lib/ProMotion/screens/_tables/table_view_cell_module.rb +156 -0
  27. data/lib/ProMotion/screens/table_screen.rb +3 -2
  28. data/lib/ProMotion/version.rb +1 -1
  29. data/spec/functional/func_screen_spec.rb +66 -0
  30. data/spec/functional/func_split_screen_spec.rb +66 -0
  31. data/spec/functional/func_table_screen_spec.rb +52 -0
  32. data/spec/helpers/functional_screen.rb +15 -0
  33. data/spec/helpers/table_screen.rb +32 -8
  34. data/spec/helpers/table_screen_refreshable.rb +1 -1
  35. data/spec/helpers/table_screen_searchable.rb +1 -1
  36. data/spec/helpers/test_delegate.rb +2 -0
  37. data/spec/unit/delegate_spec.rb +38 -0
  38. data/spec/{ios_version_spec.rb → unit/ios_version_spec.rb} +0 -0
  39. data/spec/{logger_spec.rb → unit/logger_spec.rb} +0 -0
  40. data/spec/{main_spec.rb → unit/main_spec.rb} +0 -0
  41. data/spec/{screen_helpers_spec.rb → unit/screen_helpers_spec.rb} +14 -6
  42. data/spec/{screen_module_spec.rb → unit/screen_module_spec.rb} +0 -0
  43. data/spec/{screen_spec.rb → unit/screen_spec.rb} +21 -3
  44. data/spec/{split_screen_in_tab_bar_spec.rb → unit/split_screen_in_tab_bar_spec.rb} +0 -0
  45. data/spec/{split_screen_open_screen_spec.rb → unit/split_screen_open_screen_spec.rb} +0 -0
  46. data/spec/{split_screen_spec.rb → unit/split_screen_spec.rb} +0 -0
  47. data/spec/unit/tables/table_module_spec.rb +108 -0
  48. data/spec/unit/tables/table_screen_spec.rb +92 -0
  49. data/spec/unit/tables/table_view_cell_spec.rb +106 -0
  50. data/spec/{view_helper_spec.rb → unit/view_helper_spec.rb} +0 -0
  51. metadata +50 -29
  52. data/lib/ProMotion/delegate.rb +0 -63
  53. data/lib/ProMotion/screen_helpers/_tables/_sectioned_table.rb +0 -270
  54. data/lib/ProMotion/screen_helpers/_tables/grouped_table.rb +0 -14
  55. data/lib/ProMotion/screen_helpers/_tables/plain_table.rb +0 -15
  56. data/spec/table_screen_spec.rb +0 -72
@@ -40,6 +40,10 @@ module ProMotion
40
40
  app_delegate.open_root_screen(screen)
41
41
  end
42
42
 
43
+ def open_modal(screen, args = {})
44
+ open screen, args.merge({ modal: true })
45
+ end
46
+
43
47
  def app_delegate
44
48
  UIApplication.sharedApplication.delegate
45
49
  end
@@ -48,7 +52,7 @@ module ProMotion
48
52
  args ||= {}
49
53
  args[:animated] ||= true
50
54
 
51
- if self.is_modal?
55
+ if self.modal?
52
56
  close_modal_screen args
53
57
 
54
58
  elsif self.navigation_controller
@@ -34,9 +34,9 @@ module ProMotion
34
34
  def open_tab_bar(*screens)
35
35
  tab_bar = tab_bar_controller(*screens)
36
36
 
37
- a = self.respond_to?(:load_root_screen) ? self : UIApplication.sharedApplication.delegate
37
+ a = self.respond_to?(:open_root_screen) ? self : UIApplication.sharedApplication.delegate
38
38
 
39
- a.load_root_screen(tab_bar)
39
+ a.open_root_screen(tab_bar)
40
40
  tab_bar
41
41
  end
42
42
 
@@ -0,0 +1,65 @@
1
+ module ProMotion
2
+ if defined?(Formotion) && defined?(Formotion::FormController)
3
+ class FormotionScreen < Formotion::FormController
4
+ include ProMotion::ScreenModule
5
+
6
+ def self.new(args = {})
7
+ s = self.alloc.initWithStyle(UITableViewStyleGrouped)
8
+ s.on_create(args) if s.respond_to?(:on_create)
9
+
10
+ if s.respond_to?(:table_data)
11
+ s.form = s.table_data
12
+ elsif args[:form]
13
+ s.form = args[:form]
14
+ else
15
+ PM.logger.error "PM::FormotionScreen requires a `table_data` method or form: to be passed into `new`."
16
+ end
17
+
18
+ s.tableView.allowsSelectionDuringEditing = true
19
+
20
+ s
21
+ end
22
+
23
+ def viewDidLoad
24
+ super
25
+ self.view_did_load if self.respond_to?(:view_did_load)
26
+ end
27
+
28
+ def viewWillAppear(animated)
29
+ super
30
+ self.view_will_appear(animated) if self.respond_to?(:view_will_appear)
31
+ end
32
+
33
+ def viewDidAppear(animated)
34
+ super
35
+ self.view_did_appear(animated) if self.respond_to?(:view_did_appear)
36
+ end
37
+
38
+ def viewWillDisappear(animated)
39
+ self.view_will_disappear(animated) if self.respond_to?(:view_will_disappear)
40
+ super
41
+ end
42
+
43
+ def viewDidDisappear(animated)
44
+ self.view_did_disappear(animated) if self.respond_to?(:view_did_disappear)
45
+ super
46
+ end
47
+
48
+ def shouldAutorotateToInterfaceOrientation(orientation)
49
+ self.should_rotate(orientation)
50
+ end
51
+
52
+ def shouldAutorotate
53
+ self.should_autorotate
54
+ end
55
+
56
+ def willRotateToInterfaceOrientation(orientation, duration:duration)
57
+ self.will_rotate(orientation, duration)
58
+ end
59
+
60
+ def didRotateFromInterfaceOrientation(orientation)
61
+ self.on_rotate
62
+ end
63
+ end
64
+ end
65
+ end
@@ -13,7 +13,6 @@ module ProMotion
13
13
  raise StandardError.new("ERROR: Screens must extend UIViewController or a subclass of UIViewController.")
14
14
  end
15
15
 
16
-
17
16
  self.title = self.class.send(:get_title)
18
17
 
19
18
  args.each do |k, v|
@@ -21,17 +20,28 @@ module ProMotion
21
20
  end
22
21
 
23
22
  self.add_nav_bar if args[:nav_bar]
23
+ self.navigationController.toolbarHidden = !args[:toolbar] unless args[:toolbar].nil?
24
24
  self.on_init if self.respond_to?(:on_init)
25
25
  self.table_setup if self.respond_to?(:table_setup)
26
26
  self
27
27
  end
28
28
 
29
29
  def is_modal?
30
+ PM.logger.deprecated "`is_modal?` is deprecated. Use `modal?`."
31
+ modal?
32
+ end
33
+
34
+ def modal?
30
35
  self.modal == true
31
36
  end
32
37
 
33
38
  def has_nav_bar?
34
- self.navigation_controller.nil? != true
39
+ PM.logger.deprecated "`has_nav_bar? is deprecated. Use `nav_bar?`."
40
+ nav_bar?
41
+ end
42
+
43
+ def nav_bar?
44
+ !!self.navigation_controller
35
45
  end
36
46
 
37
47
  def navigation_controller
@@ -45,7 +55,7 @@ module ProMotion
45
55
 
46
56
  # [DEPRECATED]
47
57
  def load_view_controller
48
- warn "[DEPRECATION] `load_view_controller` is deprecated and doesn't actually do anything anymore. You can safely remove it from your code."
58
+ PM.logger.deprecated "`load_view_controller` is deprecated and doesn't actually do anything anymore. You can safely remove it from your code."
49
59
  end
50
60
 
51
61
  def set_tab_bar_item(args = {})
@@ -74,25 +84,13 @@ module ProMotion
74
84
  set_nav_bar_button :left, args
75
85
  end
76
86
 
77
- # If you call set_nav_bar_button with a nil title and system_icon: UIBarButtonSystemItemAdd (or any other
78
- # system icon), the button is initialized with a barButtonSystemItem instead of a title.
79
87
  def set_nav_bar_button(side, args={})
80
88
  args[:style] ||= UIBarButtonItemStyleBordered
81
89
  args[:target] ||= self
82
90
  args[:action] ||= nil
91
+ button_type = args[:image] || args[:button] || args[:system_icon] || args[:title] || "Button"
83
92
 
84
- button = case args[:title]
85
- when String
86
- UIBarButtonItem.alloc.initWithTitle(args[:title], style: args[:style], target: args[:target], action: args[:action])
87
- when UIImage
88
- UIBarButtonItem.alloc.initWithImage(args[:title], style: args[:style], target: args[:target], action: args[:action])
89
- when Symbol, NilClass
90
- UIBarButtonItem.alloc.initWithBarButtonSystemItem(args[:system_icon], target: args[:target], action: args[:action]) if args[:system_icon]
91
- when UIBarButtonItem
92
- args[:title]
93
- else
94
- PM.logger.error("Please supply a title string, a UIImage or :system.")
95
- end
93
+ button = bar_button_item button_type, args
96
94
 
97
95
  self.navigationItem.leftBarButtonItem = button if side == :left
98
96
  self.navigationItem.rightBarButtonItem = button if side == :right
@@ -100,6 +98,23 @@ module ProMotion
100
98
  button
101
99
  end
102
100
 
101
+ def bar_button_item(button_type, args)
102
+ case button_type
103
+ when UIBarButtonItem
104
+ button_type
105
+ when UIImage
106
+ UIBarButtonItem.alloc.initWithImage(button_type, style: args[:style], target: args[:target], action: args[:action])
107
+ when String
108
+ UIBarButtonItem.alloc.initWithTitle(button_type, style: args[:style], target: args[:target], action: args[:action])
109
+ else
110
+ if args[:system_icon]
111
+ UIBarButtonItem.alloc.initWithBarButtonSystemItem(args[:system_icon], target: args[:target], action: args[:action])
112
+ else
113
+ PM.logger.error("Please supply a title string, a UIImage or :system.")
114
+ end
115
+ end
116
+ end
117
+
103
118
  # [DEPRECATED]
104
119
  def view_controller=(vc)
105
120
  set_view_controller(vc)
@@ -116,9 +131,6 @@ module ProMotion
116
131
  end
117
132
 
118
133
  def view_did_load; end
119
- def on_opened
120
- warn "[DEPRECATION] `on_opened` is deprecated. Please use `on_appear` instead."
121
- end
122
134
 
123
135
  def view_will_appear(animated)
124
136
  self.will_appear
@@ -193,6 +205,7 @@ module ProMotion
193
205
 
194
206
  def supported_device_families
195
207
  NSBundle.mainBundle.infoDictionary["UIDeviceFamily"].map do |m|
208
+ # TODO: What about universal apps?
196
209
  case m
197
210
  when "1"
198
211
  :iphone
@@ -1,9 +1,9 @@
1
1
  module ProMotion
2
2
  module TableScreenModule
3
- include MotionTable::PlainTable
4
- include MotionTable::SearchableTable
5
- include MotionTable::RefreshableTable
6
- include ProMotion::ScreenModule
3
+ include PlainTable
4
+ include SearchableTable
5
+ include RefreshableTable
6
+ include ScreenModule
7
7
 
8
8
  def update_table_data
9
9
  self.update_table_view_data(table_data)
@@ -1,4 +1,4 @@
1
- module ProMotion::MotionTable
1
+ module ProMotion
2
2
  module RefreshableTable
3
3
  def make_refreshable(params={})
4
4
  pull_message = params[:pull_message] || "Pull to refresh"
@@ -39,4 +39,4 @@ module ProMotion::MotionTable
39
39
  @refresh_control.endRefreshing
40
40
  end
41
41
  end
42
- end
42
+ end
@@ -1,18 +1,10 @@
1
- module ProMotion::MotionTable
1
+ module ProMotion
2
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
3
 
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
4
+ def make_searchable(params={})
5
+ params = set_searchable_param_defaults(params)
13
6
 
14
- search_bar = UISearchBar.alloc.initWithFrame(params[:frame])
15
- search_bar.autoresizingMask = UIViewAutoresizingFlexibleWidth
7
+ search_bar = create_search_bar(params)
16
8
 
17
9
  if params[:search_bar] && params[:search_bar][:placeholder]
18
10
  search_bar.placeholder = params[:search_bar][:placeholder]
@@ -27,40 +19,41 @@ module ProMotion::MotionTable
27
19
  end
28
20
  alias :makeSearchable :make_searchable
29
21
 
30
- ######### iOS methods, headless camel case #######
22
+ def set_searchable_param_defaults(params)
23
+ params[:content_controller] ||= params[:contentController]
24
+ params[:data_source] ||= params[:searchResultsDataSource]
25
+ params[:search_results_delegate] ||= params[:searchResultsDelegate]
31
26
 
32
- def searchDisplayController(controller, shouldReloadTableForSearchString:search_string)
33
- @mt_filtered_data = nil
34
- @mt_filtered_data = []
27
+ params[:frame] ||= CGRectMake(0, 0, 320, 44) # TODO: Don't hardcode this...
28
+ params[:content_controller] ||= self
29
+ params[:delegate] ||= self
30
+ params[:data_source] ||= self
31
+ params[:search_results_delegate] ||= self
32
+ params
33
+ end
35
34
 
36
- @mt_table_view_groups.each do |section|
37
- new_section = {}
38
- new_section[:cells] = []
35
+ def create_search_bar(params)
36
+ search_bar = UISearchBar.alloc.initWithFrame(params[:frame])
37
+ search_bar.autoresizingMask = UIViewAutoresizingFlexibleWidth
38
+ search_bar
39
+ end
39
40
 
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
41
+ ######### iOS methods, headless camel case #######
45
42
 
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
43
+ def searchDisplayController(controller, shouldReloadTableForSearchString:search_string)
44
+ @promotion_table_data.search(search_string)
51
45
  true
52
46
  end
53
47
 
54
48
  def searchDisplayControllerWillEndSearch(controller)
55
- @mt_filtered = false
56
- @mt_filtered_data = nil
49
+ @promotion_table_data.stop_searching
50
+ @promotion_table_data_data = nil
57
51
  self.table_view.setScrollEnabled true
58
52
  end
59
53
 
60
54
  def searchDisplayControllerWillBeginSearch(controller)
61
- @mt_filtered = true
62
- @mt_filtered_data = []
63
55
  self.table_view.setScrollEnabled false
64
56
  end
57
+
65
58
  end
66
- end
59
+ end
@@ -0,0 +1,5 @@
1
+ module ProMotion
2
+ module SectionedTable
3
+ include Table
4
+ end
5
+ end
@@ -0,0 +1,149 @@
1
+ module ProMotion
2
+ module Table
3
+ include ProMotion::ViewHelper
4
+
5
+ def table_setup
6
+ check_table_data
7
+ set_up_table_view
8
+ set_up_searchable
9
+ set_up_refreshable
10
+ end
11
+
12
+ def check_table_data
13
+ PM.logger.error "Missing #table_data method in TableScreen #{self.class.to_s}." unless self.respond_to?(:table_data)
14
+ end
15
+
16
+ def set_up_table_view
17
+ self.view = self.create_table_view_from_data(self.table_data)
18
+ end
19
+
20
+ def set_up_searchable
21
+ if self.class.respond_to?(:get_searchable) && self.class.get_searchable
22
+ self.make_searchable(content_controller: self, search_bar: self.class.get_searchable_params)
23
+ end
24
+ end
25
+
26
+ def set_up_refreshable
27
+ if self.class.respond_to?(:get_refreshable) && self.class.get_refreshable
28
+ if defined?(UIRefreshControl)
29
+ self.make_refreshable(self.class.get_refreshable_params)
30
+ else
31
+ PM.logger.warn "To use the refresh control on < iOS 6, you need to include the CocoaPod 'CKRefreshControl'."
32
+ end
33
+ end
34
+ end
35
+
36
+ def create_table_view_from_data(data)
37
+ @promotion_table_data = TableData.new(data, table_view)
38
+ table_view
39
+ end
40
+
41
+ def update_table_view_data(data)
42
+ @promotion_table_data.data = data
43
+ table_view.reloadData
44
+ end
45
+
46
+ # Methods to retrieve data
47
+
48
+ def section_at_index(index)
49
+ @promotion_table_data.section(index)
50
+ end
51
+
52
+ def cell_at_section_and_index(section, index)
53
+ @promotion_table_data.cell(section: section, index: index)
54
+ end
55
+
56
+ def trigger_action(action, arguments)
57
+ if self.respond_to?(action)
58
+ expected_arguments = self.method(action).arity
59
+ if expected_arguments == 0
60
+ self.send(action)
61
+ elsif expected_arguments == 1 || expected_arguments == -1
62
+ self.send(action, arguments)
63
+ else
64
+ PM.logger.warn "#{action} expects #{expected_arguments} arguments. Maximum number of required arguments for an action is 1."
65
+ end
66
+ else
67
+ PM.logger.info "Action not implemented: #{action.to_s}"
68
+ end
69
+ end
70
+
71
+ def accessory_toggled_switch(switch)
72
+ table_cell = switch.superview
73
+ index_path = table_cell.superview.indexPathForCell(table_cell)
74
+
75
+ data_cell = cell_at_section_and_index(index_path.section, index_path.row)
76
+ data_cell[:arguments] = {} unless data_cell[:arguments]
77
+ data_cell[:arguments][:value] = switch.isOn if data_cell[:arguments].is_a? Hash
78
+ data_cell[:accessory_action] ||= data_cell[:accessoryAction] # For legacy support
79
+
80
+ trigger_action(data_cell[:accessory_action], data_cell[:arguments]) if data_cell[:accessory_action]
81
+ end
82
+
83
+ def delete_row(index_paths, animation = nil)
84
+ animation ||= UITableViewRowAnimationAutomatic
85
+ index_paths = Array(index_paths)
86
+
87
+ index_paths.each do |index_path|
88
+ @promotion_table_data.delete_cell(index_path: index_path)
89
+ end
90
+ table_view.deleteRowsAtIndexPaths(index_paths, withRowAnimation:animation)
91
+ end
92
+
93
+ ########## Cocoa touch methods #################
94
+ def numberOfSectionsInTableView(table_view)
95
+ return Array(@promotion_table_data.data).length
96
+ end
97
+
98
+ # Number of cells
99
+ def tableView(table_view, numberOfRowsInSection:section)
100
+ return @promotion_table_data.section_length(section)
101
+ 0
102
+ end
103
+
104
+ def tableView(table_view, titleForHeaderInSection:section)
105
+ return section_at_index(section)[:title] if section_at_index(section) && section_at_index(section)[:title]
106
+ end
107
+
108
+ # Set table_data_index if you want the right hand index column (jumplist)
109
+ def sectionIndexTitlesForTableView(table_view)
110
+ self.table_data_index if self.respond_to?(:table_data_index)
111
+ end
112
+
113
+ def tableView(table_view, cellForRowAtIndexPath:index_path)
114
+ @promotion_table_data.table_view_cell(index_path: index_path)
115
+ end
116
+
117
+ def tableView(table_view, heightForRowAtIndexPath:index_path)
118
+ (@promotion_table_data.cell(index_path: index_path)[:height] || table_view.rowHeight).to_f
119
+ end
120
+
121
+ def tableView(table_view, didSelectRowAtIndexPath:index_path)
122
+ cell = @promotion_table_data.cell(index_path: index_path)
123
+ table_view.deselectRowAtIndexPath(index_path, animated: true)
124
+
125
+ cell[:arguments] ||= {}
126
+ cell[:arguments][:cell] = cell if cell[:arguments].is_a?(Hash) # TODO: Should we really do this?
127
+
128
+ trigger_action(cell[:action], cell[:arguments]) if cell[:action]
129
+ end
130
+
131
+ def tableView(tableView, commitEditingStyle:editing_style, forRowAtIndexPath:index_path)
132
+ if editing_style == UITableViewCellEditingStyleDelete
133
+ delete_cell(index_path)
134
+ end
135
+ end
136
+
137
+ def deleteRowsAtIndexPaths(indexPaths, withRowAnimation:animation)
138
+ PM.logger.warn "ProMotion expects you to use 'delete_cell(index_paths, animation)'' instead of 'deleteRowsAtIndexPaths(indexPaths, withRowAnimation:animation)'."
139
+ delete_cell(indexPaths, animation)
140
+ end
141
+
142
+
143
+ # Old aliases, deprecated, will be removed
144
+ alias :createTableViewFromData :create_table_view_from_data
145
+ alias :updateTableViewData :update_table_view_data
146
+ alias :cellAtSectionAndIndex :cell_at_section_and_index
147
+
148
+ end
149
+ end