ProMotion 0.6.5 → 0.7.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 (56) hide show
  1. data/README.md +79 -502
  2. data/Rakefile +30 -0
  3. data/app/app_delegate.rb +3 -1
  4. data/lib/ProMotion.rb +1 -1
  5. data/lib/ProMotion/cocoatouch/TableViewCell.rb +5 -1
  6. data/lib/ProMotion/delegate/delegate.rb +16 -0
  7. data/lib/ProMotion/delegate/delegate_helper.rb +88 -0
  8. data/lib/ProMotion/delegate/delegate_notifications.rb +61 -0
  9. data/lib/ProMotion/helpers/console.rb +3 -3
  10. data/lib/ProMotion/helpers/logger.rb +15 -15
  11. data/lib/ProMotion/helpers/view_helper.rb +7 -5
  12. data/lib/ProMotion/push_notifications/push_notification.rb +51 -0
  13. data/lib/ProMotion/screen_helpers/screen_elements.rb +2 -2
  14. data/lib/ProMotion/screen_helpers/screen_navigation.rb +5 -1
  15. data/lib/ProMotion/screen_helpers/screen_tabs.rb +2 -2
  16. data/lib/ProMotion/screens/_compatibility/formotion_screen.rb +65 -0
  17. data/lib/ProMotion/screens/_screen_module.rb +33 -20
  18. data/lib/ProMotion/screens/_table_screen_module.rb +4 -4
  19. data/lib/ProMotion/{screen_helpers → screens}/_tables/_refreshable_table.rb +2 -2
  20. data/lib/ProMotion/{screen_helpers → screens}/_tables/_searchable_table.rb +27 -34
  21. data/lib/ProMotion/screens/_tables/_sectioned_table.rb +5 -0
  22. data/lib/ProMotion/screens/_tables/_table.rb +149 -0
  23. data/lib/ProMotion/screens/_tables/grouped_table.rb +16 -0
  24. data/lib/ProMotion/screens/_tables/plain_table.rb +17 -0
  25. data/lib/ProMotion/screens/_tables/table_data.rb +148 -0
  26. data/lib/ProMotion/screens/_tables/table_view_cell_module.rb +156 -0
  27. data/lib/ProMotion/screens/table_screen.rb +3 -2
  28. data/lib/ProMotion/version.rb +1 -1
  29. data/spec/functional/func_screen_spec.rb +66 -0
  30. data/spec/functional/func_split_screen_spec.rb +66 -0
  31. data/spec/functional/func_table_screen_spec.rb +52 -0
  32. data/spec/helpers/functional_screen.rb +15 -0
  33. data/spec/helpers/table_screen.rb +32 -8
  34. data/spec/helpers/table_screen_refreshable.rb +1 -1
  35. data/spec/helpers/table_screen_searchable.rb +1 -1
  36. data/spec/helpers/test_delegate.rb +2 -0
  37. data/spec/unit/delegate_spec.rb +38 -0
  38. data/spec/{ios_version_spec.rb → unit/ios_version_spec.rb} +0 -0
  39. data/spec/{logger_spec.rb → unit/logger_spec.rb} +0 -0
  40. data/spec/{main_spec.rb → unit/main_spec.rb} +0 -0
  41. data/spec/{screen_helpers_spec.rb → unit/screen_helpers_spec.rb} +14 -6
  42. data/spec/{screen_module_spec.rb → unit/screen_module_spec.rb} +0 -0
  43. data/spec/{screen_spec.rb → unit/screen_spec.rb} +21 -3
  44. data/spec/{split_screen_in_tab_bar_spec.rb → unit/split_screen_in_tab_bar_spec.rb} +0 -0
  45. data/spec/{split_screen_open_screen_spec.rb → unit/split_screen_open_screen_spec.rb} +0 -0
  46. data/spec/{split_screen_spec.rb → unit/split_screen_spec.rb} +0 -0
  47. data/spec/unit/tables/table_module_spec.rb +108 -0
  48. data/spec/unit/tables/table_screen_spec.rb +92 -0
  49. data/spec/unit/tables/table_view_cell_spec.rb +106 -0
  50. data/spec/{view_helper_spec.rb → unit/view_helper_spec.rb} +0 -0
  51. metadata +50 -29
  52. data/lib/ProMotion/delegate.rb +0 -63
  53. data/lib/ProMotion/screen_helpers/_tables/_sectioned_table.rb +0 -270
  54. data/lib/ProMotion/screen_helpers/_tables/grouped_table.rb +0 -14
  55. data/lib/ProMotion/screen_helpers/_tables/plain_table.rb +0 -15
  56. data/spec/table_screen_spec.rb +0 -72
