ProMotion 0.7.8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +1 -1
  3. data/ProMotion.gemspec +2 -0
  4. data/README.md +51 -449
  5. data/Rakefile +11 -1
  6. data/app/screens/basic_screen.rb +0 -24
  7. data/lib/ProMotion/cocoatouch/{NavigationController.rb → navigation_controller.rb} +0 -0
  8. data/lib/ProMotion/cocoatouch/split_view_controller.rb +29 -0
  9. data/lib/ProMotion/cocoatouch/tab_bar_controller.rb +43 -0
  10. data/lib/ProMotion/cocoatouch/{TableViewCell.rb → table_view_cell.rb} +0 -1
  11. data/lib/ProMotion/cocoatouch/{TableViewController.rb → table_view_controller.rb} +0 -0
  12. data/lib/ProMotion/cocoatouch/{ViewController.rb → view_controller.rb} +6 -1
  13. data/lib/ProMotion/{screen_helpers → containers}/split_screen.rb +0 -2
  14. data/lib/ProMotion/containers/tabs.rb +95 -0
  15. data/lib/ProMotion/delegate/delegate.rb +2 -13
  16. data/lib/ProMotion/delegate/{delegate_helper.rb → delegate_module.rb} +29 -16
  17. data/lib/ProMotion/delegate/delegate_notifications.rb +17 -8
  18. data/lib/ProMotion/delegate/delegate_parent.rb +11 -0
  19. data/lib/ProMotion/extensions/conversions.rb +20 -0
  20. data/lib/ProMotion/{helpers/logger.rb → logger.rb} +0 -0
  21. data/lib/ProMotion/map/map_screen.rb +6 -0
  22. data/lib/ProMotion/map/map_screen_annotation.rb +56 -0
  23. data/lib/ProMotion/map/map_screen_module.rb +207 -0
  24. data/lib/ProMotion/{push_notifications → push_notification}/push_notification.rb +13 -6
  25. data/lib/ProMotion/{screens → screen}/screen.rb +1 -1
  26. data/lib/ProMotion/{screens/_screen_module.rb → screen/screen_module.rb} +76 -51
  27. data/lib/ProMotion/{screen_helpers → screen}/screen_navigation.rb +8 -12
  28. data/lib/ProMotion/{screens/_tables → table/cell}/table_view_cell_module.rb +18 -27
  29. data/lib/ProMotion/table/data/table_data.rb +98 -0
  30. data/lib/ProMotion/table/extensions/indexable.rb +9 -0
  31. data/lib/ProMotion/table/extensions/refreshable.rb +45 -0
  32. data/lib/ProMotion/table/extensions/searchable.rb +61 -0
  33. data/lib/ProMotion/table/grouped_table.rb +9 -0
  34. data/lib/ProMotion/table/grouped_table_screen.rb +6 -0
  35. data/lib/ProMotion/table/table.rb +312 -0
  36. data/lib/ProMotion/table/table_screen.rb +6 -0
  37. data/lib/ProMotion/{screens/_compatibility → thirdparty}/formotion_screen.rb +18 -5
  38. data/lib/ProMotion/version.rb +1 -1
  39. data/lib/ProMotion/view/styling.rb +137 -0
  40. data/lib/ProMotion/web/web_screen.rb +6 -0
  41. data/lib/ProMotion/web/web_screen_module.rb +155 -0
  42. data/resources/WebScreen.html +6 -0
  43. data/resources/test.jpeg +0 -0
  44. data/spec/functional/func_map_screen_spec.rb +105 -0
  45. data/spec/functional/func_screen_spec.rb +54 -7
  46. data/spec/functional/func_searchable_table_spec.rb +1 -1
  47. data/spec/functional/func_tab_bar_spec.rb +78 -0
  48. data/spec/functional/func_table_screen_spec.rb +61 -14
  49. data/spec/functional/func_web_screen_spec.rb +51 -0
  50. data/spec/helpers/map_screen.rb +51 -0
  51. data/spec/helpers/present_screen.rb +26 -0
  52. data/spec/helpers/tab_screen.rb +4 -0
  53. data/spec/helpers/table_screen.rb +12 -3
  54. data/spec/helpers/table_screen_formotion.rb +25 -0
  55. data/spec/helpers/table_screen_indexable.rb +13 -0
  56. data/spec/helpers/test_delegate.rb +28 -0
  57. data/spec/helpers/web_screen.rb +22 -0
  58. data/spec/unit/delegate_spec.rb +53 -4
  59. data/spec/unit/map_spec.rb +48 -0
  60. data/spec/unit/screen_helpers_spec.rb +24 -16
  61. data/spec/unit/screen_spec.rb +22 -18
  62. data/spec/unit/split_screen_in_tab_bar_spec.rb +2 -2
  63. data/spec/unit/split_screen_open_screen_spec.rb +15 -10
  64. data/spec/unit/split_screen_spec.rb +2 -2
  65. data/spec/unit/tab_spec.rb +41 -0
  66. data/spec/unit/tables/formotion_screen_spec.rb +16 -0
  67. data/spec/unit/tables/table_indexable_spec.rb +12 -0
  68. data/spec/unit/tables/table_module_spec.rb +24 -9
  69. data/spec/unit/tables/table_screen_spec.rb +1 -1
  70. data/spec/unit/tables/table_view_cell_spec.rb +9 -8
  71. data/spec/unit/view_helper_spec.rb +2 -2
  72. data/spec/unit/web_spec.rb +106 -0
  73. metadata +96 -35
  74. data/lib/ProMotion/cocoatouch/SplitViewController.rb +0 -23
  75. data/lib/ProMotion/helpers/console.rb +0 -29
  76. data/lib/ProMotion/helpers/measure_helper.rb +0 -20
  77. data/lib/ProMotion/helpers/system_helper.rb +0 -29
  78. data/lib/ProMotion/helpers/view_helper.rb +0 -82
  79. data/lib/ProMotion/screen_helpers/screen_elements.rb +0 -36
  80. data/lib/ProMotion/screen_helpers/screen_tabs.rb +0 -95
  81. data/lib/ProMotion/screens/_table_screen_module.rb +0 -47
  82. data/lib/ProMotion/screens/_tables/_refreshable_table.rb +0 -45
  83. data/lib/ProMotion/screens/_tables/_searchable_table.rb +0 -60
  84. data/lib/ProMotion/screens/_tables/_sectioned_table.rb +0 -5
  85. data/lib/ProMotion/screens/_tables/_table.rb +0 -169
  86. data/lib/ProMotion/screens/_tables/grouped_table.rb +0 -16
  87. data/lib/ProMotion/screens/_tables/plain_table.rb +0 -17
  88. data/lib/ProMotion/screens/_tables/table_data.rb +0 -175
  89. data/lib/ProMotion/screens/behaves_like_screen.rb +0 -10
  90. data/lib/ProMotion/screens/table_screen.rb +0 -16
  91. data/spec/unit/ios_version_spec.rb +0 -28
