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