@@ -0,0 +1,16 @@
1
+ module ProMotion
2
+ module GroupedTable
3
+ include Table
4
+ include RefreshableTable
5
+
6
+ def table_view
7
+ @table_view ||= begin
8
+ t = UITableView.alloc.initWithFrame(self.view.frame, style:UITableViewStyleGrouped)
9
+ t.dataSource = self
10
+ t.delegate = self
11
+ t
12
+ end
13
+ end
14
+ alias :tableView :table_view
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module ProMotion
2
+ module PlainTable
3
+ include Table
4
+ include SearchableTable
5
+ include RefreshableTable
6
+
7
+ def table_view
8
+ @table_view ||= begin
9
+ t = UITableView.alloc.initWithFrame(self.view.frame, style:UITableViewStylePlain)
10
+ t.dataSource = self
11
+ t.delegate = self
12
+ t
13
+ end
14
+ end
15
+ alias :tableView :table_view
16
+ end
17
+ end
@@ -0,0 +1,148 @@
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
+ table_section[:cells].at(params[:index].to_i)
31
+ end
32
+
33
+ def delete_cell(params={})
34
+ if params[:index_path]
35
+ params[:section] = params[:index_path].section
36
+ params[:index] = params[:index_path].row
37
+ end
38
+
39
+ table_section = self.section(params[:section])
40
+ table_section[:cells].delete_at(params[:index].to_i)
41
+ end
42
+
43
+ def search(search_string)
44
+ self.filtered_data = []
45
+ self.filtered = true
46
+
47
+ search_string = search_string.downcase.strip
48
+
49
+ self.data.each do |section|
50
+ new_section = {}
51
+ new_section[:cells] = []
52
+
53
+ new_section[:cells] = section[:cells].map do |cell|
54
+ cell[:searchable] != false && "#{cell[:title]}\n#{cell[:search_text]}".downcase.strip.include?(search_string) ? cell : nil
55
+ end.compact
56
+
57
+ if new_section[:cells] && new_section[:cells].length > 0
58
+ new_section[:title] = section[:title]
59
+ self.filtered_data << new_section
60
+ end
61
+ end
62
+
63
+ self.filtered_data
64
+ end
65
+
66
+ def stop_searching
67
+ self.filtered_data = []
68
+ self.filtered = false
69
+ end
70
+
71
+ def table_view_cell(params={})
72
+ if params[:index_path]
73
+ params[:section] = params[:index_path].section
74
+ params[:index] = params[:index_path].row
75
+ end
76
+
77
+ data_cell = self.cell(section: params[:section], index: params[:index])
78
+ return UITableViewCell.alloc.init unless data_cell # No data?
79
+
80
+ data_cell = self.remap_data_cell(data_cell) # TODO: Deprecated, remove in version 1.0
81
+ data_cell = self.set_data_cell_defaults(data_cell)
82
+
83
+ table_cell = self.create_table_cell(data_cell)
84
+
85
+ table_cell
86
+ end
87
+
88
+ def set_data_cell_defaults(data_cell)
89
+ data_cell[:cell_style] ||= UITableViewCellStyleDefault
90
+ data_cell[:cell_identifier] ||= "Cell"
91
+ data_cell[:cell_class] ||= ProMotion::TableViewCell
92
+ data_cell
93
+ end
94
+
95
+ def remap_data_cell(data_cell)
96
+ # Re-maps legacy data cell calls
97
+ mappings = {
98
+ cell_style: :cellStyle,
99
+ cell_identifier: :cellIdentifier,
100
+ cell_class: :cellClass,
101
+ masks_to_bounds: :masksToBounds,
102
+ background_color: :backgroundColor,
103
+ selection_style: :selectionStyle,
104
+ cell_class_attributes: :cellClassAttributes,
105
+ accessory_view: :accessoryView,
106
+ accessory_type: :accessoryType,
107
+ accessory_checked: :accessoryDefault,
108
+ remote_image: :remoteImage,
109
+ subviews: :subViews
110
+ }
111
+
112
+ if data_cell[:masks_to_bounds]
113
+ PM.logger.deprecated "masks_to_bounds: (value) is deprecated. Use layer: { masks_to_bounds: (value) } instead."
114
+ data_cell[:layer] ||= {}
115
+ data_cell[:layer][:masks_to_bounds] = data_cell[:masks_to_bounds] # TODO: Deprecate and then remove this.
116
+ end
117
+
118
+ mappings.each_pair do |n, old|
119
+ if data_cell[old]
120
+ warn "[DEPRECATION] `:#{old}` is deprecated in TableScreens. Use `:#{n}`"
121
+ data_cell[n] = data_cell[old]
122
+ end
123
+ end
124
+
125
+ if data_cell[:styles] && data_cell[:styles][:textLabel]
126
+ warn "[DEPRECATION] `:textLabel` is deprecated in TableScreens. Use `:label`"
127
+ data_cell[:styles][:label] = data_cell[:styles][:textLabel]
128
+ end
129
+
130
+ data_cell
131
+ end
132
+
133
+ def create_table_cell(data_cell)
134
+ table_cell = table_view.dequeueReusableCellWithIdentifier(data_cell[:cell_identifier])
135
+
136
+ unless table_cell
137
+ table_cell = data_cell[:cell_class].alloc.initWithStyle(data_cell[:cell_style], reuseIdentifier:data_cell[:cell_identifier])
138
+ table_cell.extend ProMotion::TableViewCellModule unless table_cell.is_a?(ProMotion::TableViewCellModule)
139
+ table_cell.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin
140
+ end
141
+
142
+ table_cell.setup(data_cell)
143
+
144
+ table_cell
145
+ end
146
+
147
+ end
148
+ end
@@ -0,0 +1,156 @@
1
+ module ProMotion
2
+ module TableViewCellModule
3
+ include ViewHelper
4
+
5
+ attr_accessor :data_cell
6
+
7
+ def setup(data_cell)
8
+ self.data_cell = data_cell
9
+
10
+ # TODO: Some of these need to go away. Unnecessary overhead.
11
+ set_cell_attributes
12
+ set_background_color
13
+ set_class_attributes
14
+ set_accessory_view
15
+ set_subtitle
16
+ set_image
17
+ set_remote_image
18
+ set_subviews
19
+ set_details
20
+ set_styles
21
+ set_selection_style
22
+
23
+ self
24
+ end
25
+
26
+ def set_cell_attributes
27
+ set_attributes self, data_cell.dup.tap{ |h| h.delete(:image) }
28
+ self
29
+ end
30
+
31
+ def set_background_color
32
+ self.backgroundView = UIView.new.tap{|v| v.backgroundColor = data_cell[:background_color]} if data_cell[:background_color]
33
+ end
34
+
35
+ def set_class_attributes
36
+ if data_cell[:cell_class_attributes]
37
+ PM.logger.deprecated "`cell_class_attributes` is deprecated. Just add the attributes you want to set right into your cell hash."
38
+ set_attributes self, data_cell[:cell_class_attributes]
39
+ end
40
+ self
41
+ end
42
+
43
+ def set_accessory_view
44
+ if data_cell[:accessory_view]
45
+ self.accessoryView = data_cell[:accessory_view]
46
+ self.accessoryView.autoresizingMask = UIViewAutoresizingFlexibleWidth
47
+ end
48
+
49
+ if data_cell[:accessory] && data_cell[:accessory] == :switch
50
+ switch_view = UISwitch.alloc.initWithFrame(CGRectZero)
51
+ switch_view.addTarget(self, action: "accessory_toggled_switch:", forControlEvents:UIControlEventValueChanged)
52
+ switch_view.on = true if data_cell[:accessory_checked]
53
+ self.accessoryView = switch_view
54
+ end
55
+
56
+ self
57
+ end
58
+
59
+ def set_subtitle
60
+ if data_cell[:subtitle] && self.detailTextLabel
61
+ self.detailTextLabel.text = data_cell[:subtitle]
62
+ self.detailTextLabel.backgroundColor = UIColor.clearColor
63
+ self.detailTextLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth
64
+ end
65
+ self
66
+ end
67
+
68
+ def set_remote_image
69
+ if data_cell[:remote_image]
70
+ if self.imageView.respond_to?("setImageWithURL:placeholderImage:")
71
+ url = data_cell[:remote_image][:url]
72
+ url = NSURL.URLWithString(url) unless url.is_a?(NSURL)
73
+ placeholder = data_cell[:remote_image][:placeholder]
74
+ placeholder = UIImage.imageNamed(placeholder) if placeholder.is_a?(String)
75
+
76
+ self.image_size = data_cell[:remote_image][:size] if data_cell[:remote_image][:size] && self.respond_to?("image_size=")
77
+ self.imageView.setImageWithURL(url, placeholderImage: placeholder)
78
+ self.imageView.layer.masksToBounds = true
79
+ self.imageView.layer.cornerRadius = data_cell[:remote_image][:radius] if data_cell[:remote_image].has_key?(:radius)
80
+ else
81
+ PM.logger.error "ProMotion Warning: to use remote_image with TableScreen you need to include the CocoaPod 'SDWebImage'."
82
+ end
83
+ end
84
+ self
85
+ end
86
+
87
+ def set_image
88
+ if data_cell[:image]
89
+
90
+ cell_image = data_cell[:image].is_a?(Hash) ? data_cell[:image][:image] : data_cell[:image]
91
+ cell_image = UIImage.imageNamed(cell_image) if cell_image.is_a?(String)
92
+
93
+ self.imageView.layer.masksToBounds = true
94
+ self.imageView.image = cell_image
95
+ self.imageView.layer.cornerRadius = data_cell[:image][:radius] if data_cell[:image].is_a?(Hash) && data_cell[:image][:radius]
96
+ end
97
+ self
98
+ end
99
+
100
+ def set_subviews
101
+ tag_number = 0
102
+ Array(data_cell[:subviews]).each do |view|
103
+ # Remove an existing view at that tag number
104
+ tag_number += 1
105
+ existing_view = self.viewWithTag(tag_number)
106
+ existing_view.removeFromSuperview if existing_view
107
+
108
+ # Add the subview if it exists
109
+ if view
110
+ view.tag = tag_number
111
+ self.addSubview view
112
+ end
113
+ end
114
+ self
115
+ end
116
+
117
+ def set_details
118
+ if data_cell[:details]
119
+ self.addSubview data_cell[:details][:image]
120
+ end
121
+ self
122
+ end
123
+
124
+ def set_styles
125
+ if data_cell[:styles] && data_cell[:styles][:label] && data_cell[:styles][:label][:frame]
126
+ ui_label = false
127
+ self.contentView.subviews.each do |view|
128
+ if view.is_a? UILabel
129
+ ui_label = true
130
+ view.text = data_cell[:styles][:label][:text]
131
+ end
132
+ end
133
+
134
+ unless ui_label == true
135
+ label ||= UILabel.alloc.initWithFrame(CGRectZero)
136
+ set_attributes label, data_cell[:styles][:label]
137
+ self.contentView.addSubview label
138
+ end
139
+
140
+ # TODO: What is this and why is it necessary?
141
+ self.textLabel.textColor = UIColor.clearColor
142
+ else
143
+ cell_title = data_cell[:title]
144
+ cell_title ||= ""
145
+ self.textLabel.backgroundColor = UIColor.clearColor
146
+ self.textLabel.text = cell_title
147
+ end
148
+
149
+ self
150
+ end
151
+
152
+ def set_selection_style
153
+ self.selectionStyle = UITableViewCellSelectionStyleNone if data_cell[:no_select]
154
+ end
155
+ end
156
+ end
@@ -3,13 +3,14 @@ module ProMotion
3
3
  # Just make sure to implement the Obj-C methods in cocoatouch/TableViewController.rb.
