ProMotion 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +6 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +5 -1
  5. data/README.md +237 -138
  6. data/Rakefile +4 -9
  7. data/app/app_delegate.rb +3 -0
  8. data/app/screens/basic_screen.rb +27 -0
  9. data/lib/ProMotion.rb +3 -5
  10. data/lib/ProMotion/cocoatouch/SplitViewController.rb +25 -0
  11. data/lib/ProMotion/cocoatouch/TableViewController.rb +5 -5
  12. data/lib/ProMotion/cocoatouch/ViewController.rb +5 -5
  13. data/lib/ProMotion/{app_delegate.rb → delegate.rb} +23 -23
  14. data/lib/ProMotion/helpers/console.rb +6 -4
  15. data/lib/ProMotion/helpers/logger.rb +73 -0
  16. data/lib/ProMotion/helpers/view_helper.rb +45 -13
  17. data/lib/ProMotion/screen_helpers/_tables/_refreshable_table.rb +42 -0
  18. data/lib/ProMotion/screen_helpers/_tables/_searchable_table.rb +2 -2
  19. data/lib/ProMotion/screen_helpers/_tables/_sectioned_table.rb +46 -41
  20. data/lib/ProMotion/screen_helpers/_tables/grouped_table.rb +2 -1
  21. data/lib/ProMotion/screen_helpers/_tables/plain_table.rb +1 -0
  22. data/lib/ProMotion/screen_helpers/screen_elements.rb +16 -11
  23. data/lib/ProMotion/screen_helpers/screen_navigation.rb +15 -16
  24. data/lib/ProMotion/screen_helpers/screen_tabs.rb +20 -16
  25. data/lib/ProMotion/screen_helpers/split_screen.rb +42 -0
  26. data/lib/ProMotion/screens/_screen_module.rb +44 -35
  27. data/lib/ProMotion/screens/_table_screen_module.rb +18 -1
  28. data/lib/ProMotion/screens/screen.rb +1 -1
  29. data/lib/ProMotion/version.rb +1 -1
  30. data/spec/helpers/table_screen.rb +48 -0
  31. data/spec/helpers/table_screen_refreshable.rb +11 -0
  32. data/spec/helpers/table_screen_searchable.rb +5 -0
  33. data/spec/helpers/test_delegate.rb +9 -0
  34. data/spec/ios_version_spec.rb +6 -6
  35. data/spec/logger_spec.rb +68 -0
  36. data/spec/main_spec.rb +1 -1
  37. data/spec/screen_helpers_spec.rb +35 -6
  38. data/spec/{view_controller_spec.rb → screen_spec.rb} +1 -1
  39. data/spec/split_screen_in_tab_bar_spec.rb +49 -0
  40. data/spec/split_screen_open_screen_spec.rb +46 -0
  41. data/spec/split_screen_spec.rb +35 -0
  42. data/spec/table_screen_spec.rb +72 -0
  43. data/spec/view_helper_spec.rb +112 -8
  44. metadata +29 -8
  45. data/lib/ProMotion/helpers/tab_bar.rb +0 -115
  46. data/spec/helpers/.gitkeep +0 -0
@@ -22,13 +22,13 @@ module ProMotion::MotionTable
22
22
  @contacts_search_display_controller.delegate = params[:delegate]
23
23
  @contacts_search_display_controller.searchResultsDataSource = params[:data_source]
24
24
  @contacts_search_display_controller.searchResultsDelegate = params[:search_results_delegate]
25
-
25
+
26
26
  self.table_view.tableHeaderView = search_bar
27
27
  end
28
28
  alias :makeSearchable :make_searchable
29
29
 
30
30
  ######### iOS methods, headless camel case #######
31
-
31
+
32
32
  def searchDisplayController(controller, shouldReloadTableForSearchString:search_string)
33
33
  @mt_filtered_data = nil
34
34
  @mt_filtered_data = []
@@ -1,12 +1,22 @@
1
1
  module ProMotion::MotionTable
2
2
  module SectionedTable
3
+ include ProMotion::ViewHelper
4
+
3
5
  def table_setup
