formotion 0.0.3 → 0.5
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.
- data/.gitignore +2 -1
- data/CHANGELOG.md +10 -0
- data/Gemfile +6 -0
- data/LIST_OF_ROW_TYPES.md +163 -0
- data/NEW_ROW_TYPES.md +73 -0
- data/README.md +11 -14
- data/Rakefile +6 -0
- data/app/app_delegate.rb +22 -0
- data/examples/KitchenSink/Rakefile +1 -1
- data/examples/KitchenSink/app/app_delegate.rb +50 -2
- data/examples/Login/.gitignore +5 -0
- data/examples/Login/Rakefile +9 -0
- data/examples/Login/app/app_delegate.rb +16 -0
- data/examples/Login/app/login_controller.rb +47 -0
- data/examples/Login/app/user_controller.rb +38 -0
- data/examples/Login/spec/main_spec.rb +9 -0
- data/lib/formotion.rb +7 -1
- data/lib/formotion/{form.rb → form/form.rb} +0 -0
- data/lib/formotion/{form_delegate.rb → form/form_delegate.rb} +10 -15
- data/lib/formotion/patch/object.rb +9 -0
- data/lib/formotion/patch/ui_action_sheet.rb +27 -0
- data/lib/formotion/patch/ui_text_view.rb +122 -0
- data/lib/formotion/patch/ui_text_view_placeholder.rb +65 -0
- data/lib/formotion/{row.rb → row/row.rb} +37 -27
- data/lib/formotion/row/row_cell_builder.rb +28 -0
- data/lib/formotion/row_type/base.rb +41 -0
- data/lib/formotion/row_type/check_row.rb +30 -0
- data/lib/formotion/row_type/date_row.rb +61 -0
- data/lib/formotion/row_type/email_row.rb +11 -0
- data/lib/formotion/row_type/image_row.rb +99 -0
- data/lib/formotion/row_type/number_row.rb +11 -0
- data/lib/formotion/row_type/options_row.rb +24 -0
- data/lib/formotion/row_type/phone_row.rb +11 -0
- data/lib/formotion/row_type/row_type.rb +21 -0
- data/lib/formotion/row_type/slider_row.rb +43 -0
- data/lib/formotion/row_type/static_row.rb +6 -0
- data/lib/formotion/row_type/string_row.rb +112 -0
- data/lib/formotion/row_type/submit_row.rb +34 -0
- data/lib/formotion/row_type/switch_row.rb +19 -0
- data/lib/formotion/row_type/text_row.rb +76 -0
- data/lib/formotion/{section.rb → section/section.rb} +3 -0
- data/lib/formotion/version.rb +1 -1
- data/spec/form_spec.rb +5 -4
- data/spec/functional/character_spec.rb +70 -0
- data/spec/functional/check_row_spec.rb +42 -0
- data/spec/functional/date_row_spec.rb +63 -0
- data/spec/functional/image_row_spec.rb +82 -0
- data/spec/functional/options_row_spec.rb +27 -0
- data/spec/functional/slider_row_spec.rb +27 -0
- data/spec/functional/submit_row_spec.rb +30 -0
- data/spec/functional/switch_row_spec.rb +28 -0
- data/spec/functional/text_row_spec.rb +60 -0
- data/spec/row_type/check_spec.rb +27 -0
- data/spec/row_type/date_spec.rb +69 -0
- data/spec/row_type/email_spec.rb +22 -0
- data/spec/row_type/image_spec.rb +43 -0
- data/spec/row_type/number_spec.rb +22 -0
- data/spec/row_type/options_spec.rb +37 -0
- data/spec/row_type/phone_spec.rb +22 -0
- data/spec/row_type/slider_spec.rb +47 -0
- data/spec/row_type/static_spec.rb +15 -0
- data/spec/row_type/string_spec.rb +52 -0
- data/spec/row_type/submit_spec.rb +28 -0
- data/spec/row_type/switch_spec.rb +27 -0
- data/spec/row_type/text_spec.rb +41 -0
- data/spec/support/ui_control_wrap_extension.rb +7 -0
- metadata +88 -9
- data/lib/formotion/row_cell_builder.rb +0 -168
- data/lib/formotion/row_type.rb +0 -33
@@ -0,0 +1,28 @@
|
|
1
|
+
#################
|
2
|
+
#
|
3
|
+
# Formotion::RowCellBuilder
|
4
|
+
# RowCellBuilder handles taking Formotion::Rows
|
5
|
+
# and configuring UITableViewCells based on their properties.
|
6
|
+
#
|
7
|
+
#################
|
8
|
+
module Formotion
|
9
|
+
class RowCellBuilder
|
10
|
+
|
11
|
+
# PARAMS row.is_a? Formotion::Row
|
12
|
+
# RETURNS [cell configured to that row, a UITextField for that row if applicable or nil]
|
13
|
+
def self.make_cell(row)
|
14
|
+
cell, text_field = nil
|
15
|
+
|
16
|
+
cell = UITableViewCell.alloc.initWithStyle(row.object.cell_style, reuseIdentifier:row.reuse_identifier)
|
17
|
+
|
18
|
+
cell.accessoryType = UITableViewCellAccessoryNone
|
19
|
+
cell.textLabel.text = row.title
|
20
|
+
cell.detailTextLabel.text = row.subtitle
|
21
|
+
|
22
|
+
edit_field = row.object.build_cell(cell)
|
23
|
+
|
24
|
+
[cell, edit_field]
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Formotion
|
2
|
+
module RowType
|
3
|
+
class Base
|
4
|
+
attr_accessor :row, :tableView
|
5
|
+
|
6
|
+
def tableView
|
7
|
+
@tableView ||= self.row.form.table
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(row)
|
11
|
+
@row = row
|
12
|
+
end
|
13
|
+
|
14
|
+
def submit_button?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
# RowCellBuilder uses this to instantiate the UITableViewCell.
|
19
|
+
def cell_style
|
20
|
+
UITableViewCellStyleSubtitle
|
21
|
+
end
|
22
|
+
|
23
|
+
# builder method for row cell specific implementation
|
24
|
+
def build_cell(cell)
|
25
|
+
# implement in row class
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# called by the Row after all the setup and connections are made
|
30
|
+
# in #make_cell
|
31
|
+
def after_build(cell)
|
32
|
+
end
|
33
|
+
|
34
|
+
# method gets triggered when tableView(tableView, didSelectRowAtIndexPath:indexPath)
|
35
|
+
# in UITableViewDelegate is executed
|
36
|
+
def on_select(tableView, tableViewDelegate)
|
37
|
+
# implement in row class
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Formotion
|
2
|
+
module RowType
|
3
|
+
class CheckRow < Base
|
4
|
+
|
5
|
+
# This is actually called whenever again cell is checked/unchecked
|
6
|
+
# in the UITableViewDelegate callbacks. So (for now) don't
|
7
|
+
# instantiate long-lived objects in them.
|
8
|
+
# Maybe that logic should be moved elsewhere?
|
9
|
+
def build_cell(cell)
|
10
|
+
cell.accessoryType = row.value ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def on_select(tableView, tableViewDelegate)
|
15
|
+
if row.section.select_one and !row.value
|
16
|
+
row.section.rows.each do |other_row|
|
17
|
+
other_row.value = (other_row == row)
|
18
|
+
|
19
|
+
cell = tableView.cellForRowAtIndexPath(other_row.index_path)
|
20
|
+
other_row.object.build_cell(cell) if cell
|
21
|
+
end
|
22
|
+
elsif !row.section.select_one
|
23
|
+
row.value = !row.value
|
24
|
+
build_cell(tableView.cellForRowAtIndexPath(row.index_path))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Formotion
|
2
|
+
module RowType
|
3
|
+
class DateRow < StringRow
|
4
|
+
# overwrite Character on_change method
|
5
|
+
def on_change(text_field)
|
6
|
+
end
|
7
|
+
|
8
|
+
def update
|
9
|
+
self.row.text_field && self.row.text_field.text = self.formatted_value
|
10
|
+
end
|
11
|
+
|
12
|
+
def date_value
|
13
|
+
value = self.row.value
|
14
|
+
if value.is_a? Numeric
|
15
|
+
NSDate.dateWithTimeIntervalSince1970(value.to_i)
|
16
|
+
else
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def formatter
|
22
|
+
@formatter ||= begin
|
23
|
+
formatter = NSDateFormatter.new
|
24
|
+
|
25
|
+
date_style = self.row.format
|
26
|
+
if date_style && date_style.to_s[-5..-1] != "style"
|
27
|
+
date_style = (date_style.to_s + "_style").to_sym
|
28
|
+
end
|
29
|
+
formatter.dateStyle = self.row.send(:const_int_get, "NSDateFormatter", date_style || NSDateFormatterShortStyle)
|
30
|
+
formatter
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def formatted_value
|
35
|
+
return formatter.stringFromDate(self.date_value) if self.date_value
|
36
|
+
self.row.value
|
37
|
+
end
|
38
|
+
|
39
|
+
def after_build(cell)
|
40
|
+
self.row.text_field.inputView = self.picker
|
41
|
+
update
|
42
|
+
end
|
43
|
+
|
44
|
+
def picker
|
45
|
+
@picker ||= begin
|
46
|
+
picker = UIDatePicker.alloc.initWithFrame(CGRectZero)
|
47
|
+
picker.datePickerMode = UIDatePickerModeDate
|
48
|
+
picker.hidden = false
|
49
|
+
picker.date = self.date_value || NSDate.date
|
50
|
+
|
51
|
+
picker.when(UIControlEventValueChanged) do
|
52
|
+
self.row.value = @picker.date.timeIntervalSince1970.to_i
|
53
|
+
update
|
54
|
+
end
|
55
|
+
|
56
|
+
picker
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Formotion
|
2
|
+
module RowType
|
3
|
+
class ImageRow < Base
|
4
|
+
include BW::KVO
|
5
|
+
|
6
|
+
IMAGE_VIEW_TAG=1100
|
7
|
+
|
8
|
+
def build_cell(cell)
|
9
|
+
add_plus_accessory(cell)
|
10
|
+
|
11
|
+
observe(self.row, "value") do |old_value, new_value|
|
12
|
+
@image_view.image = new_value
|
13
|
+
if new_value
|
14
|
+
self.row.rowHeight = 200
|
15
|
+
cell.accessoryView = nil
|
16
|
+
else
|
17
|
+
self.row.rowHeight = 44
|
18
|
+
add_plus_accessory(cell)
|
19
|
+
end
|
20
|
+
row.form.reload_data
|
21
|
+
end
|
22
|
+
|
23
|
+
@image_view = UIImageView.alloc.init
|
24
|
+
@image_view.image = row.value if row.value
|
25
|
+
@image_view.tag = IMAGE_VIEW_TAG
|
26
|
+
@image_view.contentMode = UIViewContentModeScaleAspectFit
|
27
|
+
@image_view.backgroundColor = UIColor.clearColor
|
28
|
+
cell.addSubview(@image_view)
|
29
|
+
|
30
|
+
cell.swizzle(:layoutSubviews) do
|
31
|
+
def layoutSubviews
|
32
|
+
old_layoutSubviews
|
33
|
+
|
34
|
+
# viewWithTag is terrible, but I think it's ok to use here...
|
35
|
+
formotion_field = self.viewWithTag(IMAGE_VIEW_TAG)
|
36
|
+
|
37
|
+
field_frame = formotion_field.frame
|
38
|
+
field_frame.origin.y = 10
|
39
|
+
field_frame.origin.x = self.textLabel.frame.origin.x + self.textLabel.frame.size.width + 20
|
40
|
+
field_frame.size.width = self.frame.size.width - field_frame.origin.x - 20
|
41
|
+
field_frame.size.height = self.frame.size.height - 20
|
42
|
+
formotion_field.frame = field_frame
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_select(tableView, tableViewDelegate)
|
48
|
+
@action_sheet = UIActionSheet.alloc.init
|
49
|
+
@action_sheet.delegate = self
|
50
|
+
|
51
|
+
@action_sheet.destructiveButtonIndex = (@action_sheet.addButtonWithTitle "Delete") if row.value
|
52
|
+
@action_sheet.addButtonWithTitle "Take" if BW::Device.camera.front? or BW::Device.camera.rear?
|
53
|
+
@action_sheet.addButtonWithTitle "Choose"
|
54
|
+
@action_sheet.cancelButtonIndex = (@action_sheet.addButtonWithTitle "Cancel")
|
55
|
+
|
56
|
+
@action_sheet.showInView @image_view
|
57
|
+
end
|
58
|
+
|
59
|
+
def actionSheet actionSheet, clickedButtonAtIndex: index
|
60
|
+
source = nil
|
61
|
+
|
62
|
+
if index == actionSheet.destructiveButtonIndex
|
63
|
+
row.value = nil
|
64
|
+
return
|
65
|
+
end
|
66
|
+
|
67
|
+
case actionSheet.buttonTitleAtIndex(index)
|
68
|
+
when "Take"
|
69
|
+
source = :camera
|
70
|
+
when "Choose"
|
71
|
+
source = :photo_library
|
72
|
+
when "Cancel"
|
73
|
+
else
|
74
|
+
p "Unrecognized button title #{actionSheet.buttonTitleAtIndex(index)}"
|
75
|
+
end
|
76
|
+
|
77
|
+
if source
|
78
|
+
@camera = BW::Device.camera.any
|
79
|
+
@camera.picture(source_type: source, media_types: [:image]) do |result|
|
80
|
+
if result[:original_image]
|
81
|
+
row.value = result[:original_image]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def add_plus_accessory(cell)
|
88
|
+
@add_button ||= begin
|
89
|
+
button = UIButton.buttonWithType(UIButtonTypeContactAdd)
|
90
|
+
button.when(UIControlEventTouchUpInside) do
|
91
|
+
self.on_select(nil, nil)
|
92
|
+
end
|
93
|
+
button
|
94
|
+
end
|
95
|
+
cell.accessoryView = @add_button
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Formotion
|
2
|
+
module RowType
|
3
|
+
class OptionsRow < Base
|
4
|
+
|
5
|
+
SLIDER_VIEW_TAG = 1200
|
6
|
+
|
7
|
+
def build_cell(cell)
|
8
|
+
cell.selectionStyle = UITableViewCellSelectionStyleNone
|
9
|
+
slideView = UISegmentedControl.alloc.initWithItems(row.items || [])
|
10
|
+
slideView.selectedSegmentIndex = row.items.index(row.value) if row.value
|
11
|
+
slideView.segmentedControlStyle = UISegmentedControlStyleBar
|
12
|
+
cell.accessoryView = slideView
|
13
|
+
|
14
|
+
slideView.when(UIControlEventValueChanged) do
|
15
|
+
row.value = row.items[slideView.selectedSegmentIndex]
|
16
|
+
end
|
17
|
+
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Formotion
|
2
|
+
module RowType
|
3
|
+
ROW_TYPES = Formotion::RowType.constants(false).select { |constant_name| constant_name =~ /Row$/ }
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def for(string_or_sym)
|
7
|
+
type = string_or_sym
|
8
|
+
|
9
|
+
if type.is_a?(Symbol) or type.is_a? String
|
10
|
+
string = "#{type.to_s.downcase}_row".camelize
|
11
|
+
if not const_defined? string
|
12
|
+
raise Formotion::InvalidClassError, "Invalid RowType value #{string_or_sym}. Create a class called #{string}"
|
13
|
+
end
|
14
|
+
Formotion::RowType.const_get(string)
|
15
|
+
else
|
16
|
+
raise Formotion::InvalidClassError, "Attempted row type #{type.inspect} is not a valid RowType."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Formotion
|
2
|
+
module RowType
|
3
|
+
class SliderRow < Base
|
4
|
+
|
5
|
+
SLIDER_VIEW_TAG = 1200
|
6
|
+
|
7
|
+
def build_cell(cell)
|
8
|
+
cell.selectionStyle = UITableViewCellSelectionStyleNone
|
9
|
+
slideView = UISlider.alloc.initWithFrame(CGRectZero)
|
10
|
+
cell.accessoryView = slideView
|
11
|
+
row.range ||= (1..10)
|
12
|
+
slideView.minimumValue = row.range.first
|
13
|
+
slideView.maximumValue = row.range.last
|
14
|
+
slideView.tag = SLIDER_VIEW_TAG
|
15
|
+
slideView.setValue(row.value, animated:true) if row.value
|
16
|
+
slideView.accessibilityLabel = row.title + " Slider"
|
17
|
+
|
18
|
+
slideView.when(UIControlEventValueChanged) do
|
19
|
+
row.value = slideView.value
|
20
|
+
end
|
21
|
+
|
22
|
+
cell.swizzle(:layoutSubviews) do
|
23
|
+
def layoutSubviews
|
24
|
+
old_layoutSubviews
|
25
|
+
|
26
|
+
# viewWithTag is terrible, but I think it's ok to use here...
|
27
|
+
formotion_field = self.viewWithTag(SLIDER_VIEW_TAG)
|
28
|
+
formotion_field.sizeToFit
|
29
|
+
|
30
|
+
field_frame = formotion_field.frame
|
31
|
+
field_frame.origin.y = 10
|
32
|
+
field_frame.origin.x = self.textLabel.frame.origin.x + self.textLabel.frame.size.width + 20
|
33
|
+
field_frame.size.width = self.frame.size.width - field_frame.origin.x - 20
|
34
|
+
field_frame.size.height = self.frame.size.height - 20
|
35
|
+
formotion_field.frame = field_frame
|
36
|
+
end
|
37
|
+
end
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Formotion
|
2
|
+
module RowType
|
3
|
+
class StringRow < Base
|
4
|
+
|
5
|
+
# The new UITextField in a UITableViewCell
|
6
|
+
# will be assigned this tag, if applicable.
|
7
|
+
TEXT_FIELD_TAG=1000
|
8
|
+
|
9
|
+
def keyboardType
|
10
|
+
UIKeyboardTypeDefault
|
11
|
+
end
|
12
|
+
|
13
|
+
# Configures the cell to have a new UITextField
|
14
|
+
# which is used to enter data. Consists of
|
15
|
+
# 1) setting up that field with the appropriate properties
|
16
|
+
# specified by `row` 2) configures the callbacks on the field
|
17
|
+
# to call any callbacks `row` listens for.
|
18
|
+
# Also does the layoutSubviews swizzle trick
|
19
|
+
# to size the UITextField so it won't bump into the titleLabel.
|
20
|
+
def build_cell(cell)
|
21
|
+
field = UITextField.alloc.initWithFrame(CGRectZero)
|
22
|
+
field.tag = TEXT_FIELD_TAG
|
23
|
+
|
24
|
+
field.placeholder = row.placeholder
|
25
|
+
field.text = row.value.to_s
|
26
|
+
|
27
|
+
field.clearButtonMode = UITextFieldViewModeWhileEditing
|
28
|
+
field.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter
|
29
|
+
field.textAlignment = UITextAlignmentRight
|
30
|
+
|
31
|
+
field.keyboardType = keyboardType
|
32
|
+
|
33
|
+
field.secureTextEntry = true if row.secure?
|
34
|
+
field.returnKeyType = row.return_key || UIReturnKeyNext
|
35
|
+
field.autocapitalizationType = row.auto_capitalization if row.auto_capitalization
|
36
|
+
field.autocorrectionType = row.auto_correction if row.auto_correction
|
37
|
+
field.clearButtonMode = row.clear_button || UITextFieldViewModeWhileEditing
|
38
|
+
|
39
|
+
add_callbacks(field)
|
40
|
+
|
41
|
+
cell.swizzle(:layoutSubviews) do
|
42
|
+
def layoutSubviews
|
43
|
+
old_layoutSubviews
|
44
|
+
|
45
|
+
# viewWithTag is terrible, but I think it's ok to use here...
|
46
|
+
formotion_field = self.viewWithTag(TEXT_FIELD_TAG)
|
47
|
+
formotion_field.sizeToFit
|
48
|
+
|
49
|
+
field_frame = formotion_field.frame
|
50
|
+
field_frame.origin.x = self.textLabel.frame.origin.x + self.textLabel.frame.size.width + 20
|
51
|
+
field_frame.origin.y = ((self.frame.size.height - field_frame.size.height) / 2.0).round
|
52
|
+
field_frame.size.width = self.frame.size.width - field_frame.origin.x - 20
|
53
|
+
formotion_field.frame = field_frame
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
cell.addSubview(field)
|
58
|
+
field
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_callbacks(field)
|
63
|
+
if row.on_enter_callback
|
64
|
+
field.should_return? do |text_field|
|
65
|
+
if row.on_enter_callback.arity == 0
|
66
|
+
row.on_enter_callback.call
|
67
|
+
elsif row.on_enter_callback.arity == 1
|
68
|
+
row.on_enter_callback.call(row)
|
69
|
+
end
|
70
|
+
false
|
71
|
+
end
|
72
|
+
elsif field.returnKeyType == UIReturnKeyDone
|
73
|
+
field.should_return? do |text_field|
|
74
|
+
text_field.resignFirstResponder
|
75
|
+
false
|
76
|
+
end
|
77
|
+
else
|
78
|
+
field.should_return? do |text_field|
|
79
|
+
if row.next_row && row.next_row.text_field
|
80
|
+
row.next_row.text_field.becomeFirstResponder
|
81
|
+
else
|
82
|
+
text_field.resignFirstResponder
|
83
|
+
end
|
84
|
+
true
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
field.on_begin do |text_field|
|
89
|
+
row.on_begin_callback && row.on_begin_callback.call
|
90
|
+
end
|
91
|
+
|
92
|
+
field.should_begin? do |text_field|
|
93
|
+
row.section.form.active_row = row
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
field.on_change do |text_field|
|
98
|
+
on_change(text_field)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def on_change(text_field)
|
103
|
+
row.value = text_field.text
|
104
|
+
end
|
105
|
+
|
106
|
+
def on_select(tableView, tableViewDelegate)
|
107
|
+
row.text_field.becomeFirstResponder
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|