@@ -8,13 +8,11 @@ module ProMotion
8
8
  screen = set_up_screen_for_open(screen, args)
9
9
  ensure_wrapper_controller_in_place(screen, args)
10
10
 
11
- screen.send(:on_load) if screen.respond_to?(:on_load)
12
-
13
11
  if args[:in_detail] && self.split_screen
14
12
  self.split_screen.detail_screen = screen
15
13
 
16
14
  elsif args[:in_master] && self.split_screen
17
- self.split_screen.master_screen = screen
15
+ self.split_screen.master_screen = screen
18
16
 
19
17
  elsif args[:close_all]
20
18
  open_root_screen screen
@@ -29,10 +27,11 @@ module ProMotion
29
27
  push_view_controller screen
30
28
 
31
29
  else
32
- open_root_screen screen.pm_main_controller
30
+ open_root_screen (screen.navigationController || screen)
33
31
 
34
32
  end
35
33
 
34
+ screen
36
35
  end
37
36
  alias :open :open_screen
38
37
 
@@ -77,11 +76,6 @@ module ProMotion
77
76
  end
78
77
  end
79
78
 
80
- def open_view_controller(screen)
81
- PM.logger.deprecated "Use `open_root_screen` instead of the more ambiguous `open_view_controller`."
82
- open_root_screen screen
83
- end
84
-
85
79
  def push_view_controller(vc, nav_controller=nil)