4
- ProMotion::Console.log(" - #table_data method needed in TableScreen #{self.class.to_s}.", with_color: ProMotion::Console::RED_COLOR) unless self.respond_to?(:table_data)
6
+ PM.logger.error "Missing #table_data method in TableScreen #{self.class.to_s}." unless self.respond_to?(:table_data)
5
7
 
6
8
  self.view = self.create_table_view_from_data(self.table_data)
9
+
7
10
  if self.class.respond_to?(:get_searchable) && self.class.get_searchable
8
11
  self.make_searchable(content_controller: self, search_bar: self.class.get_searchable_params)
9
12
  end
13
+ if self.class.respond_to?(:get_refreshable) && self.class.get_refreshable
14
+ if defined?(UIRefreshControl)
15
+ self.make_refreshable(self.class.get_refreshable_params)
16
+ else
17
+ PM.logger.warn "To use the refresh control on < iOS 6, you need to include the CocoaPod 'CKRefreshControl'."
18
+ end
19
+ end
10
20
  end
11
21
 
12
22
  # @param [Array] Array of table data
@@ -37,7 +47,9 @@ module ProMotion::MotionTable
37
47
  end
38
48
 
39
49
  def cell_at_section_and_index(section, index)
40
- return section_at_index(section)[:cells].at(index) if section_at_index(section) && section_at_index(section)[:cells]
50
+ if section_at_index(section) && section_at_index(section)[:cells]
51
+ return section_at_index(section)[:cells].at(index)
52
+ end
41
53
  end
42
54
  alias :cellAtSectionAndIndex :cell_at_section_and_index
43
55
 
@@ -49,36 +61,22 @@ module ProMotion::MotionTable
49
61
  elsif expected_arguments == 1 || expected_arguments == -1
50
62
  self.send(action, arguments)
51
63
  else
52
- ProMotion::Console.log("MotionTable warning: #{action} expects #{expected_arguments} arguments. Maximum number of required arguments for an action is 1.", with_color: MotionTable::ProMotion::Console::RED_COLOR)
64
+ PM.logger.warn "#{action} expects #{expected_arguments} arguments. Maximum number of required arguments for an action is 1."
53
65
  end
54
66
  else
55
- ProMotion::Console.log("Action not implemented: #{action.to_s}", with_color: ProMotion::Console::RED_COLOR)
56
- end
57
- end
58
-
59
- def set_cell_attributes(element, args = {})
60
- args.each do |k, v|
61
- if v.is_a? Hash
62
- v.each do
63
- sub_element = element.send("#{k}")
64
- set_cell_attributes(sub_element, v)
65
- end
66
- else
67
- element.send("#{k}=", v) if element.respond_to?("#{k}=")
68
- end
67
+ PM.logger.info "Action not implemented: #{action.to_s}"
69
68
  end
70
- element
71
69
  end
72
70
 
73
71
  def accessory_toggled_switch(switch)
74
72
  table_cell = switch.superview
75
- indexPath = table_cell.superview.indexPathForCell(table_cell)
73
+ index_path = table_cell.superview.indexPathForCell(table_cell)
76
74
 
77
- data_cell = cell_at_section_and_index(indexPath.section, indexPath.row)
75
+ data_cell = cell_at_section_and_index(index_path.section, index_path.row)
78
76
  data_cell[:arguments] = {} unless data_cell[:arguments]
79
77
  data_cell[:arguments][:value] = switch.isOn if data_cell[:arguments].is_a? Hash
80
78
  data_cell[:accessory_action] ||= data_cell[:accessoryAction] # For legacy support
81
-
79
+
82
80
  trigger_action(data_cell[:accessory_action], data_cell[:arguments]) if data_cell[:accessory_action]
83
81
  end
84
82
 
@@ -105,14 +103,14 @@ module ProMotion::MotionTable
105
103
  # Set table_data_index if you want the right hand index column (jumplist)
106
104
  def sectionIndexTitlesForTableView(table_view)
107
105
  if self.respond_to?(:table_data_index)
108
- self.table_data_index
106
+ self.table_data_index
109
107
  end
110
108
  end
111
-
109
+
112
110
  def remap_data_cell(data_cell)
113
111
  # Re-maps legacy data cell calls
