ProMotion 0.5.2 → 0.6.0

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