ProMotion 0.7.8 → 1.0.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.
- data/.gitignore +2 -1
- data/.travis.yml +1 -1
- data/ProMotion.gemspec +2 -0
- data/README.md +51 -449
- data/Rakefile +11 -1
- data/app/screens/basic_screen.rb +0 -24
- data/lib/ProMotion/cocoatouch/{NavigationController.rb → navigation_controller.rb} +0 -0
- data/lib/ProMotion/cocoatouch/split_view_controller.rb +29 -0
- data/lib/ProMotion/cocoatouch/tab_bar_controller.rb +43 -0
- data/lib/ProMotion/cocoatouch/{TableViewCell.rb → table_view_cell.rb} +0 -1
- data/lib/ProMotion/cocoatouch/{TableViewController.rb → table_view_controller.rb} +0 -0
- data/lib/ProMotion/cocoatouch/{ViewController.rb → view_controller.rb} +6 -1
- data/lib/ProMotion/{screen_helpers → containers}/split_screen.rb +0 -2
- data/lib/ProMotion/containers/tabs.rb +95 -0
- data/lib/ProMotion/delegate/delegate.rb +2 -13
- data/lib/ProMotion/delegate/{delegate_helper.rb → delegate_module.rb} +29 -16
- data/lib/ProMotion/delegate/delegate_notifications.rb +17 -8
- data/lib/ProMotion/delegate/delegate_parent.rb +11 -0
- data/lib/ProMotion/extensions/conversions.rb +20 -0
- data/lib/ProMotion/{helpers/logger.rb → logger.rb} +0 -0
- data/lib/ProMotion/map/map_screen.rb +6 -0
- data/lib/ProMotion/map/map_screen_annotation.rb +56 -0
- data/lib/ProMotion/map/map_screen_module.rb +207 -0
- data/lib/ProMotion/{push_notifications → push_notification}/push_notification.rb +13 -6
- data/lib/ProMotion/{screens → screen}/screen.rb +1 -1
- data/lib/ProMotion/{screens/_screen_module.rb → screen/screen_module.rb} +76 -51
- data/lib/ProMotion/{screen_helpers → screen}/screen_navigation.rb +8 -12
- data/lib/ProMotion/{screens/_tables → table/cell}/table_view_cell_module.rb +18 -27
- data/lib/ProMotion/table/data/table_data.rb +98 -0
- data/lib/ProMotion/table/extensions/indexable.rb +9 -0
- data/lib/ProMotion/table/extensions/refreshable.rb +45 -0
- data/lib/ProMotion/table/extensions/searchable.rb +61 -0
- data/lib/ProMotion/table/grouped_table.rb +9 -0
- data/lib/ProMotion/table/grouped_table_screen.rb +6 -0
- data/lib/ProMotion/table/table.rb +312 -0
- data/lib/ProMotion/table/table_screen.rb +6 -0
- data/lib/ProMotion/{screens/_compatibility → thirdparty}/formotion_screen.rb +18 -5
- data/lib/ProMotion/version.rb +1 -1
- data/lib/ProMotion/view/styling.rb +137 -0
- data/lib/ProMotion/web/web_screen.rb +6 -0
- data/lib/ProMotion/web/web_screen_module.rb +155 -0
- data/resources/WebScreen.html +6 -0
- data/resources/test.jpeg +0 -0
- data/spec/functional/func_map_screen_spec.rb +105 -0
- data/spec/functional/func_screen_spec.rb +54 -7
- data/spec/functional/func_searchable_table_spec.rb +1 -1
- data/spec/functional/func_tab_bar_spec.rb +78 -0
- data/spec/functional/func_table_screen_spec.rb +61 -14
- data/spec/functional/func_web_screen_spec.rb +51 -0
- data/spec/helpers/map_screen.rb +51 -0
- data/spec/helpers/present_screen.rb +26 -0
- data/spec/helpers/tab_screen.rb +4 -0
- data/spec/helpers/table_screen.rb +12 -3
- data/spec/helpers/table_screen_formotion.rb +25 -0
- data/spec/helpers/table_screen_indexable.rb +13 -0
- data/spec/helpers/test_delegate.rb +28 -0
- data/spec/helpers/web_screen.rb +22 -0
- data/spec/unit/delegate_spec.rb +53 -4
- data/spec/unit/map_spec.rb +48 -0
- data/spec/unit/screen_helpers_spec.rb +24 -16
- data/spec/unit/screen_spec.rb +22 -18
- data/spec/unit/split_screen_in_tab_bar_spec.rb +2 -2
- data/spec/unit/split_screen_open_screen_spec.rb +15 -10
- data/spec/unit/split_screen_spec.rb +2 -2
- data/spec/unit/tab_spec.rb +41 -0
- data/spec/unit/tables/formotion_screen_spec.rb +16 -0
- data/spec/unit/tables/table_indexable_spec.rb +12 -0
- data/spec/unit/tables/table_module_spec.rb +24 -9
- data/spec/unit/tables/table_screen_spec.rb +1 -1
- data/spec/unit/tables/table_view_cell_spec.rb +9 -8
- data/spec/unit/view_helper_spec.rb +2 -2
- data/spec/unit/web_spec.rb +106 -0
- metadata +96 -35
- data/lib/ProMotion/cocoatouch/SplitViewController.rb +0 -23
- data/lib/ProMotion/helpers/console.rb +0 -29
- data/lib/ProMotion/helpers/measure_helper.rb +0 -20
- data/lib/ProMotion/helpers/system_helper.rb +0 -29
- data/lib/ProMotion/helpers/view_helper.rb +0 -82
- data/lib/ProMotion/screen_helpers/screen_elements.rb +0 -36
- data/lib/ProMotion/screen_helpers/screen_tabs.rb +0 -95
- data/lib/ProMotion/screens/_table_screen_module.rb +0 -47
- data/lib/ProMotion/screens/_tables/_refreshable_table.rb +0 -45
- data/lib/ProMotion/screens/_tables/_searchable_table.rb +0 -60
- data/lib/ProMotion/screens/_tables/_sectioned_table.rb +0 -5
- data/lib/ProMotion/screens/_tables/_table.rb +0 -169
- data/lib/ProMotion/screens/_tables/grouped_table.rb +0 -16
- data/lib/ProMotion/screens/_tables/plain_table.rb +0 -17
- data/lib/ProMotion/screens/_tables/table_data.rb +0 -175
- data/lib/ProMotion/screens/behaves_like_screen.rb +0 -10
- data/lib/ProMotion/screens/table_screen.rb +0 -16
- 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.
|
|
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.
|
|
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]
|
|
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
|
|
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]
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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,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,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
|