4
4
  class TableScreen < TableViewController
5
5
  include ProMotion::TableScreenModule
6
+ # Includes PM::PlainTable already
6
7
  end
7
8
 
8
9
  class GroupedTableScreen < TableScreen
9
- include ProMotion::MotionTable::GroupedTable
10
+ include ProMotion::GroupedTable
10
11
  end
11
12
 
12
13
  class SectionedTableScreen < TableScreen
13
- include ProMotion::MotionTable::SectionedTable
14
+ include ProMotion::SectionedTable
14
15
  end
15
16
  end
@@ -1,3 +1,3 @@
1
1
  module ProMotion
2
- VERSION = "0.6.5" unless defined?(ProMotion::VERSION)
2
+ VERSION = "0.7.0" unless defined?(ProMotion::VERSION)
3
3
  end
@@ -0,0 +1,66 @@
1
+ describe "ProMotion::Screen functional" do
2
+ tests PM::Screen
3
+
4
+ # Override controller to properly instantiate
5
+ def controller
6
+ rotate_device to: :portrait, button: :bottom
7
+ @controller ||= FunctionalScreen.new(nav_bar: true)
8
+ @controller.main_controller
9
+ end
10
+
11
+ after do
12
+ @controller = nil
13
+ end
14
+
15
+ it "should have a navigation bar" do
16
+ view("Functional").should.be.kind_of UINavigationItemView
17
+ end
18
+
19
+ it "should allow setting a left nav bar button" do
20
+ @controller.set_nav_bar_button :left, title: "Cool", action: :triggered_button
21
+ tap("Cool")
22
+ @controller.button_was_triggered.should.be.true
23
+ end
24
+
25
+ it "should allow setting a right nav bar button" do
26
+ @controller.set_nav_bar_button :right, title: "Cool2", action: :triggered_button
27
+ tap("Cool2")
28
+ @controller.button_was_triggered.should.be.true
29
+ end
30
+
31
+ it "should allow opening another screen in the same nav bar and have a back button that is operational" do
32
+ @controller.open BasicScreen
33
+
34
+ wait 0.5 do
35
+
36
+ view("Basic").should.be.kind_of UINavigationItemView
37
+ view("Functional").should.be.kind_of UINavigationItemButtonView
38
+
39
+ tap("Functional")
40
+ wait 0.5 do
41
+ view("Functional").should.be.kind_of UINavigationItemView
42
+ end
43
+
44
+ end
45
+ end
46
+
47
+ it "should allow opening and closing a modal screen" do
48
+ @basic = BasicScreen.new(nav_bar: true)
49
+ wait 0.1 do
50
+ @controller.open_modal @basic
51
+
52
+ wait 0.6 do
53
+
54
+ view("Basic").should.be.kind_of UINavigationItemView
55
+ @basic.close
56
+
57
+ wait 0.6 do
58
+ @basic = nil
59
+ view("Functional").should.be.kind_of UINavigationItemView
60
+ end
61
+
62
+ end
63
+ end
64
+ end
65
+
66
+ end
@@ -0,0 +1,66 @@
1
+ describe "Split screen functional" do
2
+ tests PM::SplitViewController
3
+
4
+ # Override controller to properly instantiate
5
+ def controller
6
+ @app ||= TestDelegate.new
7
+ @master = MasterScreen.new(nav_bar: true)
8
+ @detail = DetailScreen.new(nav_bar: true)
9
+ @controller ||= @app.create_split_screen @master, @detail
10
+ end
11
+
12
+ before do
13
+ rotate_device to: :landscape, button: :right
14
+ end
15
+
16
+ after do
17
+ rotate_device to: :portrait, button: :bottom
18
+ end
19
+
20
+ it "should allow opening a detail view from the master view" do
21
+
22
+ @master.open BasicScreen.new(nav_bar: true), in_detail: true
23
+
24
+ wait 0.75 do
25
+ view("Master").should.be.kind_of UINavigationItemView
26
+ view("Basic").should.be.kind_of UINavigationItemView
27
+ views(UINavigationItemView).each { |v| v.title.should.not == "Detail" }
28
+ end
29
+
30
+ end
31
+
32
+ it "should allow opening another view from the master view" do
33
+
34
+ @master.open BasicScreen
35
+
36
+ wait 0.75 do
37
+ view("Basic").should.be.kind_of UINavigationItemView
38
+ view("Detail").should.be.kind_of UINavigationItemView
39
+ end
40
+
41
+ end
42
+
43
+ it "should allow opening a master view from the detail view" do
44
+
45
+ @detail.open BasicScreen.new(nav_bar: true), in_master: true
46
+
47
+ wait 0.75 do
48
+ view("Basic").should.be.kind_of UINavigationItemView
49
+ view("Detail").should.be.kind_of UINavigationItemView
50
+ views(UINavigationItemView).each { |v| v.title.should.not == "Master" }
51
+ end
52
+
53
+ end
54
+
55
+ it "should allow opening another view from the detail view" do
56
+
57
+ @detail.open BasicScreen
58
+
59
+ wait 0.75 do
60
+ view("Basic").should.be.kind_of UINavigationItemView
61
+ view("Master").should.be.kind_of UINavigationItemView
62
+ end
63
+
64
+ end
65
+
66
+ end