114
- mappings = {
115
- cell_style: :cellStyle,
112
+ mappings = {
113
+ cell_style: :cellStyle,
116
114
  cell_identifier: :cellIdentifier,
117
115
  cell_class: :cellClass,
118
116
  masks_to_bounds: :masksToBounds,
@@ -138,14 +136,12 @@ module ProMotion::MotionTable
138
136
  data_cell
139
137
  end
140
138
 
141
- def tableView(table_view, cellForRowAtIndexPath:indexPath)
142
- # Aah, magic happens here...
143
-
144
- data_cell = cell_at_section_and_index(indexPath.section, indexPath.row)
139
+ def tableView(table_view, cellForRowAtIndexPath:index_path)
140
+ data_cell = cell_at_section_and_index(index_path.section, index_path.row)
145
141
  return UITableViewCell.alloc.init unless data_cell
146
-
142
+
147
143
  data_cell = self.remap_data_cell(data_cell)
148
-
144
+
149
145
  data_cell[:cell_style] ||= UITableViewCellStyleDefault
150
146
  data_cell[:cell_identifier] ||= "Cell"
151
147
  cell_identifier = data_cell[:cell_identifier]
@@ -154,7 +150,7 @@ module ProMotion::MotionTable
154
150
  table_cell = table_view.dequeueReusableCellWithIdentifier(cell_identifier)
155
151
  unless table_cell
156
152
  table_cell = data_cell[:cell_class].alloc.initWithStyle(data_cell[:cell_style], reuseIdentifier:cell_identifier)
157
-
153
+
158
154
  # Add optimizations here
159
155
  table_cell.layer.masksToBounds = true if data_cell[:masks_to_bounds]
160
156
  table_cell.backgroundColor = data_cell[:background_color] if data_cell[:background_color]
@@ -163,9 +159,9 @@ module ProMotion::MotionTable
163
159
  end
164
160
 
165
161
  if data_cell[:cell_class_attributes]
166
- set_cell_attributes table_cell, data_cell[:cell_class_attributes]
162
+ set_attributes table_cell, data_cell[:cell_class_attributes]
167
163
  end
168
-
164
+
169
165
  if data_cell[:accessory_view]
170
166
  table_cell.accessoryView = data_cell[:accessory_view]
171
167
  table_cell.accessoryView.autoresizingMask = UIViewAutoresizingFlexibleWidth
@@ -177,7 +173,7 @@ module ProMotion::MotionTable
177
173
 
178
174
  if data_cell[:accessory] && data_cell[:accessory] == :switch
179
175
  switch_view = UISwitch.alloc.initWithFrame(CGRectZero)
180
- switch_view.addTarget(self, action: "accessory_toggled_switch:", forControlEvents:UIControlEventValueChanged);
176
+ switch_view.addTarget(self, action: "accessory_toggled_switch:", forControlEvents:UIControlEventValueChanged)
181
177
  switch_view.on = true if data_cell[:accessory_checked]
182
178
  table_cell.accessoryView = switch_view
183
179
  end
@@ -199,9 +195,9 @@ module ProMotion::MotionTable
199
195
  table_cell.image_size = data_cell[:remote_image][:size] if data_cell[:remote_image][:size] && table_cell.respond_to?("image_size=")
200
196
  table_cell.imageView.setImageWithURL(url, placeholderImage: placeholder)
201
197
  table_cell.imageView.layer.masksToBounds = true
202
- table_cell.imageView.layer.cornerRadius = data_cell[:remote_image][:radius]
198
+ table_cell.imageView.layer.cornerRadius = data_cell[:remote_image][:radius] if data_cell[:remote_image].has_key?(:radius)
203
199
  else
204
- ProMotion::Console.log("ProMotion Warning: to use remote_image with TableScreen you need to include the CocoaPod 'SDWebImage'.", with_color: ProMotion::Console::RED_COLOR)
200
+ PM.logger.error "ProMotion Warning: to use remote_image with TableScreen you need to include the CocoaPod 'SDWebImage'."
205
201
  end
206
202
  elsif data_cell[:image]
207
203
  table_cell.imageView.layer.masksToBounds = true
@@ -240,7 +236,7 @@ module ProMotion::MotionTable
240
236
 
241
237
  unless ui_label == true
242
238
  label ||= UILabel.alloc.initWithFrame(CGRectZero)
243
- set_cell_attributes label, data_cell[:styles][:label]
239
+ set_attributes label, data_cell[:styles][:label]
244
240
  table_cell.contentView.addSubview label
245
241
  end
246
242
  # hackery
@@ -254,9 +250,18 @@ module ProMotion::MotionTable
254
250
  return table_cell
255
251
  end
256
252
 
257
- def tableView(table_view, didSelectRowAtIndexPath:indexPath)
258
- cell = cell_at_section_and_index(indexPath.section, indexPath.row)
259
- table_view.deselectRowAtIndexPath(indexPath, animated: true);
253
+ def tableView(tableView, heightForRowAtIndexPath:index_path)
254
+ cell = cell_at_section_and_index(index_path.section, index_path.row)
255
+ if cell[:height]
256
+ cell[:height].to_f
257
+ else
258
+ tableView.rowHeight
259
+ end
260
+ end
261
+
262
+ def tableView(table_view, didSelectRowAtIndexPath:index_path)
263
+ cell = cell_at_section_and_index(index_path.section, index_path.row)
264
+ table_view.deselectRowAtIndexPath(index_path, animated: true)
260
265
  cell[:arguments] ||= {}
261
266
  cell[:arguments][:cell] = cell if cell[:arguments].is_a?(Hash)
262
267
  trigger_action(cell[:action], cell[:arguments]) if cell[:action]
@@ -1,7 +1,8 @@
1
1
  module ProMotion::MotionTable
2
2
  module GroupedTable
3
3
  include SectionedTable
4
-
4
+ include RefreshableTable
5
+
5
6
  def table_view
6
7
  @table_view ||= UITableView.alloc.initWithFrame(self.view.frame, style:UITableViewStyleGrouped)
7
8
  @table_view.dataSource = self;
@@ -2,6 +2,7 @@ module ProMotion::MotionTable
2
2
  module PlainTable
3
3
  include SectionedTable
4
4
  include SearchableTable
5
+ include RefreshableTable
5
6
 
6
7
  def table_view
7
8
  @table_view ||= UITableView.alloc.initWithFrame(self.view.frame, style:UITableViewStylePlain)
@@ -1,28 +1,33 @@
1
1
  module ProMotion
2
2
  module ScreenElements
3
3
  include ProMotion::ViewHelper
4
-
5
- def add(v, attrs = {})
6
- if attrs && attrs.length > 0
7
- set_attributes(v, attrs)
8
- end
9
- self.view.addSubview(v)
10
- v
4
+
5
+ def add(element, attrs = {})
6
+ add_to self.view, element, attrs
11
7
  end
12
8
  alias :add_element :add
13
9
  alias :add_view :add
14
10
 
15
- def remove(v)
16
- v.removeFromSuperview
17
- v = nil
11
+ def remove(element)
12
+ element.removeFromSuperview
13
+ element = nil
18
14
  end
19
15
  alias :remove_element :remove
20
16
  alias :remove_view :remove
17
+
18
+ def add_to(parent_element, element, attrs = {})
19
+ if attrs && attrs.length > 0
20
+ set_attributes(element, attrs)
21
+ set_easy_attributes(parent_element, element, attrs)
22
+ end
23
+ parent_element.addSubview element
24
+ element
25
+ end
21
26
 
22
27
  def bounds
23
28
  return self.view.bounds
24
29
  end
25
-
30
+
26
31
  def frame
27
32
  return self.view.frame
28
33
  end
@@ -2,14 +2,17 @@ module ProMotion
2
2
  module ScreenNavigation
3
3
 
4
4
  def open_screen(screen, args = {})
5
-
5
+
6
6
  # Apply properties to instance
7
7
  screen = setup_screen_for_open(screen, args)
8
8
  ensure_wrapper_controller_in_place(screen, args)
9
9
 
10
- screen.send(:on_load) if screen.respond_to?(:on_load)
10
+ screen.send(:on_load) if screen.respond_to?(:on_load)
11
11
  animated = args[:animated] || true
12
12
 
13
+ return self.split_screen.detail_screen = screen if args[:in_detail] && self.split_screen
14
+ return self.split_screen.master_screen = screen if args[:in_master] && self.split_screen
15
+
13
16
  if args[:close_all]
14
17
  open_root_screen screen
15
18
 
@@ -36,18 +39,15 @@ module ProMotion
36
39
  def open_root_screen(screen)
37
40
  app_delegate.open_root_screen(screen)
38
41
  end
39
- alias :fresh_start :open_root_screen
40
42
 
41
43
  def app_delegate
42
44
  UIApplication.sharedApplication.delegate
43
45
  end
44
-
45
- # TODO: De-uglify this method.
46
+
46
47
  def close_screen(args = {})
47
48
  args ||= {}
48
49
  args[:animated] ||= true
49
-
50
- # Pop current view, maybe with arguments, if in navigation controller
50
+
51
51
  if self.is_modal?
52
52
  close_modal_screen args
53
53
 
@@ -56,8 +56,8 @@ module ProMotion
56
56
  send_on_return(args) # TODO: this would be better implemented in a callback or view_did_disappear.
57
57
 
58
58
  else
59
- Console.log("Tried to close #{self.to_s}; however, this screen isn't modal or in a nav bar.", withColor: Console::PURPLE_COLOR)
60
-
59
+ PM.logger.warn "Tried to close #{self.to_s}; however, this screen isn't modal or in a nav bar."
60
+
61
61
  end
62
62
  end
63
63
  alias :close :close_screen
@@ -69,7 +69,6 @@ module ProMotion
69
69
  else
70
70
  self.parent_screen.send(:on_return)
71
71
  end
72
- ProMotion::Screen.current_screen = self.parent_screen
73
72
  end
74
73
  end
75
74
 
@@ -78,14 +77,14 @@ module ProMotion
78
77
  end
79
78
 
80
79
  def push_view_controller(vc, nav_controller=nil)
81
- Console.log(" You need a nav_bar if you are going to push #{vc.to_s} onto it.", withColor: Console::RED_COLOR) unless self.navigation_controller
80
+ unless self.navigation_controller
81
+ PM.logger.error "You need a nav_bar if you are going to push #{vc.to_s} onto it."
82
+ end
82
83
  nav_controller ||= self.navigation_controller
84
+ vc.first_screen = false if vc.respond_to?(:first_screen=)
83
85
  nav_controller.pushViewController(vc, animated: true)
84
86
  end
85
87
 
86
-
87
-
88
-
89
88
  protected
90
89
 
91
90
  def setup_screen_for_open(screen, args={})
@@ -97,7 +96,7 @@ module ProMotion
97
96
  screen.parent_screen = self if screen.respond_to?("parent_screen=")
98
97
  screen.title = args[:title] if args[:title] && screen.respond_to?("title=")
99
98
  screen.modal = args[:modal] if args[:modal] && screen.respond_to?("modal=")
100
-
99
+
101
100
  # Hide bottom bar?
102
101
  screen.hidesBottomBarWhenPushed = args[:hide_tab_bar] == true
103
102
 
@@ -134,7 +133,7 @@ module ProMotion
134
133
  end
135
134
 
136
135
  else
137
- Console.log("No tab bar item '#{tab_name}'", with_color: Console::RED_COLOR)
136
+ PM.logger.error "No tab bar item '#{tab_name}'"
138
137
  end
139
138
  end
140
139
 
@@ -9,31 +9,35 @@ module ProMotion
9
9
  screens.map! { |s| s.respond_to?(:new) ? s.new : s } # Initialize any classes
10
10
 
11
11
  screens.each do |s|
12
- if s.is_a?(ProMotion::Screen) || s.is_a?(ProMotion::TableScreen) || s.is_a?(ProMotion::ScreenModule)
13
- s = s.new if s.respond_to?(:new)
14
- s.tabBarItem.tag = tag_index
15
- s.parent_screen = self if self.is_a?(UIViewController) && s.respond_to?("parent_screen=")
16
- s.tab_bar = tab_bar_controller if s.respond_to?("tab_bar=")
17
- view_controllers << s.main_controller
18
- tag_index += 1
19
- else
20
- Console.log("Non-Screen passed into tab_bar_controller: #{s.to_s}", withColor: Console::RED_COLOR)
21
- end
22
-
12
+ s = s.new if s.respond_to?(:new)
13
+
14
+ s.tabBarItem.tag = tag_index
15
+
16
+ s.parent_screen = self if self.is_a?(UIViewController) && s.respond_to?("parent_screen=")
17
+ s.tab_bar = tab_bar_controller if s.respond_to?("tab_bar=")
18
+
19
+ vc = s.respond_to?(:main_controller) ? s.main_controller : s
20
+ view_controllers << vc
21
+
22
+ tag_index += 1
23
+
23
24
  s.on_load if s.respond_to?(:on_load)
24
25
  end
25
26
 
26
27
  tab_bar_controller.viewControllers = view_controllers
27
28
  tab_bar_controller
28
29
  end
29
-
30
+
30
31
  # Open a UITabBarController with the specified screens as the
31
32
  # root view controller of the current app.
32
33
  # @param [Array] A comma-delimited list of screen classes or instances.
33
34
  # @return [UITabBarController]
34
35
  def open_tab_bar(*screens)
35
36
  tab_bar = tab_bar_controller(*screens)
36
- UIApplication.sharedApplication.delegate.load_root_screen(tab_bar)
37
+
38
+ a = self.respond_to?(:load_root_screen) ? self : UIApplication.sharedApplication.delegate
39
+
40
+ a.load_root_screen(tab_bar)
37
41
  tab_bar
38
42
  end
39
43
 
@@ -62,12 +66,12 @@ module ProMotion
62
66
  title = tab[:title] if tab[:title]
63
67
  tab[:tag] ||= @current_tag ||= 0
64
68
  @current_tag = tab[:tag] + 1
65
-
69
+
66
70
  tab_bar_item = create_tab_bar_icon(tab[:system_icon], tab[:tag]) if tab[:system_icon]
67
71
  tab_bar_item = create_tab_bar_icon_custom(title, tab[:icon], tab[:tag]) if tab[:icon]
68
-
72
+
69
73
  tab_bar_item.badgeValue = tab[:badge_number].to_s unless tab[:badge_number].nil? || tab[:badge_number] <= 0
70
-
74
+
71
75
  return tab_bar_item
72
76
  end
73
77
 
@@ -0,0 +1,42 @@
1
+ module ProMotion
2
+ module SplitScreen
3
+ def split_screen_controller(master, detail)
4
+ master_main = master.navigationController ? master.navigationController : master
5
+ detail_main = detail.navigationController ? detail.navigationController : detail
6
+
7
+ split = SplitViewController.alloc.init
8
+ split.viewControllers = [ master_main, detail_main ]
9
+ split.delegate = self
10
+
11
+ [ master, detail ].map { |s| s.split_screen = split if s.respond_to?(:split_screen=) }
12
+
13
+ split
14
+ end
15
+
16
+ def create_split_screen(master, detail, args={})
17
+ master = master.new if master.respond_to?(:new)
18
+ detail = detail.new if detail.respond_to?(:new)
19
+
20
+ [ master, detail ].map { |s| s.on_load if s.respond_to?(:on_load) }
21
+
22
+ split_screen_controller master, detail
23
+ end
24
+
25
+ def open_split_screen(master, detail, args={})
26
+ split = create_split_screen(master, detail, args)
27
+ open split, args
28
+ split
29
+ end
30
+
31
+ # UISplitViewControllerDelegate methods
32
+
33
+ def splitViewController(svc, willHideViewController: vc, withBarButtonItem: button, forPopoverController: pc)
34
+ button.title = vc.title
35
+ svc.detail_screen.navigationItem.leftBarButtonItem = button;
36
+ end
37
+
38
+ def splitViewController(svc, willShowViewController: vc, invalidatingBarButtonItem: barButtonItem)
39
+ svc.detail_screen.navigationItem.leftBarButtonItem = nil
40
+ end
41
+ end
42
+ end