86
80
  unless self.navigation_controller
87
81
  PM.logger.error "You need a nav_bar if you are going to push #{vc.to_s} onto it."
@@ -98,7 +92,7 @@ module ProMotion
98
92
 
99
93
  # Instantiate screen if given a class
100
94
  screen = screen.new if screen.respond_to?(:new)
101
-
95
+
102
96
  # Set parent
103
97
  screen.parent_screen = self if screen.respond_to?(:parent_screen=)
104
98
 
@@ -125,7 +119,7 @@ module ProMotion
125
119
  end
126
120
 
127
121
  def present_modal_view_controller(screen, animated)
128
- self.presentModalViewController(screen.pm_main_controller, animated:animated)
122
+ self.presentModalViewController((screen.navigationController || screen), animated:animated)
129
123
  end
130
124
 
131
125
  def present_view_controller_in_tab_bar_controller(screen, tab_name)
@@ -155,7 +149,9 @@ module ProMotion
155
149
 
156
150
  def close_nav_screen(args={})
157
151
  args[:animated] = true unless args.has_key?(:animated)
158
- if args[:to_screen] && args[:to_screen].is_a?(UIViewController)
152
+ if args[:to_screen] == :root
153
+ self.navigation_controller.popToRootViewControllerAnimated args[:animated]
154
+ elsif args[:to_screen] && args[:to_screen].is_a?(UIViewController)
159
155
  self.parent_screen = args[:to_screen]
160
156
  self.navigation_controller.popToViewController(args[:to_screen], animated: args[:animated])
161
157
  else
@@ -1,16 +1,15 @@
1
1
  module ProMotion
2
2
  module TableViewCellModule
3
- include ViewHelper
3
+ include Styling
4
4
 
5
- attr_accessor :data_cell
5
+ attr_accessor :data_cell, :table_screen
6
6
 
7
- def setup(data_cell)
7
+ def setup(data_cell, screen)
8
+ self.table_screen = WeakRef.new(screen)
8
9
  self.data_cell = data_cell
9
10
 
10
11
  # TODO: Some of these need to go away. Unnecessary overhead.
11
12
  set_cell_attributes
12
- set_background_color
13
- set_class_attributes
14
13
  set_accessory_view
15
14
  set_subtitle
16
15
  set_image
@@ -25,33 +24,25 @@ module ProMotion
25
24
 
26
25
  def set_cell_attributes
27
26
  data_cell_attributes = data_cell.dup
28
- [:image, :accessory_action].each { |k| data_cell_attributes.delete(k) }
27
+ [:image, :accessory_action, :editing_style].each { |k| data_cell_attributes.delete(k) }
29
28
  set_attributes self, data_cell_attributes
30
29
  self
31
30
  end
32
31
 
33
- def set_background_color
34
- self.backgroundView = UIView.new.tap{|v| v.backgroundColor = data_cell[:background_color]} if data_cell[:background_color]
35
- end
36
-
37
- def set_class_attributes
38
- if data_cell[:cell_class_attributes]
39
- PM.logger.deprecated "`cell_class_attributes` is deprecated. Just add the attributes you want to set right into your cell hash."
40
- set_attributes self, data_cell[:cell_class_attributes]
41
- end
42
- self
43
- end
44
-
45
32
  def set_accessory_view
46
- if data_cell[:accessory][:view] == :switch
47
- switch_view = UISwitch.alloc.initWithFrame(CGRectZero)
48
- switch_view.setAccessibilityLabel(data_cell[:accessory][:accessibility_label]) if data_cell[:accessory][:accessibility_label]
49
- switch_view.addTarget(self.closest_parent(UITableView), action: "accessory_toggled_switch:", forControlEvents:UIControlEventValueChanged)
50
- switch_view.on = true if data_cell[:accessory][:value]
51
- self.accessoryView = switch_view
52
- elsif data_cell[:accessory][:view]
53
- self.accessoryView = data_cell[:accessory][:view]
54
- self.accessoryView.autoresizingMask = UIViewAutoresizingFlexibleWidth
33
+ if data_cell[:accessory]
34
+ if data_cell[:accessory][:view] == :switch
35
+ switch_view = UISwitch.alloc.initWithFrame(CGRectZero)
36
+ switch_view.setAccessibilityLabel(data_cell[:accessory][:accessibility_label] || data_cell[:title])
37
+ switch_view.addTarget(self.table_screen, action: "accessory_toggled_switch:", forControlEvents:UIControlEventValueChanged)
38
+ switch_view.on = !!data_cell[:accessory][:value]
39
+ self.accessoryView = switch_view
40
+ elsif data_cell[:accessory][:view]
41
+ self.accessoryView = data_cell[:accessory][:view]
42
+ self.accessoryView.autoresizingMask = UIViewAutoresizingFlexibleWidth
43
+ end
44
+ else
45
+ self.accessoryView = nil
55
46
  end
