ProMotion 0.6.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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