formotion 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.
- data/.gitignore +10 -0
- data/Formotion.gemspec +19 -0
- data/README.md +168 -0
- data/Rakefile +11 -0
- data/app/app_delegate.rb +68 -0
- data/examples/KitchenSink/.gitignore +5 -0
- data/examples/KitchenSink/README.md +5 -0
- data/examples/KitchenSink/Rakefile +9 -0
- data/examples/KitchenSink/app/app_delegate.rb +101 -0
- data/examples/KitchenSink/spec/main_spec.rb +9 -0
- data/lib/formotion.rb +6 -0
- data/lib/formotion/base.rb +44 -0
- data/lib/formotion/exceptions.rb +20 -0
- data/lib/formotion/form.rb +173 -0
- data/lib/formotion/form_controller.rb +102 -0
- data/lib/formotion/form_delegate.rb +87 -0
- data/lib/formotion/patch/ui_text_field.rb +158 -0
- data/lib/formotion/row.rb +211 -0
- data/lib/formotion/row_cell_builder.rb +168 -0
- data/lib/formotion/row_type.rb +33 -0
- data/lib/formotion/section.rb +108 -0
- data/lib/formotion/version.rb +3 -0
- data/spec/form_spec.rb +106 -0
- data/spec/row_spec.rb +25 -0
- data/spec/section_spec.rb +20 -0
- metadata +105 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
module Formotion
|
2
|
+
class InvalidClassError < StandardError; end
|
3
|
+
class InvalidSectionError < StandardError; end
|
4
|
+
|
5
|
+
class Conditions
|
6
|
+
class << self
|
7
|
+
def assert_nil_or_boolean(obj)
|
8
|
+
if not (obj.nil? or obj.is_a? TrueClass or obj.is_a? FalseClass)
|
9
|
+
raise Formotion::InvalidClassError, "#{obj.inspect} should be nil, true, or false, but is #{obj.class.to_s}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def assert_class(obj, klass)
|
14
|
+
if not obj.is_a? klass
|
15
|
+
raise Formotion::InvalidClassError, "#{obj.inspect} of class #{obj.class.to_s} is not of class #{klass.to_s}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
module Formotion
|
2
|
+
class Form < Formotion::Base
|
3
|
+
PROPERTIES = [
|
4
|
+
# By default, Formotion::Controller will set it's title to this
|
5
|
+
# (so navigation bars will reflect it).
|
6
|
+
:title,
|
7
|
+
# If you want to have some internal id to track the form.
|
8
|
+
:id
|
9
|
+
]
|
10
|
+
PROPERTIES.each {|prop|
|
11
|
+
attr_accessor prop
|
12
|
+
}
|
13
|
+
|
14
|
+
# Sections are create specially using #create_section, so we don't allow
|
15
|
+
# them to be pased in the hash
|
16
|
+
SERIALIZE_PROPERTIES = PROPERTIES + [:sections]
|
17
|
+
|
18
|
+
def initialize(params = {})
|
19
|
+
# super takes care of initializing the ::PROPERTIES in params
|
20
|
+
super
|
21
|
+
|
22
|
+
sections = params[:sections] || params["sections"]
|
23
|
+
sections && sections.each_with_index {|section_hash, index|
|
24
|
+
section = create_section(section_hash.merge({index: index}))
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
# Use this as a DSL for building forms
|
29
|
+
# EX
|
30
|
+
# @form = Form.build do |form|
|
31
|
+
# form.title = 'Form Title'
|
32
|
+
# form.id = 'anything'
|
33
|
+
# end
|
34
|
+
class << self
|
35
|
+
def build(&block)
|
36
|
+
form = new
|
37
|
+
block.call(form)
|
38
|
+
form
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Use this as a DSL for adding sections
|
43
|
+
# EX
|
44
|
+
# @form.build_section do |section|
|
45
|
+
# section.title = 'Section Title'
|
46
|
+
# end
|
47
|
+
def build_section(&block)
|
48
|
+
section = create_section
|
49
|
+
block.call(section)
|
50
|
+
section
|
51
|
+
end
|
52
|
+
|
53
|
+
# Use this to add sections via a hash
|
54
|
+
# EX
|
55
|
+
# @form.create_section(:title => 'Section Title')
|
56
|
+
def create_section(hash = {})
|
57
|
+
section = Formotion::Section.new(hash)
|
58
|
+
section.form = self
|
59
|
+
section.index = self.sections.count
|
60
|
+
self.sections << section
|
61
|
+
section
|
62
|
+
end
|
63
|
+
|
64
|
+
#########################
|
65
|
+
# attributes
|
66
|
+
|
67
|
+
def sections
|
68
|
+
@sections ||= []
|
69
|
+
end
|
70
|
+
|
71
|
+
def sections=(sections)
|
72
|
+
sections.each {|section|
|
73
|
+
Formotion::Conditions.assert_class(section, Formotion::Section)
|
74
|
+
}
|
75
|
+
@sections = sections
|
76
|
+
end
|
77
|
+
|
78
|
+
# Accepts an NSIndexPath and gives back a Formotion::Row
|
79
|
+
# EX
|
80
|
+
# row = @form.row_for_index_path(NSIndexPath.indexPathForRow(0, inSection: 0))
|
81
|
+
def row_for_index_path(index_path)
|
82
|
+
self.sections[index_path.section].rows[index_path.row]
|
83
|
+
end
|
84
|
+
|
85
|
+
#########################
|
86
|
+
# callbacks
|
87
|
+
|
88
|
+
# Stores the callback block when you do #submit.
|
89
|
+
# EX
|
90
|
+
# @form.on_submit do
|
91
|
+
# do_something(@form.render)
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# EX
|
95
|
+
# @form.on_submit do |form|
|
96
|
+
# pass_to_server(form.render)
|
97
|
+
# end
|
98
|
+
def on_submit(&block)
|
99
|
+
@on_submit_callback = block
|
100
|
+
end
|
101
|
+
|
102
|
+
# Triggers the #on_submit block
|
103
|
+
# Handles either zero or one arguments,
|
104
|
+
# as shown above.
|
105
|
+
def submit
|
106
|
+
if @on_submit_callback.arity == 0
|
107
|
+
@on_submit_callback.call
|
108
|
+
elsif @on_submit_callback.arity == 1
|
109
|
+
@on_submit_callback.call(self)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
#########################
|
114
|
+
# Retreiving data
|
115
|
+
|
116
|
+
# A complete hashification of the Form
|
117
|
+
# EX
|
118
|
+
# @form = Formotion::Form.new(title: 'My Title')
|
119
|
+
# @form.id = 'anything'
|
120
|
+
# @form.to_hash
|
121
|
+
# => {title: 'My Title', id: 'anything'}
|
122
|
+
def to_hash
|
123
|
+
# super handles all of the ::PROPERTIES
|
124
|
+
h = super
|
125
|
+
h[:sections] = self.sections.collect { |section|
|
126
|
+
section.to_hash
|
127
|
+
}
|
128
|
+
recursive_delete_nil(h)
|
129
|
+
h
|
130
|
+
end
|
131
|
+
|
132
|
+
# A hashification with the user's inputted values
|
133
|
+
# and row keys.
|
134
|
+
# EX
|
135
|
+
# @form = Formotion::Form.new(sections: [{
|
136
|
+
# rows: [{
|
137
|
+
# key: 'Email',
|
138
|
+
# editable: true,
|
139
|
+
# title: 'Email'
|
140
|
+
# }]}])
|
141
|
+
# ...user plays with the Form...
|
142
|
+
# @form.render
|
143
|
+
# => {email: 'something@email.com'}
|
144
|
+
def render
|
145
|
+
kv = {}
|
146
|
+
self.sections.each {|section|
|
147
|
+
if section.select_one?
|
148
|
+
section.rows.each {|row|
|
149
|
+
if row.value
|
150
|
+
kv[section.key] = row.key
|
151
|
+
end
|
152
|
+
}
|
153
|
+
else
|
154
|
+
section.rows.each {|row|
|
155
|
+
next if row.submit_button?
|
156
|
+
kv[row.key] = row.value
|
157
|
+
}
|
158
|
+
end
|
159
|
+
}
|
160
|
+
kv.delete_if {|k, v| k.nil? }
|
161
|
+
kv
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
def recursive_delete_nil(h)
|
166
|
+
delete_empty = Proc.new { |k, v|
|
167
|
+
v.delete_if(&delete_empty) if v.kind_of?(Hash)
|
168
|
+
v.nil?
|
169
|
+
}
|
170
|
+
h.delete_if &delete_empty
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
#################
|
2
|
+
#
|
3
|
+
# Formotion::FormController
|
4
|
+
# Use #initWithForm to create a view controller
|
5
|
+
# loaded with your form.
|
6
|
+
#
|
7
|
+
#################
|
8
|
+
module Formotion
|
9
|
+
class FormController < UIViewController
|
10
|
+
attr_accessor :form
|
11
|
+
attr_reader :table_view
|
12
|
+
|
13
|
+
# Initializes controller with a form
|
14
|
+
# PARAMS form.is_a? [Hash, Formotion::Form]
|
15
|
+
# RETURNS An instance of Formotion::FormController
|
16
|
+
def initWithForm(form)
|
17
|
+
self.initWithNibName(nil, bundle: nil)
|
18
|
+
self.form = form
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
# Set the form; ensure it is/can be converted to Formotion::Form
|
23
|
+
# or raises an exception.
|
24
|
+
def form=(form)
|
25
|
+
if form.is_a? Hash
|
26
|
+
form = Formotion::Form.new(form)
|
27
|
+
elsif not form.is_a? Formotion::Form
|
28
|
+
raise Formotion::InvalidClassError, "Attempted FormController.form = #{form.inspect} should be of type Formotion::Form or Hash"
|
29
|
+
end
|
30
|
+
@form = form
|
31
|
+
end
|
32
|
+
|
33
|
+
def viewDidLoad
|
34
|
+
super
|
35
|
+
|
36
|
+
# via https://gist.github.com/330916, could be wrong.
|
37
|
+
tabBarHeight = self.tabBarController && self.tabBarController.tabBar.bounds.size.height
|
38
|
+
tabBarHeight ||= 0
|
39
|
+
navBarHeight = self.navigationController && (self.navigationController.isNavigationBarHidden ? 0.0 : self.navigationController.navigationBar.bounds.size.height)
|
40
|
+
navBarHeight ||= 0
|
41
|
+
frame = self.view.frame
|
42
|
+
frame.size.height = frame.size.height - navBarHeight - tabBarHeight
|
43
|
+
self.view.frame = frame
|
44
|
+
|
45
|
+
self.title = self.form.title
|
46
|
+
|
47
|
+
NSNotificationCenter.defaultCenter.addObserver(self, selector:'keyboardWillHideOrShow:', name:UIKeyboardWillHideNotification, object:nil);
|
48
|
+
NSNotificationCenter.defaultCenter.addObserver(self, selector:'keyboardWillHideOrShow:', name:UIKeyboardWillShowNotification, object:nil);
|
49
|
+
|
50
|
+
@table_view = UITableView.alloc.initWithFrame(self.view.bounds, style: UITableViewStyleGrouped)
|
51
|
+
self.view.addSubview @table_view
|
52
|
+
|
53
|
+
# Triggers this block when the enter key is pressed
|
54
|
+
# while editing the last text field.
|
55
|
+
@form.sections[-1] && @form.sections[-1].rows[-1].on_enter do |row|
|
56
|
+
if row.text_field
|
57
|
+
@form.submit
|
58
|
+
row.text_field.resignFirstResponder
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Setting @form.controller assigns
|
63
|
+
# @form as the datasource and delegate
|
64
|
+
# and reloads the data.
|
65
|
+
@form.controller = self
|
66
|
+
end
|
67
|
+
|
68
|
+
# This re-sizes + scrolls the tableview to account for the keyboard size.
|
69
|
+
# TODO: Test this on iPads, etc.
|
70
|
+
def keyboardWillHideOrShow(note)
|
71
|
+
last_note = @keyboard_state
|
72
|
+
@keyboard_state = note.name
|
73
|
+
if last_note == @keyboard_state
|
74
|
+
return
|
75
|
+
end
|
76
|
+
|
77
|
+
userInfo = note.userInfo
|
78
|
+
duration = userInfo[UIKeyboardAnimationDurationUserInfoKey].doubleValue
|
79
|
+
curve = userInfo[UIKeyboardAnimationCurveUserInfoKey].intValue
|
80
|
+
keyboardFrame = userInfo[UIKeyboardFrameEndUserInfoKey].CGRectValue
|
81
|
+
|
82
|
+
view_frame = @table_view.frame;
|
83
|
+
|
84
|
+
if @keyboard_state == UIKeyboardWillHideNotification
|
85
|
+
view_frame.size.height = self.view.bounds.size.height
|
86
|
+
else
|
87
|
+
view_frame.size.height -= keyboardFrame.size.height
|
88
|
+
end
|
89
|
+
|
90
|
+
UIView.beginAnimations(nil, context: nil)
|
91
|
+
UIView.setAnimationDuration(duration)
|
92
|
+
UIView.setAnimationDelay(0)
|
93
|
+
UIView.setAnimationCurve(curve)
|
94
|
+
UIView.setAnimationBeginsFromCurrentState(true)
|
95
|
+
|
96
|
+
@table_view.frame = view_frame
|
97
|
+
@form.active_row = @form.active_row
|
98
|
+
|
99
|
+
UIView.commitAnimations
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Formotion
|
2
|
+
class Form
|
3
|
+
attr_reader :table
|
4
|
+
attr_reader :controller
|
5
|
+
attr_reader :active_row
|
6
|
+
|
7
|
+
def active_row=(row)
|
8
|
+
@active_row = row
|
9
|
+
|
10
|
+
if @active_row && @table
|
11
|
+
index_path = NSIndexPath.indexPathForRow(@active_row.index, inSection:@active_row.section.index)
|
12
|
+
@table.scrollToRowAtIndexPath(index_path, atScrollPosition:UITableViewScrollPositionMiddle, animated:true)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
####################
|
17
|
+
# Table Methods
|
18
|
+
def controller=(controller)
|
19
|
+
@controller = controller
|
20
|
+
self.table = controller.table_view
|
21
|
+
end
|
22
|
+
|
23
|
+
def table=(table_view)
|
24
|
+
@table = table_view
|
25
|
+
|
26
|
+
@table.delegate = self
|
27
|
+
@table.dataSource = self
|
28
|
+
reload_data
|
29
|
+
end
|
30
|
+
|
31
|
+
def reload_data
|
32
|
+
previous_row, next_row = nil
|
33
|
+
|
34
|
+
last_row = self.sections[-1].rows[-1]
|
35
|
+
if last_row
|
36
|
+
last_row.return_key ||= UIReturnKeyDone
|
37
|
+
end
|
38
|
+
|
39
|
+
@table.reloadData
|
40
|
+
end
|
41
|
+
|
42
|
+
# UITableViewDataSource Methods
|
43
|
+
def numberOfSectionsInTableView(tableView)
|
44
|
+
self.sections.count
|
45
|
+
end
|
46
|
+
|
47
|
+
def tableView(tableView, numberOfRowsInSection: section)
|
48
|
+
self.sections[section].rows.count
|
49
|
+
end
|
50
|
+
|
51
|
+
def tableView(tableView, titleForHeaderInSection:section)
|
52
|
+
section = self.sections[section].title
|
53
|
+
end
|
54
|
+
|
55
|
+
def tableView(tableView, cellForRowAtIndexPath:indexPath)
|
56
|
+
row = row_for_index_path(indexPath)
|
57
|
+
reuseIdentifier = row.reuse_identifier
|
58
|
+
|
59
|
+
cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier) || begin
|
60
|
+
row.make_cell
|
61
|
+
end
|
62
|
+
|
63
|
+
cell
|
64
|
+
end
|
65
|
+
|
66
|
+
# UITableViewDelegate Methods
|
67
|
+
def tableView(tableView, didSelectRowAtIndexPath:indexPath)
|
68
|
+
tableView.deselectRowAtIndexPath(indexPath, animated:true)
|
69
|
+
row = row_for_index_path(indexPath)
|
70
|
+
if row.submit_button?
|
71
|
+
self.submit
|
72
|
+
elsif row.checkable?
|
73
|
+
if row.section.select_one and !row.value
|
74
|
+
row.section.rows.each {|other_row|
|
75
|
+
other_row.value = (other_row == row)
|
76
|
+
Formotion::RowCellBuilder.make_check_cell(other_row, tableView.cellForRowAtIndexPath(other_row.index_path))
|
77
|
+
}
|
78
|
+
elsif !row.section.select_one
|
79
|
+
row.value = !row.value
|
80
|
+
Formotion::RowCellBuilder.make_check_cell(row, tableView.cellForRowAtIndexPath(row.index_path))
|
81
|
+
end
|
82
|
+
elsif row.editable?
|
83
|
+
row.text_field.becomeFirstResponder
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# Methods which use blocks for UITextFieldDelegate methods.
|
2
|
+
# EX
|
3
|
+
# field.should_end? do |text_field|
|
4
|
+
# if text_field.text != "secret"
|
5
|
+
# return false
|
6
|
+
# end
|
7
|
+
# true
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# Also includes an on_change method, which calls after the text
|
11
|
+
# has changed (there is no UITextFieldDelegate equivalent.)
|
12
|
+
# EX
|
13
|
+
# field.on_change do |text_field|
|
14
|
+
# p text_field.text
|
15
|
+
# end
|
16
|
+
|
17
|
+
class UITextField
|
18
|
+
attr_accessor :menu_options_enabled
|
19
|
+
|
20
|
+
def canPerformAction(action, withSender:sender)
|
21
|
+
self.menu_options_enabled
|
22
|
+
end
|
23
|
+
|
24
|
+
# block takes argument textField; should return true/false
|
25
|
+
def should_begin?(&block)
|
26
|
+
add_delegate_method do
|
27
|
+
@delegate.textFieldShouldBeginEditing_callback = block
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# block takes argument textField
|
32
|
+
def on_begin(&block)
|
33
|
+
add_delegate_method do
|
34
|
+
@delegate.textFieldDidBeginEditing_callback = block
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# block takes argument textField; should return true/false
|
39
|
+
def should_end?(&block)
|
40
|
+
add_delegate_method do
|
41
|
+
@delegate.textFieldShouldEndEditing_callback = block
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# block takes argument textField
|
46
|
+
def on_end(&block)
|
47
|
+
add_delegate_method do
|
48
|
+
@delegate.textFieldDidBeginEditing_callback = block
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# block takes argument textField, range [NSRange], and string; should return true/false
|
53
|
+
def should_change?(&block)
|
54
|
+
add_delegate_method do
|
55
|
+
@delegate.shouldChangeCharactersInRange_callback = block
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# block takes argument textField
|
60
|
+
def on_change(&block)
|
61
|
+
add_delegate_method do
|
62
|
+
@delegate.on_change_callback = block
|
63
|
+
self.addTarget(@delegate, action: 'on_change:', forControlEvents: UIControlEventEditingChanged)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# block takes argument textField; should return true/false
|
68
|
+
def should_clear?(&block)
|
69
|
+
add_delegate_method do
|
70
|
+
@delegate.textFieldShouldClear_callback = block
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# block takes argument textField; should return true/false
|
75
|
+
def should_return?(&block)
|
76
|
+
add_delegate_method do
|
77
|
+
@delegate.textFieldShouldReturn_callback = block
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
def add_delegate_method
|
83
|
+
# create strong reference to the delegate
|
84
|
+
# (.delegate= only creates a weak reference)
|
85
|
+
@delegate ||= UITextField_Delegate.new
|
86
|
+
yield
|
87
|
+
self.delegate = @delegate
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class UITextField_Delegate
|
92
|
+
[:textFieldShouldBeginEditing, :textFieldDidBeginEditing,
|
93
|
+
:textFieldShouldEndEditing, :textFieldDidEndEditing,
|
94
|
+
:shouldChangeCharactersInRange, :textFieldShouldClear,
|
95
|
+
:textFieldShouldReturn].each {|method|
|
96
|
+
attr_accessor (method.to_s + "_callback").to_sym
|
97
|
+
}
|
98
|
+
|
99
|
+
# Called from
|
100
|
+
# [textField addTarget:block
|
101
|
+
# action:'call'
|
102
|
+
# forControlEvents:UIControlEventEditingChanged],
|
103
|
+
# NOT a UITextFieldDelegate method.
|
104
|
+
attr_accessor :on_change_callback
|
105
|
+
|
106
|
+
def textFieldShouldBeginEditing(theTextField)
|
107
|
+
if self.textFieldShouldBeginEditing_callback
|
108
|
+
return self.textFieldShouldBeginEditing_callback.call(theTextField)
|
109
|
+
end
|
110
|
+
true
|
111
|
+
end
|
112
|
+
|
113
|
+
def textFieldDidBeginEditing(theTextField)
|
114
|
+
if self.textFieldDidBeginEditing_callback
|
115
|
+
return self.textFieldDidBeginEditing_callback.call(theTextField)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def textFieldShouldEndEditing(theTextField)
|
120
|
+
if self.textFieldShouldEndEditing_callback
|
121
|
+
return self.textFieldShouldEndEditing_callback.call(theTextField)
|
122
|
+
end
|
123
|
+
true
|
124
|
+
end
|
125
|
+
|
126
|
+
def textFieldDidEndEditing(theTextField)
|
127
|
+
if self.textFieldDidEndEditing_callback
|
128
|
+
return self.textFieldDidEndEditing_callback.call(theTextField)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def textField(theTextField, shouldChangeCharactersInRange:range, replacementString:string)
|
133
|
+
if self.shouldChangeCharactersInRange_callback
|
134
|
+
return self.shouldChangeCharactersInRange_callback.call(theTextField, range, string)
|
135
|
+
end
|
136
|
+
true
|
137
|
+
end
|
138
|
+
|
139
|
+
def on_change(theTextField)
|
140
|
+
if self.on_change_callback
|
141
|
+
self.on_change_callback.call(theTextField)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def textFieldShouldClear(theTextField)
|
146
|
+
if self.textFieldShouldClear_callback
|
147
|
+
return self.textFieldShouldClear_callback.call(theTextField)
|
148
|
+
end
|
149
|
+
true
|
150
|
+
end
|
151
|
+
|
152
|
+
def textFieldShouldReturn(theTextField)
|
153
|
+
if self.textFieldShouldReturn_callback
|
154
|
+
return self.textFieldShouldReturn_callback.call(theTextField)
|
155
|
+
end
|
156
|
+
true
|
157
|
+
end
|
158
|
+
end
|