56
47
 
57
48
  self
@@ -0,0 +1,98 @@
1
+ module ProMotion
2
+ class TableData
3
+ attr_accessor :data, :filtered_data, :filtered, :table_view
4
+
5
+ def initialize(data, table_view)
6
+ self.data = data
7
+ self.table_view = table_view
8
+ end
9
+
10
+ def section(index)
11
+ s = sections.at(index)
12
+ s || { title: nil, cells: [] }
13
+ end
14
+
15
+ def sections
16
+ self.filtered ? self.filtered_data : self.data
17
+ end
18
+
19
+ def section_length(index)
20
+ section(index)[:cells].length
21
+ end
22
+
23
+ def cell(params={})
24
+ if params[:index_path]
25
+ params[:section] = params[:index_path].section
26
+ params[:index] = params[:index_path].row
27
+ end
28
+
29
+ table_section = self.section(params[:section])
30
+ c = table_section[:cells].at(params[:index].to_i)
31
+ set_data_cell_defaults c
32
+ end
33
+
34
+ def delete_cell(params={})
35
+ if params[:index_path]
36
+ params[:section] = params[:index_path].section
37
+ params[:index] = params[:index_path].row
38
+ end
39
+
40
+ table_section = self.section(params[:section])
41
+ table_section[:cells].delete_at(params[:index].to_i)
42
+ end
43
+
44
+ def search(search_string)
45
+ self.filtered_data = []
46
+ self.filtered = true
47
+
48
+ search_string = search_string.downcase.strip
49
+
50
+ self.data.compact.each do |section|
51
+ new_section = {}
52
+ new_section[:cells] = []
53
+
54
+ new_section[:cells] = section[:cells].map do |cell|
55
+ cell[:searchable] != false && "#{cell[:title]}\n#{cell[:search_text]}".downcase.strip.include?(search_string) ? cell : nil
56
+ end.compact
57
+
58
+ if new_section[:cells] && new_section[:cells].length > 0
59
+ new_section[:title] = section[:title]
60
+ self.filtered_data << new_section
61
+ end
62
+ end
63
+
64
+ self.filtered_data
65
+ end
66
+
67
+ def stop_searching
68
+ self.filtered_data = []
69
+ self.filtered = false
70
+ end
71
+
72
+ def set_data_cell_defaults(data_cell)
73
+ data_cell[:cell_style] ||= UITableViewCellStyleDefault
74
+ data_cell[:cell_class] ||= PM::TableViewCell
75
+ data_cell[:cell_identifier] ||= build_cell_identifier(data_cell)
76
+
77
+ data_cell[:accessory] = {
78
+ view: data_cell[:accessory],
79
+ value: data_cell[:accessory_value],
80
+ action: data_cell[:accessory_action],
81
+ arguments: data_cell[:accessory_arguments]
82
+ } unless data_cell[:accessory].nil? || data_cell[:accessory].is_a?(Hash)
83
+
84
+ data_cell
85
+ end
86
+
87
+ def build_cell_identifier(data_cell)
88
+ ident = "#{data_cell[:cell_class].to_s}"
89
+ ident << "-#{data_cell[:stylename].to_s}" if data_cell[:stylename] # For Teacup
90
+ ident << "-accessory" if data_cell[:accessory]
91
+ ident << "-subtitle" if data_cell[:subtitle]
92
+ ident << "-remoteimage" if data_cell[:remote_image]
93
+ ident << "-image" if data_cell[:image]
94
+ ident
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,9 @@
1
+ module ProMotion
2
+ module Table
3
+ module Indexable
4
+ def index_from_section_titles
5
+ @promotion_table_data.filtered ? nil : @promotion_table_data.sections.collect{ |section| section[:title][0] }
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,45 @@
1
+ module ProMotion
2
+ module Table
3
+ module Refreshable
4
+ def make_refreshable(params={})
5
+ pull_message = params[:pull_message] || "Pull to refresh"
6
+ @refreshing = params[:refreshing] || "Refreshing data..."
7
+ @updated_format = params[:updated_format] || "Last updated at %s"
8
+ @updated_time_format = params[:updated_time_format] || "%l:%M %p"
9
+ @refreshable_callback = params[:callback] || :on_refresh
10
+
11
+ @refresh_control = UIRefreshControl.alloc.init
12
+ @refresh_control.attributedTitle = NSAttributedString.alloc.initWithString(pull_message)
13
+ @refresh_control.addTarget(self, action:'refreshView:', forControlEvents:UIControlEventValueChanged)
14
+ self.refreshControl = @refresh_control
15
+ end
16
+
17
+ def start_refreshing
18
+ return unless @refresh_control
19
+
20
+ @refresh_control.beginRefreshing
21
+ end
22
+ alias :begin_refreshing :start_refreshing
23
+
24
+ def end_refreshing
25
+ return unless @refresh_control
26
+
27
+ @refresh_control.attributedTitle = NSAttributedString.alloc.initWithString(sprintf(@updated_format, Time.now.strftime(@updated_time_format)))
28
+ @refresh_control.endRefreshing
29
+ end
30
+ alias :stop_refreshing :end_refreshing
31
+
32
+ ######### iOS methods, headless camel case #######
33
+
34
+ # UIRefreshControl Delegates
35
+ def refreshView(refresh)
36
+ refresh.attributedTitle = NSAttributedString.alloc.initWithString(@refreshing)
37
+ if @refreshable_callback && self.respond_to?(@refreshable_callback)
38
+ self.send(@refreshable_callback)
39
+ else
40
+ PM.logger.warn "You must implement the '#{@refreshable_callback}' method in your TableScreen."
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,61 @@
1
+ module ProMotion
2
+ module Table
3
+ module Searchable
4
+
5
+ def make_searchable(params={})
6
+ params = set_searchable_param_defaults(params)
7
+
8
+ search_bar = create_search_bar(params)
9
+
10
+ if params[:search_bar] && params[:search_bar][:placeholder]
11
+ search_bar.placeholder = params[:search_bar][:placeholder]
12
+ end
13
+
14
+ @table_search_display_controller = UISearchDisplayController.alloc.initWithSearchBar(search_bar, contentsController: params[:content_controller])
15
+ @table_search_display_controller.delegate = params[:delegate]
16
+ @table_search_display_controller.searchResultsDataSource = params[:data_source]
17
+ @table_search_display_controller.searchResultsDelegate = params[:search_results_delegate]
18
+
19
+ self.table_view.tableHeaderView = search_bar
20
+ end
21
+ alias :makeSearchable :make_searchable
22
+
23
+ def set_searchable_param_defaults(params)
24
+ params[:content_controller] ||= params[:contentController]
25
+ params[:data_source] ||= params[:searchResultsDataSource]
26
+ params[:search_results_delegate] ||= params[:searchResultsDelegate]
27
+
28
+ params[:frame] ||= CGRectMake(0, 0, 320, 44) # TODO: Don't hardcode this...
29
+ params[:content_controller] ||= self
30
+ params[:delegate] ||= self
31
+ params[:data_source] ||= self
32
+ params[:search_results_delegate] ||= self
33
+ params
34
+ end
35
+
36
+ def create_search_bar(params)
37
+ search_bar = UISearchBar.alloc.initWithFrame(params[:frame])
38
+ search_bar.autoresizingMask = UIViewAutoresizingFlexibleWidth
39
+ search_bar
40
+ end
41
+
42
+ ######### iOS methods, headless camel case #######
43
+
44
+ def searchDisplayController(controller, shouldReloadTableForSearchString:search_string)
45
+ @promotion_table_data.search(search_string)
46
+ true
47
+ end
48
+
49
+ def searchDisplayControllerWillEndSearch(controller)
50
+ @promotion_table_data.stop_searching
51
+ @promotion_table_data_data = nil
52
+ self.table_view.setScrollEnabled true
53
+ self.table_view.reloadData
54
+ end
55
+
56
+ def searchDisplayControllerWillBeginSearch(controller)
57
+ self.table_view.setScrollEnabled false
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,9 @@
1
+ module ProMotion
2
+ module GroupedTable
3
+ include ProMotion::Table
4
+
5
+ def table_style
6
+ UITableViewStyleGrouped
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module ProMotion
2
+ class GroupedTableScreen < TableViewController
3
+ include ProMotion::ScreenModule
4
+ include ProMotion::GroupedTable
5
+ end
6
+ end
@@ -0,0 +1,312 @@
1
+ module ProMotion
2
+ module Table
3
+
4
+ include ProMotion::Styling
5
+ include ProMotion::Table::Searchable
6
+ include ProMotion::Table::Refreshable
7
+ include ProMotion::Table::Indexable
8
+
9
+ def table_view
10
+ @table_view ||= begin
11
+ t = UITableView.alloc.initWithFrame(self.view.frame, style: table_style)
12
+ t.dataSource = self
13
+ t.delegate = self
14
+ t
15
+ end
16
+ end
17
+
18
+ def table_style
19
+ UITableViewStylePlain
20
+ end
21
+
22
+ def screen_setup
23
+ check_table_data
24
+ set_up_table_view
25
+ set_up_searchable
26
+ set_up_refreshable
27
+ end
28
+
29
+ def check_table_data
30
+ PM.logger.error "Missing #table_data method in TableScreen #{self.class.to_s}." unless self.respond_to?(:table_data)
31
+ end
32
+
33
+ def set_up_table_view
34
+ self.view = self.create_table_view_from_data(self.table_data)
35
+ end
36
+
37
+ def set_up_searchable
38
+ if self.class.respond_to?(:get_searchable) && self.class.get_searchable
39
+ self.make_searchable(content_controller: self, search_bar: self.class.get_searchable_params)
40
+ end
41
+ end
42
+
43
+ def set_up_refreshable
44
+ if self.class.respond_to?(:get_refreshable) && self.class.get_refreshable
45
+ if defined?(UIRefreshControl)
46
+ self.make_refreshable(self.class.get_refreshable_params)
47
+ else
48
+ PM.logger.warn "To use the refresh control on < iOS 6, you need to include the CocoaPod 'CKRefreshControl'."
49
+ end
50
+ end
51
+ end
52
+
53
+ def create_table_view_from_data(data)
54
+ @promotion_table_data = TableData.new(data, table_view)
55
+ table_view
56
+ end
57
+
58
+ def searching?
59
+ @promotion_table_data.filtered
60
+ end
61
+
62
+ def update_table_view_data(data)
63
+ @promotion_table_data.data = data
64
+ table_view.reloadData
65
+ end
66
+
67
+ # Methods to retrieve data
68
+
69
+ def section_at_index(index)
70
+ @promotion_table_data.section(index)
71
+ end
72
+
73
+ def cell_at_section_and_index(section, index)
74
+ @promotion_table_data.cell(section: section, index: index)
75
+ end
76
+
77
+ def trigger_action(action, arguments)
78
+ if self.respond_to?(action)
79
+ expected_arguments = self.method(action).arity
80
+ if expected_arguments == 0
81
+ self.send(action)
82
+ elsif expected_arguments == 1 || expected_arguments == -1
83
+ self.send(action, arguments)
84
+ else
85
+ PM.logger.warn "#{action} expects #{expected_arguments} arguments. Maximum number of required arguments for an action is 1."
86
+ end
87
+ else
88
+ PM.logger.info "Action not implemented: #{action.to_s}"
89
+ end
90
+ end
91
+
92
+ def accessory_toggled_switch(switch)
93
+ table_cell = closest_parent(UITableViewCell, switch)
94
+ index_path = closest_parent(UITableView, table_cell).indexPathForCell(table_cell)
95
+
96
+ if index_path
97
+ data_cell = cell_at_section_and_index(index_path.section, index_path.row)
98
+ data_cell[:accessory][:arguments] ||= {}
99
+ data_cell[:accessory][:arguments][:value] = switch.isOn if data_cell[:accessory][:arguments].is_a?(Hash)
100
+
101
+ trigger_action(data_cell[:accessory][:action], data_cell[:accessory][:arguments]) if data_cell[:accessory][:action]
102
+ end
103
+ end
104
+
105
+ def delete_row(index_paths, animation = nil)
106
+ animation = map_row_animation_symbol(animation)
107
+ index_paths = Array(index_paths)
108
+ deletable_index_paths = []
109
+
110
+ index_paths.each do |index_path|
111
+ delete_cell = false
112
+ delete_cell = send(:on_cell_deleted, @promotion_table_data.cell(index_path: index_path)) if self.respond_to?("on_cell_deleted:")
113
+ unless delete_cell == false
114
+ @promotion_table_data.delete_cell(index_path: index_path)
115
+ deletable_index_paths << index_path
116
+ end
117
+ end
118
+ table_view.deleteRowsAtIndexPaths(deletable_index_paths, withRowAnimation:animation) if deletable_index_paths.length > 0
119
+ end
120
+
121
+ def table_view_cell(params={})
122
+ if params[:index_path]
123
+ params[:section] = params[:index_path].section
124
+ params[:index] = params[:index_path].row
125
+ end
126
+
127
+ data_cell = @promotion_table_data.cell(section: params[:section], index: params[:index])
128
+ return UITableViewCell.alloc.init unless data_cell # No data?
129
+
130
+ table_cell = create_table_cell(data_cell)
131
+
132
+ table_cell
133
+ end
134
+
135
+ def create_table_cell(data_cell)
136
+ table_cell = table_view.dequeueReusableCellWithIdentifier(data_cell[:cell_identifier])
137
+
138
+ unless table_cell
139
+ data_cell[:cell_style] ||= UITableViewCellStyleSubtitle
140
+ table_cell = data_cell[:cell_class].alloc.initWithStyle(data_cell[:cell_style], reuseIdentifier:data_cell[:cell_identifier])
141
+ table_cell.extend PM::TableViewCellModule unless table_cell.is_a?(PM::TableViewCellModule)
142
+ table_cell.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin
143
+ end
144
+
145
+ table_cell.setup(data_cell, self)
146
+
147
+ table_cell
148
+ end
149
+
150
+ def update_table_data
151
+ self.update_table_view_data(self.table_data)
152
+ end
153
+
154
+ ########## Cocoa touch methods #################
155
+ def numberOfSectionsInTableView(table_view)
156
+ return @promotion_table_data.data.size
157
+ end
158
+
159
+ # Number of cells
160
+ def tableView(table_view, numberOfRowsInSection:section)
161
+ return @promotion_table_data.section_length(section)
162
+ 0
163
+ end
164
+
165
+ def tableView(table_view, titleForHeaderInSection:section)
166
+ return section_at_index(section)[:title] if section_at_index(section) && section_at_index(section)[:title]
167
+ end
168
+
169
+ # Set table_data_index if you want the right hand index column (jumplist)
170
+ def sectionIndexTitlesForTableView(table_view)
171
+ if @promotion_table_data.filtered
172
+ nil
173
+ else
174
+ if self.respond_to?(:table_data_index)
175
+ self.table_data_index
176
+ elsif self.class.get_indexable
177
+ self.index_from_section_titles
178
+ else
179
+ nil
180
+ end
181
+ end
182
+ end
183
+
184
+ def tableView(table_view, cellForRowAtIndexPath:index_path)
185
+ table_view_cell(index_path: index_path)
186
+ end
187
+
188
+ def tableView(table_view, willDisplayCell: table_cell, forRowAtIndexPath: index_path)
189
+ data_cell = @promotion_table_data.cell(index_path: index_path)
190
+ table_cell.backgroundColor = data_cell[:background_color] || UIColor.whiteColor
191
+ table_cell.send(:restyle!) if table_cell.respond_to?(:restyle!)
192
+ end
193
+
194
+ def tableView(table_view, heightForRowAtIndexPath:index_path)
195
+ (@promotion_table_data.cell(index_path: index_path)[:height] || table_view.rowHeight).to_f
196
+ end
197
+
198
+ def tableView(table_view, didSelectRowAtIndexPath:index_path)
199
+ data_cell = @promotion_table_data.cell(index_path: index_path)
200
+ table_view.deselectRowAtIndexPath(index_path, animated: true)
201
+
202
+ data_cell[:arguments] ||= {}
203
+ data_cell[:arguments][:cell] = data_cell if data_cell[:arguments].is_a?(Hash) # TODO: Should we really do this?
204
+
205
+ trigger_action(data_cell[:action], data_cell[:arguments]) if data_cell[:action]
206
+ end
207
+
208
+ def tableView(table_view, editingStyleForRowAtIndexPath: index_path)
209
+ data_cell = @promotion_table_data.cell(index_path: index_path)
210
+
211
+ case data_cell[:editing_style]
212
+ when nil, :none
213
+ UITableViewCellEditingStyleNone
214
+ when :delete
215
+ UITableViewCellEditingStyleDelete
216
+ when :insert
217
+ UITableViewCellEditingStyleInsert
218
+ else
219
+ data_cell[:editing_style]
220
+ end
221
+ end
222
+
223
+ def tableView(table_view, commitEditingStyle: editing_style, forRowAtIndexPath: index_path)
224
+ if editing_style == UITableViewCellEditingStyleDelete
225
+ delete_row(index_path)
226
+ end
227
+ end
228
+
229
+ def tableView(tableView, sectionForSectionIndexTitle:title, atIndex:index)
230
+ return index unless ["{search}", UITableViewIndexSearch].include?(self.table_data_index[0])
231
+
232
+ if index == 0
233
+ tableView.setContentOffset(CGPointZero, animated:false)
234
+ NSNotFound
235
+ else
236
+ index - 1
237
+ end
238
+ end
239
+
240
+ def deleteRowsAtIndexPaths(index_paths, withRowAnimation:animation)
241
+ PM.logger.warn "ProMotion expects you to use 'delete_cell(index_paths, animation)'' instead of 'deleteRowsAtIndexPaths(index_paths, withRowAnimation:animation)'."
242
+ delete_row(index_paths, animation)
243
+ end
244
+
245
+ protected
246
+
247
+ def map_row_animation_symbol(symbol)
248
+ symbol ||= UITableViewRowAnimationAutomatic
249
+ {
250
+ automatic: UITableViewRowAnimationAutomatic,
251
+ fade: UITableViewRowAnimationFade,
252
+ right: UITableViewRowAnimationRight,
253
+ left: UITableViewRowAnimationLeft,
254
+ top: UITableViewRowAnimationTop,
255
+ bottom: UITableViewRowAnimationBottom,
256
+ none: UITableViewRowAnimationNone,
257
+ middle: UITableViewRowAnimationMiddle,
258
+ automatic: UITableViewRowAnimationAutomatic
259
+ }[symbol] || symbol
260
+ end
261
+
262
+ module TableClassMethods
263
+ # Searchable
264
+ def searchable(params={})
265
+ @searchable_params = params
266
+ @searchable = true
267
+ end
268
+
269
+ def get_searchable_params
270
+ @searchable_params ||= nil
271
+ end
272
+
273
+ def get_searchable
274
+ @searchable ||= false
275
+ end
276
+
277
+ # Refreshable
278
+ def refreshable(params = {})
279
+ @refreshable_params = params
280
+ @refreshable = true
281
+ end
282
+
283
+ def get_refreshable
284
+ @refreshable ||= false
285
+ end
286
+
287
+ def get_refreshable_params
288
+ @refreshable_params ||= nil
289
+ end
290
+
291
+ # Indexable
292
+ def indexable(params = {})
293
+ @indexable_params = params
294
+ @indexable = true
295
+ end
296
+
297
+ def get_indexable
298
+ @indexable ||= false
299
+ end
300
+
301
+ def get_indexable_params
302
+ @indexable_params ||= nil
303
+ end
304
+
305
+ end
306
+
307
+ def self.included(base)
308
+ base.extend(TableClassMethods)
309
+ end
310
+
311
+ end
312
+ end