ProMotion-XLForm 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b72f5d70e733afcd851aff9900f8b13b19fbed0b
4
+ data.tar.gz: 2b63d7b46c59924fb9839f45e2f358a135f34028
5
+ SHA512:
6
+ metadata.gz: 341ac2b44c3f2f74cf667351d087d6ee239ef4237fe5fb690c8dd4736ff941c23f164fb8c095e367230155c42b728ef36775b2a44fb9b3b757425da8ee92407f
7
+ data.tar.gz: 63926ccbdea5af6926c5f75de41415d6eae24b797421f29c7e4aafe508d34628bf859532ea49aa02572994b08423816d35e33c1398017570e6ab6aa9d8a9d1c0
data/README.md ADDED
@@ -0,0 +1,227 @@
1
+ # ProMotion-XLForm
2
+
3
+ ProMotion-XLForm provides a PM::XLFormScreen for [ProMotion](https://github.com/clearsightstudio/ProMotion) powered by the CocoaPod [XLForm](https://github.com/xmartlabs/XLForm).
4
+
5
+ ## Warning
6
+ This gem is currently in very early-stage. Use it at your own risk :)
7
+
8
+ ## Installation
9
+
10
+ ```ruby
11
+ gem 'ProMotion-form'
12
+ ```
13
+
14
+ Then:
15
+
16
+ ```sh-session
17
+ $ bundle
18
+ $ rake pod:install
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ `PM::XLFormScreen` include `PM::ScreenModule` so you'll have all ProMotion methods available.
24
+
25
+ To create a form, create a new `PM::XLFormScreen` and implement `form_data`.
26
+
27
+ ```ruby
28
+ class TestFormScreen < PM::XLFormScreen
29
+ def form_data
30
+ [
31
+ {
32
+ title: 'Account information',
33
+ cells: [
34
+ {
35
+ title: 'Email',
36
+ name: :email,
37
+ type: :email,
38
+ placeholder: 'Enter your email',
39
+ required: true
40
+ },
41
+ {
42
+ title: 'Name',
43
+ name: :name,
44
+ type: :text,
45
+ value: 'Default value'
46
+ },
47
+ {
48
+ title: 'Sex',
49
+ name: :sex,
50
+ type: :selector_push,
51
+ options: {
52
+ male: 'Male',
53
+ female: 'Female',
54
+ other: 'Other'
55
+ }
56
+ ]
57
+ }
58
+ ]
59
+ end
60
+ end
61
+ ```
62
+
63
+ ## Available types
64
+ * :text
65
+ * :name
66
+ * :url
67
+ * :email
68
+ * :password
69
+ * :number
70
+ * :phone
71
+ * :twitter
72
+ * :account
73
+ * :integer
74
+ * :decimal
75
+ * :textview
76
+ * :selector_push
77
+ * :selector_popover
78
+ * :selector_action_sheet
79
+ * :selector_alert_view
80
+ * :selector_picker_view
81
+ * :selector_picker_view_inline
82
+ * :multiple_selector
83
+ * :multiple_selector_popover
84
+ * :selector_left_right
85
+ * :selector_segmented_control
86
+ * :date_inline
87
+ * :datetime_inline
88
+ * :time_inline
89
+ * :countdown_timer_inline
90
+ * :date
91
+ * :datetime
92
+ * :time
93
+ * :countdown_timer
94
+ * :datepicker
95
+ * :picker
96
+ * :slider
97
+ * :check
98
+ * :switch
99
+ * :button
100
+ * :info
101
+ * :step_counter
102
+ * :image
103
+
104
+ ### Class options
105
+
106
+ ```ruby
107
+ class TestFormScreen < PM::XLFormScreen
108
+
109
+ form_options required: :asterisks, # add an asterisk to required fields
110
+ on_save: :'save_form:', # will be called when you touch save
111
+ on_cancel: :cancel_form # will be called when you touch cancel
112
+
113
+ def save_form(values)
114
+ mp values
115
+ end
116
+
117
+ def cancel_form
118
+ end
119
+ end
120
+ ```
121
+
122
+ ### Getting values
123
+
124
+ You can get the values of your form with `values`. You can also get validation errors with `validation_errors` and check if the form is valid with `valid?`
125
+
126
+ ### Events
127
+
128
+ `on_change`, `on_add` and `on_remove` are available for cells, `on_add` and `on_remove` are available for sections.
129
+
130
+ ```ruby
131
+ {
132
+ title: 'Sex',
133
+ name: :sex,
134
+ type: :selector_push,
135
+ options: {
136
+ male: 'Male',
137
+ female: 'Female',
138
+ other: 'Other'
139
+ },
140
+ on_change: lambda do |old_value, new_value|
141
+ puts "Changed from #{old_value} to #{new_value}"
142
+ end
143
+ }
144
+ ```
145
+
146
+ ### Multivalued Sections (Insert, Delete, Reorder rows)
147
+
148
+ ```ruby
149
+ {
150
+ title: 'Multiple value',
151
+ name: :multi_values,
152
+ options: [:insert, :delete, :reorder],
153
+ cells: [
154
+ {
155
+ title: 'Add a new tag',
156
+ name: :tag,
157
+ type: :text
158
+ }
159
+ ]
160
+ }
161
+ ```
162
+
163
+ ### Custom Selectors
164
+
165
+ You can create a custom _subform_ with a few options
166
+
167
+ ```ruby
168
+ {
169
+ title: 'Custom',
170
+ name: :custom,
171
+ cells: [
172
+ {
173
+ title: 'Some text',
174
+ name: :some_text,
175
+ type: :text
176
+ },
177
+ {
178
+ title: 'Other text',
179
+ name: :some_other_text,
180
+ type: :text
181
+ }
182
+ ]
183
+ }
184
+ ```
185
+
186
+ By default, the cell will print a `Hash.inspect` of your _subcells_. You can change this by creating a valueTransformer and set `value_transformer:`.
187
+
188
+ ```ruby
189
+ {
190
+ title: 'Custom',
191
+ name: :custom,
192
+ value_transformer: MyValueTransformer
193
+ cells: []
194
+ }
195
+
196
+ class MyValueTransformer < PM::ValueTransformer
197
+ def transformedValue(value)
198
+ return nil if value.nil?
199
+
200
+ str = []
201
+ str << value['some_text'] if value['some_text']
202
+ str << value['some_other_text'] if value['some_other_text']
203
+
204
+ str.join(',')
205
+ end
206
+ end
207
+ ```
208
+
209
+ For a more advanced custom selector, you can set `view_controller_class:`. See [XLForm documentation](https://github.com/xmartlabs/XLForm/#custom-selectors---selector-row-with-a-custom-selector-view-controller) for more informations.
210
+
211
+ ## Todo
212
+ - Validations
213
+ - Tests
214
+ - A lot of other things :)
215
+
216
+ ## Contributing
217
+
218
+ 1. Fork it
219
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
220
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
221
+ 4. Make some specs pass
222
+ 5. Push to the branch (`git push origin my-new-feature`)
223
+ 6. Create new Pull Request
224
+
225
+ ## License
226
+
227
+ Released under the [MIT license](LICENSE).
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ unless defined?(Motion::Project::Config)
3
+ raise "ProMotion-XLForm must be required within a RubyMotion project."
4
+ end
5
+ require 'motion-cocoapods'
6
+
7
+ Motion::Project::App.setup do |app|
8
+ lib_dir_path = File.dirname(File.expand_path(__FILE__))
9
+ app.files << File.join(lib_dir_path, "ProMotion/XLForm/cells/xl_form_image_selector_cell.rb")
10
+ app.files << File.join(lib_dir_path, "ProMotion/XLForm/xl_form_class_methods.rb")
11
+ app.files << File.join(lib_dir_path, "ProMotion/XLForm/xl_form_module.rb")
12
+ app.files << File.join(lib_dir_path, "ProMotion/XLForm/xl_form_view_controller.rb")
13
+ app.files << File.join(lib_dir_path, "ProMotion/XLForm/xl_form.rb")
14
+ app.files << File.join(lib_dir_path, "ProMotion/XLForm/xl_form_screen.rb")
15
+ app.files << File.join(lib_dir_path, "ProMotion/XLForm/xl_sub_form_screen.rb")
16
+ app.files << File.join(lib_dir_path, "ProMotion/XLForm/xl_form_patch.rb")
17
+ app.files << File.join(lib_dir_path, "ProMotion/XLForm/value_transformer.rb")
18
+
19
+ app.pods do
20
+ pod 'XLForm', '~> 3.0.0'
21
+ end
22
+ end
@@ -0,0 +1,227 @@
1
+ class XLFormImageSelectorCell < XLFormBaseCell
2
+
3
+ attr_accessor :imageview, :text_label
4
+
5
+ def configure
6
+ super
7
+ @image_height = 100.0
8
+ @image_width = 100.0
9
+ self.selectionStyle = UITableViewCellSelectionStyleNone
10
+ self.backgroundColor = UIColor.whiteColor
11
+ self.separatorInset = UIEdgeInsetsZero
12
+
13
+ self.contentView.addSubview(imageview)
14
+ self.contentView.addSubview(text_label)
15
+ text_label.addObserver(self, forKeyPath: "text", options: NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew, context: 0)
16
+ end
17
+
18
+ def update
19
+ text_label.text = self.rowDescriptor.title
20
+ imageview.image = self.rowDescriptor.value
21
+
22
+ text_label.setFont(UIFont.preferredFontForTextStyle(UIFontTextStyleBody))
23
+ text_label.sizeToFit
24
+ updateConstraints
25
+ end
26
+
27
+ def self.formDescriptorCellHeightForRowDescriptor(_)
28
+ 120.0
29
+ end
30
+
31
+ def formDescriptorCellDidSelectedWithFormController(controller)
32
+ if NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1
33
+ alert = UIAlertController.alertControllerWithTitle(self.rowDescriptor.selectorTitle,
34
+ message: nil,
35
+ preferredStyle: UIAlertControllerStyleActionSheet)
36
+
37
+ alert.addAction(UIAlertAction.actionWithTitle(NSLocalizedString("Choose From Library", nil),
38
+ style: UIAlertActionStyleDefault,
39
+ handler: lambda { |_|
40
+ self.formViewController.dismissViewControllerAnimated(true,
41
+ completion: -> {
42
+ open_picker(UIImagePickerControllerSourceTypePhotoLibrary)
43
+ })
44
+ }))
45
+
46
+ if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceTypeCamera)
47
+ alert.addAction(UIAlertAction.actionWithTitle(NSLocalizedString("Take Photo", nil),
48
+ style: UIAlertActionStyleDefault,
49
+ handler: lambda { |_|
50
+ self.formViewController.dismissViewControllerAnimated(true,
51
+ completion: -> {
52
+ open_picker(UIImagePickerControllerSourceTypeCamera)
53
+ })
54
+ }))
55
+ end
56
+ if self.formViewController.presentedViewController
57
+ self.formViewController.dismissViewControllerAnimated(true,
58
+ completion: -> {
59
+ self.formViewController.presentViewController(alert, animated: true, completion: nil)
60
+ })
61
+ else
62
+ self.formViewController.presentViewController(alert, animated: true, completion: nil)
63
+ end
64
+
65
+ else
66
+ if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceTypeCamera)
67
+ action_sheet = UIActionSheet.alloc.initWithTitle(self.rowDescriptor.selectorTitle,
68
+ delegate: self,
69
+ cancelButtonTitle: NSLocalizedString("Cancel", nil),
70
+ destructiveButtonTitle: nil,
71
+ otherButtonTitles: NSLocalizedString("Choose From Library", nil), NSLocalizedString("Take Photo", nil), nil)
72
+ else
73
+ action_sheet = UIActionSheet.alloc.initWithTitle(self.rowDescriptor.selectorTitle,
74
+ delegate: self,
75
+ cancelButtonTitle: NSLocalizedString("Cancel", nil),
76
+ destructiveButtonTitle: nil,
77
+ otherButtonTitles: NSLocalizedString("Choose From Library", nil), nil)
78
+ end
79
+ action_sheet.tag = self.tag
80
+ action_sheet.showInView(self.formViewController.view)
81
+ end
82
+ end
83
+
84
+ def updateConstraints
85
+ ui_components = { "image" => imageview, "text" => text_label }
86
+
87
+ self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-16-[text]", options: 0, metrics: 0, views: ui_components))
88
+ self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-6-[text]", options: 0, metrics: 0, views: ui_components))
89
+
90
+ self.contentView.addConstraint(NSLayoutConstraint.constraintWithItem(imageview,
91
+ attribute: NSLayoutAttributeTop,
92
+ relatedBy: NSLayoutRelationEqual,
93
+ toItem: self.contentView,
94
+ attribute: NSLayoutAttributeTop,
95
+ multiplier: 1.0,
96
+ constant: 10.0))
97
+
98
+ self.contentView.addConstraint(NSLayoutConstraint.constraintWithItem(imageview,
99
+ attribute: NSLayoutAttributeBottom,
100
+ relatedBy: NSLayoutRelationEqual,
101
+ toItem: self.contentView,
102
+ attribute: NSLayoutAttributeBottom,
103
+ multiplier: 1.0,
104
+ constant: -10.0))
105
+
106
+ self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[image(width)]", options: 0, metrics: { "width" => @image_width }, views: ui_components))
107
+ self.contentView.addConstraint(NSLayoutConstraint.constraintWithItem(imageview,
108
+ attribute: NSLayoutAttributeCenterX,
109
+ relatedBy: NSLayoutRelationEqual,
110
+ toItem: self.contentView,
111
+ attribute: NSLayoutAttributeCenterX,
112
+ multiplier: 1.0,
113
+ constant: 0.0))
114
+ super
115
+ end
116
+
117
+ # trick to force the status bar to be hidden when presenting the UIImagePickerViewController
118
+ def navigationController(_, willShowViewController: _, animated: _)
119
+ UIApplication.sharedApplication.setStatusBarHidden true
120
+ end
121
+
122
+ def setImageValue(image)
123
+ self.rowDescriptor.value = image
124
+ imageview.image = image
125
+ end
126
+
127
+ def observeValueForKeyPath(key_path, ofObject: object, change: change, context: _)
128
+ if object == text_label && key_path == "text"
129
+ if change[NSKeyValueChangeKindKey] == NSKeyValueChangeSetting
130
+ text_label.sizeToFit
131
+ frame = text_label.frame
132
+ frame.origin = [16, 4]
133
+ text_label.frame = frame
134
+ self.contentView.needsUpdateConstraints
135
+ end
136
+ end
137
+ end
138
+
139
+ def dealloc
140
+ text_label.removeObserver(self, forKeyPath: "text")
141
+ end
142
+
143
+ # UIActionSheetDelegate
144
+ def actionSheet(action_sheet, clickedButtonAtIndex: button_index)
145
+ return if button_index == action_sheet.cancelButtonIndex
146
+
147
+ title = action_sheet.buttonTitleAtIndex(button_index)
148
+
149
+ case title
150
+ when NSLocalizedString("Choose From Library", nil)
151
+ open_picker(UIImagePickerControllerSourceTypePhotoLibrary)
152
+ when NSLocalizedString("Take Photo", nil)
153
+ open_picker(UIImagePickerControllerSourceTypeCamera)
154
+ else
155
+ return
156
+ end
157
+ end
158
+
159
+ def open_picker(source)
160
+ image_picker = UIImagePickerController.alloc.init
161
+ image_picker.delegate = self
162
+ image_picker.allowsEditing = true
163
+ image_picker.sourceType = source
164
+
165
+ if UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad
166
+ if @popover_controller && @popover_controller.isPopoverVisible
167
+ @popover_controller.dismissPopoverAnimated(true)
168
+ end
169
+ @popover_controller = UIPopoverController.alloc.initWithContentViewController(image_picker)
170
+ Dispatch::Queue.main.async do
171
+ @popover_controller.presentPopoverFromRect(self.contentView.frame,
172
+ inView: self.formViewController.view,
173
+ permittedArrowDirections: UIPopoverArrowDirectionAny,
174
+ animated: true)
175
+ end
176
+ else
177
+ if self.formViewController.presentedViewController
178
+ self.formViewController.dismissViewControllerAnimated(true,
179
+ completion: -> {
180
+ self.formViewController.presentViewController(image_picker,
181
+ animated: true,
182
+ completion: nil)
183
+ })
184
+ else
185
+ self.formViewController.presentViewController(image_picker,
186
+ animated: true,
187
+ completion: nil)
188
+ end
189
+ end
190
+ end
191
+
192
+ # UIImagePickerControllerDelegate
193
+ def imagePickerController(_, didFinishPickingMediaWithInfo: info)
194
+ editedImage = info[UIImagePickerControllerEditedImage]
195
+ originalImage = info[UIImagePickerControllerOriginalImage]
196
+ if editedImage
197
+ imageToUse = editedImage
198
+ else
199
+ imageToUse = originalImage
200
+ end
201
+ setImageValue(imageToUse)
202
+
203
+ if UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad
204
+ if @popover_controller and @popover_controller.isPopoverVisible
205
+ @popover_controller.dismissPopoverAnimated(true)
206
+ end
207
+ else
208
+ self.formViewController.dismissViewControllerAnimated(true, completion: nil)
209
+ end
210
+ end
211
+
212
+ # Properties
213
+ def imageview
214
+ return @imageview if @imageview
215
+
216
+ @imageview = UIImageView.autolayoutView
217
+ @imageview.layer.masksToBounds = true
218
+ @imageview.contentMode = UIViewContentModeScaleAspectFit
219
+ @imageview
220
+ end
221
+
222
+ def text_label
223
+ return @text_label if @text_label
224
+ @text_label = UILabel.autolayoutView
225
+ end
226
+
227
+ end
@@ -0,0 +1,18 @@
1
+ module ProMotion
2
+ class ValueTransformer
3
+ def self.transformedValueClass
4
+ NSString
5
+ end
6
+
7
+ def self.allowsReverseTransformation
8
+ false
9
+ end
10
+
11
+ def transformedValue(value)
12
+ return nil if value.nil?
13
+
14
+ # best effort
15
+ value.inspect
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,232 @@
1
+ module ProMotion
2
+ class XLForm
3
+
4
+ attr_reader :form_data
5
+
6
+ def initialize(form_data, opts={})
7
+ @form_data = form_data
8
+ @title = opts[:title] || ''
9
+ @required = opts[:required]
10
+ end
11
+
12
+ def build
13
+ form = XLFormDescriptor.formDescriptorWithTitle(@title)
14
+ form.addAsteriskToRequiredRowsTitle = (@required == :asterisks)
15
+
16
+ form_data.each do |section_data|
17
+ title = section_data[:title]
18
+
19
+ options = parse_section_options(section_data[:options])
20
+ insert_mode = section_insert_mode(section_data[:insert_mode])
21
+
22
+ section = XLFormSectionDescriptor.formSectionWithTitle(title, sectionOptions: options, sectionInsertMode: insert_mode)
23
+ if options.nonzero?
24
+ tag = section_data[:name]
25
+ mp("MutliValued section with no :name option", force_color: :red) unless tag
26
+ if tag.respond_to? :to_s
27
+ tag = tag.to_s
28
+ end
29
+ section.multivaluedTag = tag
30
+ end
31
+ section.footerTitle = section_data[:footer] if section_data[:footer]
32
+
33
+ add_proc section, :on_add, section_data[:on_add] if section_data[:on_add]
34
+ add_proc section, :on_remove, section_data[:on_remove] if section_data[:on_remove]
35
+
36
+ form.addFormSection(section)
37
+
38
+ section_data[:cells].each do |cell_data|
39
+ tag = cell_data[:name]
40
+ mp("Cell with no :name option", force_color: :red) unless tag
41
+ if tag.respond_to? :to_s
42
+ tag = tag.to_s
43
+ end
44
+ title = cell_data[:title]
45
+ type = cell_data[:type]
46
+ if type.nil? and cell_data[:cells]
47
+ type = :selector_push
48
+ end
49
+
50
+ cell = XLFormRowDescriptor.formRowDescriptorWithTag(tag, rowType: row_type(type), title: title)
51
+
52
+ cell.required = cell_data[:required]
53
+
54
+ properties = cell_data[:properties] || {}
55
+
56
+ # placeholder
57
+ cell.cellConfigAtConfigure.setObject(cell_data[:placeholder], forKey: "textField.placeholder") if cell_data[:placeholder]
58
+
59
+ # slider
60
+ if cell_data[:type] == :slider
61
+ min = properties[:min]
62
+ max = properties[:max]
63
+ step = properties[:step]
64
+ cell.cellConfigAtConfigure.setObject(min, forKey: "slider.minimumValue") if min
65
+ cell.cellConfigAtConfigure.setObject(max, forKey: "slider.maximumValue") if max
66
+ cell.cellConfigAtConfigure.setObject(step, forKey: "steps") if step
67
+ end
68
+
69
+ # dates
70
+ if [:date_inline, :datetime_inline, :time_inline, :date, :datetime, :time, :datepicker].include? cell_data[:type]
71
+ min = properties[:min]
72
+ max = properties[:max]
73
+ cell.cellConfigAtConfigure.setObject(min, forKey: "minimumDate") if min
74
+ cell.cellConfigAtConfigure.setObject(max, forKey: "maximumDate") if max
75
+ end
76
+
77
+ cell_class = cell_data[:cell_class]
78
+
79
+ # image
80
+ if cell_data[:type] == :image
81
+ cell_class = XLFormImageSelectorCell if cell_class.nil?
82
+ end
83
+
84
+ cell.cellClass = cell_class if cell_class
85
+
86
+ # subcells
87
+ if cell_data[:cells]
88
+ cell.action.viewControllerClass = ProMotion::XLSubFormScreen
89
+ cell.action.cells = cell_data[:cells]
90
+ cell.valueTransformer = ProMotion::ValueTransformer
91
+ end
92
+
93
+ # also accept default XLForm viewControllerClass
94
+ cell.action.viewControllerClass = cell_data[:view_controller_class] if cell_data[:view_controller_class]
95
+ cell.valueTransformer = cell_data[:value_transformer] if cell_data[:value_transformer]
96
+
97
+ # callbacks
98
+ add_proc cell, :on_change, cell_data[:on_change] if cell_data[:on_change]
99
+ add_proc cell, :on_add, cell_data[:on_add] if cell_data[:on_add]
100
+ add_proc cell, :on_remove, cell_data[:on_remove] if cell_data[:on_remove]
101
+
102
+ cell.selectorTitle = cell_data[:selector_title] if cell_data[:selector_title]
103
+ options = parse_options(cell_data[:options])
104
+ cell.selectorOptions = options
105
+
106
+ value = cell_data[:value]
107
+ if value and options
108
+ options.each do |opt|
109
+ if opt.formValue == value
110
+ value = opt
111
+ break
112
+ end
113
+ end
114
+ end
115
+
116
+ cell.value = value if value
117
+
118
+ cell.disabled = !cell_data[:enabled] if cell_data[:enabled]
119
+
120
+ section.addFormRow(cell)
121
+
122
+ # multi sections
123
+ if section.multivaluedTag
124
+ cell.action.required = @required
125
+ section.multivaluedRowTemplate = cell.copy
126
+ end
127
+ end
128
+ end
129
+
130
+ form
131
+ end
132
+
133
+ def get_callback(row, event)
134
+ return if @blocks.nil? or @blocks[row].nil? or @blocks[row][event].nil?
135
+
136
+ @blocks[row][event]
137
+ end
138
+
139
+ private
140
+
141
+ def add_proc(tag, event, block)
142
+ @blocks ||= {}
143
+ @blocks[tag] ||= {}
144
+ @blocks[tag][event] = block.respond_to?('weak!') ? block.weak! : block
145
+ end
146
+
147
+ def parse_options(options)
148
+ return nil if options.nil? or options.empty?
149
+
150
+ options.map do |key, text|
151
+ val = key
152
+ if val.is_a? Symbol
153
+ val = val.to_s
154
+ end
155
+ XLFormOptionsObject.formOptionsObjectWithValue(val, displayText: text)
156
+ end
157
+ end
158
+
159
+ def parse_section_options(options)
160
+ return section_options(:none) if options.nil?
161
+
162
+ opts = section_options(:none)
163
+ options.each do |opt|
164
+ opts |= section_options(opt)
165
+ end
166
+
167
+ opts
168
+ end
169
+
170
+ def section_insert_mode(symbol)
171
+ {
172
+ last_row: XLFormSectionInsertModeLastRow,
173
+ button: XLFormSectionInsertModeButton
174
+ }[symbol] || symbol || XLFormSectionInsertModeLastRow
175
+ end
176
+
177
+ def section_options(symbol)
178
+ {
179
+ none: XLFormSectionOptionNone,
180
+ insert: XLFormSectionOptionCanInsert,
181
+ delete: XLFormSectionOptionCanDelete,
182
+ reorder: XLFormSectionOptionCanReorder
183
+ }[symbol] || symbol || XLFormSectionOptionNone
184
+ end
185
+
186
+ def row_type(symbol)
187
+ {
188
+ text: XLFormRowDescriptorTypeText,
189
+ name: XLFormRowDescriptorTypeName,
190
+ url: XLFormRowDescriptorTypeURL,
191
+ email: XLFormRowDescriptorTypeEmail,
192
+ password: XLFormRowDescriptorTypePassword,
193
+ number: XLFormRowDescriptorTypeNumber,
194
+ phone: XLFormRowDescriptorTypePhone,
195
+ twitter: XLFormRowDescriptorTypeTwitter,
196
+ account: XLFormRowDescriptorTypeAccount,
197
+ integer: XLFormRowDescriptorTypeInteger,
198
+ decimal: XLFormRowDescriptorTypeDecimal,
199
+ textview: XLFormRowDescriptorTypeTextView,
200
+ selector_push: XLFormRowDescriptorTypeSelectorPush,
201
+ selector_popover: XLFormRowDescriptorTypeSelectorPopover,
202
+ selector_action_sheet: XLFormRowDescriptorTypeSelectorActionSheet,
203
+ selector_alert_view: XLFormRowDescriptorTypeSelectorAlertView,
204
+ selector_picker_view: XLFormRowDescriptorTypeSelectorPickerView,
205
+ selector_picker_view_inline: XLFormRowDescriptorTypeSelectorPickerViewInline,
206
+ multiple_selector: XLFormRowDescriptorTypeMultipleSelector,
207
+ multiple_selector_popover: XLFormRowDescriptorTypeMultipleSelectorPopover,
208
+ selector_left_right: XLFormRowDescriptorTypeSelectorLeftRight,
209
+ selector_segmented_control: XLFormRowDescriptorTypeSelectorSegmentedControl,
210
+ date_inline: XLFormRowDescriptorTypeDateInline,
211
+ datetime_inline: XLFormRowDescriptorTypeDateTimeInline,
212
+ time_inline: XLFormRowDescriptorTypeTimeInline,
213
+ countdown_timer_inline: XLFormRowDescriptorTypeCountDownTimerInline,
214
+ date: XLFormRowDescriptorTypeDate,
215
+ datetime: XLFormRowDescriptorTypeDateTime,
216
+ time: XLFormRowDescriptorTypeTime,
217
+ countdown_timer: XLFormRowDescriptorTypeCountDownTimer,
218
+ datepicker: XLFormRowDescriptorTypeDatePicker,
219
+ picker: XLFormRowDescriptorTypePicker,
220
+ slider: XLFormRowDescriptorTypeSlider,
221
+ check: XLFormRowDescriptorTypeBooleanCheck,
222
+ switch: XLFormRowDescriptorTypeBooleanSwitch,
223
+ button: XLFormRowDescriptorTypeButton,
224
+ info: XLFormRowDescriptorTypeInfo,
225
+ step_counter: XLFormRowDescriptorTypeStepCounter,
226
+ image: 'XLFormRowDescriptorTypeImage'
227
+ }[symbol] || symbol
228
+ end
229
+
230
+ end
231
+
232
+ end
@@ -0,0 +1,11 @@
1
+ module XLFormClassMethods
2
+
3
+ def form_options(opts={})
4
+ @form_options = opts
5
+ end
6
+
7
+ def get_form_options
8
+ @form_options || {}
9
+ end
10
+
11
+ end
@@ -0,0 +1,9 @@
1
+ module ProMotion
2
+ module XLFormModule
3
+
4
+ def self.included(base)
5
+ base.extend(XLFormClassMethods)
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ # Monkey patch
2
+ class XLFormAction
3
+
4
+ attr_accessor :cells
5
+ attr_accessor :required
6
+
7
+ alias :old_copyWithZone :copyWithZone
8
+
9
+ def copyWithZone(zone)
10
+ action_copy = old_copyWithZone(zone)
11
+
12
+ action_copy.cells = self.cells.copy
13
+ action_copy.required = self.required
14
+ action_copy
15
+ end
16
+ end
17
+
18
+ class XLFormRowDescriptor
19
+
20
+ alias :old_copyWithZone :copyWithZone
21
+
22
+ def copyWithZone(zone)
23
+ row_descriptor_copy = old_copyWithZone(zone)
24
+
25
+ row_descriptor_copy.valueTransformer = self.valueTransformer
26
+ row_descriptor_copy
27
+ end
28
+
29
+ end
@@ -0,0 +1,168 @@
1
+ module ProMotion
2
+ class XLFormScreen < XlFormViewController
3
+ include ProMotion::ScreenModule
4
+ include ProMotion::XLFormModule
5
+
6
+ attr_reader :form_object
7
+
8
+ def viewDidLoad
9
+ super
10
+ update_form_data
11
+
12
+ form_options = self.class.get_form_options
13
+
14
+ if form_options[:on_cancel]
15
+ on_cancel = form_options[:on_cancel]
16
+ title = NSLocalizedString('Cancel', nil)
17
+ item = :cancel
18
+ if on_cancel.is_a? Hash
19
+ title = on_cancel[:title] if on_cancel[:title]
20
+ item = on_cancel[:item] if on_cancel[:item]
21
+ end
22
+
23
+ set_nav_bar_button :left, {
24
+ system_item: item,
25
+ title: title,
26
+ action: 'on_cancel:'
27
+ }
28
+ end
29
+
30
+ if form_options[:on_save]
31
+ on_cancel = form_options[:on_save]
32
+ title = NSLocalizedString('Save', nil)
33
+ item = :save
34
+ if on_cancel.is_a? Hash
35
+ title = on_cancel[:title] if on_cancel[:title]
36
+ item = on_cancel[:item] if on_cancel[:item]
37
+ end
38
+
39
+ set_nav_bar_button :right, {
40
+ system_item: item,
41
+ title: title,
42
+ action: 'on_save:'
43
+ }
44
+ end
45
+ end
46
+
47
+ def form_data
48
+ PM.logger.info "You need to implement a `form_data` method in #{self.class.to_s}."
49
+ []
50
+ end
51
+
52
+ def update_form_data
53
+ form_options = self.class.get_form_options
54
+ title = self.class.title
55
+ required = form_options[:required]
56
+
57
+ @form_builder = PM::XLForm.new(self.form_data,
58
+ {
59
+ title: title,
60
+ required: required
61
+ })
62
+ @form_object = @form_builder.build
63
+ self.form = @form_object
64
+ end
65
+
66
+ def values
67
+ values = {}
68
+ formValues.each do |key, value|
69
+ if value.is_a? XLFormOptionsObject
70
+ value = value.formValue
71
+ end
72
+ values[key] = value
73
+ end
74
+
75
+ values
76
+ end
77
+
78
+ def valid?
79
+ validation_errors.empty?
80
+ end
81
+
82
+ alias :validation_errors :formValidationErrors
83
+
84
+ def display_errors
85
+ return if valid?
86
+
87
+ errors = validation_errors.map do |error|
88
+ error.localizedDescription
89
+ end
90
+
91
+ if NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1
92
+ alert = UIAlertController.alertControllerWithTitle(NSLocalizedString('Error', nil),
93
+ message: errors.join(', '),
94
+ preferredStyle: UIAlertControllerStyleAlert)
95
+ action = UIAlertAction.actionWithTitle(NSLocalizedString('OK', nil),
96
+ style: UIAlertActionStyleDefault,
97
+ handler: nil)
98
+ alert.addAction(action)
99
+ presentViewController(alert, animated: true, completion: nil)
100
+ else
101
+ alert = UIAlertView.new
102
+ alert.title = NSLocalizedString('Error', nil)
103
+ alert.message = errors.join(', ')
104
+ alert.show
105
+ end
106
+ end
107
+
108
+ protected
109
+ def on_cancel(_)
110
+ form_options = self.class.get_form_options
111
+ if form_options[:on_cancel]
112
+ on_cancel = form_options[:on_cancel]
113
+ if on_cancel.is_a? Hash
114
+ on_cancel = on_cancel[:action]
115
+ end
116
+ try on_cancel
117
+ end
118
+ end
119
+
120
+ def on_save(_)
121
+ unless valid?
122
+ display_errors
123
+ return
124
+ end
125
+
126
+ form_options = self.class.get_form_options
127
+ if form_options[:on_save]
128
+ on_save = form_options[:on_save]
129
+ if on_save.is_a? Hash
130
+ on_save = on_save[:action]
131
+ end
132
+ try on_save, values
133
+ end
134
+ end
135
+
136
+ ## XLFormDescriptorDelegate
137
+ def formSectionHasBeenAdded(section, atIndex: _)
138
+ super
139
+ callback = @form_builder.get_callback(section, :on_add)
140
+ callback.call if callback
141
+ end
142
+
143
+ def formSectionHasBeenRemoved(section, atIndex: _)
144
+ super
145
+ callback = @form_builder.get_callback(section, :on_remove)
146
+ callback.call if callback
147
+ end
148
+
149
+ def formRowHasBeenAdded(row, atIndexPath: _)
150
+ super
151
+ callback = @form_builder.get_callback(row, :on_add)
152
+ callback.call if callback
153
+ end
154
+
155
+ def formRowHasBeenRemoved(row, atIndexPath: _)
156
+ super
157
+ callback = @form_builder.get_callback(row, :on_remove)
158
+ callback.call if callback
159
+ end
160
+
161
+ def formRowDescriptorValueHasChanged(row, oldValue: old_value, newValue: new_value)
162
+ super
163
+ callback = @form_builder.get_callback(row, :on_change)
164
+ callback.call(old_value, new_value) if callback
165
+ end
166
+
167
+ end
168
+ end
@@ -0,0 +1,54 @@
1
+ module ProMotion
2
+ class XlFormViewController < XLFormViewController
3
+ def self.new(args = {})
4
+ s = self.alloc.init
5
+ s.screen_init(args) if s.respond_to?(:screen_init)
6
+ s
7
+ end
8
+
9
+ def loadView
10
+ self.respond_to?(:load_view) ? self.load_view : super
11
+ end
12
+
13
+ def viewDidLoad
14
+ super
15
+ self.view_did_load if self.respond_to?(:view_did_load)
16
+ end
17
+
18
+ def viewWillAppear(animated)
19
+ super
20
+ self.view_will_appear(animated) if self.respond_to?("view_will_appear:")
21
+ end
22
+
23
+ def viewDidAppear(animated)
24
+ super
25
+ self.view_did_appear(animated) if self.respond_to?("view_did_appear:")
26
+ end
27
+
28
+ def viewWillDisappear(animated)
29
+ self.view_will_disappear(animated) if self.respond_to?("view_will_disappear:")
30
+ super
31
+ end
32
+
33
+ def viewDidDisappear(animated)
34
+ self.view_did_disappear(animated) if self.respond_to?("view_did_disappear:")
35
+ super
36
+ end
37
+
38
+ def shouldAutorotateToInterfaceOrientation(orientation)
39
+ self.should_rotate(orientation)
40
+ end
41
+
42
+ def shouldAutorotate
43
+ self.should_autorotate
44
+ end
45
+
46
+ def willRotateToInterfaceOrientation(orientation, duration:duration)
47
+ self.will_rotate(orientation, duration)
48
+ end
49
+
50
+ def didRotateFromInterfaceOrientation(orientation)
51
+ self.on_rotate
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,43 @@
1
+ module ProMotion
2
+ class XLSubFormScreen < XLFormScreen
3
+ attr_accessor :rowDescriptor
4
+
5
+ def form_data
6
+ [
7
+ {
8
+ title: rowDescriptor.title,
9
+ cells: rowDescriptor.action.cells.map do |cell|
10
+ tag = cell[:name]
11
+ if tag.respond_to? :to_s
12
+ tag = tag.to_s
13
+ end
14
+ if rowDescriptor and rowDescriptor.value and rowDescriptor.value[tag]
15
+ cell.merge({ value: rowDescriptor.value[tag] })
16
+ else
17
+ cell
18
+ end
19
+ end
20
+ }
21
+ ]
22
+ end
23
+
24
+ def update_form_data
25
+ title = rowDescriptor.title
26
+ required = rowDescriptor.action.required
27
+
28
+ @form_builder = PM::XLForm.new(self.form_data,
29
+ {
30
+ title: title,
31
+ required: required
32
+ })
33
+ @form_object = @form_builder.build
34
+ self.form = @form_object
35
+ end
36
+
37
+ ## XLFormDescriptorDelegate
38
+ def formRowDescriptorValueHasChanged(row, oldValue: old_value, newValue: new_value)
39
+ super
40
+ rowDescriptor.value = values
41
+ end
42
+ end
43
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ProMotion-XLForm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Benjamin Michotte
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ProMotion
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: motion-cocoapods
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: motion-redgreen
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Adds XLForm screen support to ProMotion.
70
+ email:
71
+ - bmichotte@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - README.md
77
+ - lib/ProMotion-XLForm.rb
78
+ - lib/ProMotion/XLForm/cells/xl_form_image_selector_cell.rb
79
+ - lib/ProMotion/XLForm/value_transformer.rb
80
+ - lib/ProMotion/XLForm/xl_form.rb
81
+ - lib/ProMotion/XLForm/xl_form_class_methods.rb
82
+ - lib/ProMotion/XLForm/xl_form_module.rb
83
+ - lib/ProMotion/XLForm/xl_form_patch.rb
84
+ - lib/ProMotion/XLForm/xl_form_screen.rb
85
+ - lib/ProMotion/XLForm/xl_form_view_controller.rb
86
+ - lib/ProMotion/XLForm/xl_sub_form_screen.rb
87
+ homepage: https://github.com/bmichotte/ProMotion-XLForm
88
+ licenses:
89
+ - MIT
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.2.3
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Adds PM::XLFormScreen support to ProMotion, similar to ProMotion-form.
111
+ test_files: []