ProMotion-XLForm 0.0.1

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