motion-form 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 +7 -0
- data/README.md +59 -0
- data/lib/motion-form.rb +8 -0
- data/lib/project/cells/base_cell.rb +31 -0
- data/lib/project/cells/button_cell.rb +52 -0
- data/lib/project/cells/text_field_cell.rb +63 -0
- data/lib/project/controllers/form_controller.rb +17 -0
- data/lib/project/form/base.rb +123 -0
- data/lib/project/keyboard_avoiding_delegate.rb +113 -0
- data/lib/project/motion-form.rb +32 -0
- data/lib/project/rows/base_row.rb +43 -0
- data/lib/project/rows/button_row.rb +27 -0
- data/lib/project/rows/text_field_row.rb +25 -0
- data/lib/project/section/section.rb +21 -0
- data/lib/project/string.rb +9 -0
- data/lib/project/views/icon_view.rb +38 -0
- data/lib/project/views/padded_label.rb +17 -0
- data/lib/project/views/section_header_view.rb +21 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 62b070da82951513aecbfda093cfbc243beb1768
|
4
|
+
data.tar.gz: 24a8bea72a87cfdeb04d08d121f0fe63ccb6be1a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 46285b557f7bbb68a6ded12d7e139ec8d8293330fb68f96614854b7cf073dfd5d83400293d1d8209618925fc070b53cbc8141e007602938be1b60d2cfaa9db89
|
7
|
+
data.tar.gz: b9d211dd2c64ff3054a7425b521f4a54205431f8983b5a170b4ca723ecdc7505d2bc9a540f28a4dd6dc2b401a054fad9d68551600d79dd6d3cce5aeec039223b
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# motion-form
|
2
|
+
|
3
|
+
RubyMotion forms made easy.
|
4
|
+
|
5
|
+
motion-form is heavily inspired by the gem simple_form for Rails.
|
6
|
+
|
7
|
+
It aims to bring a simple, yet flexible DSL to the tedious task of creating iOS forms.
|
8
|
+
|
9
|
+

|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
form = MotionForm.form_for(view) do |f|
|
15
|
+
f.section 'Profile' do |section|
|
16
|
+
section.input :name, icon: :contact
|
17
|
+
section.input :username, icon: :user
|
18
|
+
section.input :pinterest, icon: :pinterest
|
19
|
+
section.input :twitter, icon: :twitter
|
20
|
+
section.input :website, icon: :website
|
21
|
+
section.input :bio, icon: :info
|
22
|
+
end
|
23
|
+
|
24
|
+
f.section 'Account' do |section|
|
25
|
+
section.button :change_email, icon: :email, action: push_email_controller
|
26
|
+
section.button :change_password, icon: :lock, action: push_password_controller
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def push_email_controller
|
31
|
+
# push controller
|
32
|
+
end
|
33
|
+
|
34
|
+
def push_password_controller
|
35
|
+
# push controller
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
## Installation
|
40
|
+
|
41
|
+
Add this line to your application's Gemfile:
|
42
|
+
|
43
|
+
gem 'motion-form'
|
44
|
+
|
45
|
+
And then execute:
|
46
|
+
|
47
|
+
$ bundle
|
48
|
+
|
49
|
+
Or install it yourself as:
|
50
|
+
|
51
|
+
$ gem install motion-form
|
52
|
+
|
53
|
+
## Contributing
|
54
|
+
|
55
|
+
1. Fork it
|
56
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
57
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
58
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
59
|
+
5. Create new Pull Request
|
data/lib/motion-form.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
unless defined?(Motion::Project::Config)
|
2
|
+
raise "This file must be required within a RubyMotion project Rakefile."
|
3
|
+
end
|
4
|
+
|
5
|
+
lib_dir_path = File.dirname(File.expand_path(__FILE__))
|
6
|
+
Motion::Project::App.setup do |app|
|
7
|
+
app.files.unshift(Dir.glob(File.join(lib_dir_path, "project/**/*.rb")))
|
8
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class BaseCell < UITableViewCell
|
2
|
+
attr_accessor :key
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def has_value?
|
6
|
+
false
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def initWithStyle(style, reuseIdentifier: reuse_identifier)
|
11
|
+
super.tap do |cell|
|
12
|
+
cell.selectionStyle = UITableViewCellSelectionStyleNone
|
13
|
+
cell.contentView.addSubview(text_field)
|
14
|
+
cell.selectedBackgroundView = selected_background_view
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def selected_background_view
|
19
|
+
UIView.alloc.initWithFrame(bounds).tap do |view|
|
20
|
+
view.backgroundColor = 'FFCDCB'.to_color
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def notification_center
|
25
|
+
NSNotificationCenter.defaultCenter
|
26
|
+
end
|
27
|
+
|
28
|
+
def post(notification)
|
29
|
+
notification_center.postNotificationName(notification, object: self, userInfo: notification_payload)
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class ButtonCell < TextFieldCell
|
2
|
+
IDENTIFIER = 'ButtonCell'
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def has_value?
|
6
|
+
false
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def initWithStyle(style, reuseIdentifier: reuse_identifier)
|
11
|
+
super.tap do |cell|
|
12
|
+
cell.selectionStyle = UITableViewCellSelectionStyleGray
|
13
|
+
cell.text_field.enabled = false
|
14
|
+
|
15
|
+
cell.add_right_view
|
16
|
+
cell.add_tap_recognizer
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_tap_recognizer
|
21
|
+
addGestureRecognizer(tap_recognizer)
|
22
|
+
end
|
23
|
+
|
24
|
+
def tap_recognizer
|
25
|
+
UITapGestureRecognizer.alloc.initWithTarget(self, action: 'tapped:')
|
26
|
+
end
|
27
|
+
|
28
|
+
def tapped(recognizer)
|
29
|
+
post('FormCellWasTapped')
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_right_view
|
33
|
+
text_field.rightView = right_view
|
34
|
+
text_field.rightViewMode = UITextFieldViewModeAlways
|
35
|
+
end
|
36
|
+
|
37
|
+
def right_view
|
38
|
+
@right_view ||= IconView.alloc.init.tap do |icon|
|
39
|
+
icon.name = :forward_arrow
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def layoutSubviews
|
44
|
+
super
|
45
|
+
|
46
|
+
right_view.frame = [[size.width - 56, 0], [36, 43]]
|
47
|
+
end
|
48
|
+
|
49
|
+
def notification_payload
|
50
|
+
{ key: key }
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class TextFieldCell < BaseCell
|
2
|
+
IDENTIFIER = 'TextFieldCell'
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def has_value?
|
6
|
+
true
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def label=(label)
|
11
|
+
text_field.placeholder = label
|
12
|
+
end
|
13
|
+
|
14
|
+
def icon=(icon)
|
15
|
+
left_view.name = icon
|
16
|
+
end
|
17
|
+
|
18
|
+
def text_field
|
19
|
+
@text_field ||= UITextField.alloc.init.tap do |field|
|
20
|
+
field.autocorrectionType = UITextAutocorrectionTypeNo
|
21
|
+
field.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
|
22
|
+
field.backgroundColor = UIColor.clearColor
|
23
|
+
field.clearButtonMode = UITextFieldViewModeWhileEditing
|
24
|
+
field.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter
|
25
|
+
field.leftView = left_view
|
26
|
+
field.leftViewMode = UITextFieldViewModeAlways
|
27
|
+
field.textColor = UIColor.grayColor
|
28
|
+
|
29
|
+
field.delegate = self
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def left_view
|
34
|
+
@left_view ||= IconView.alloc.init
|
35
|
+
end
|
36
|
+
|
37
|
+
def value
|
38
|
+
text_field.text
|
39
|
+
end
|
40
|
+
|
41
|
+
def layoutSubviews
|
42
|
+
text_field.frame = [[10, 0], [300, 43]]
|
43
|
+
left_view.frame = [[0, 0], [36, 43]]
|
44
|
+
end
|
45
|
+
|
46
|
+
def textFieldDidBeginEditing(text_field)
|
47
|
+
post('FormCellDidBeginEditing')
|
48
|
+
end
|
49
|
+
|
50
|
+
def textFieldDidEndEditing(text_field)
|
51
|
+
post('FormCellDidEndEditing')
|
52
|
+
end
|
53
|
+
|
54
|
+
def textFieldShouldReturn(text_field)
|
55
|
+
text_field.resignFirstResponder
|
56
|
+
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def notification_payload
|
61
|
+
{ key: key, value: value, text_field: text_field }
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module MotionForm
|
2
|
+
class Base < UITableView
|
3
|
+
attr_reader :keyboard_avoiding_delegate
|
4
|
+
|
5
|
+
def init
|
6
|
+
initWithFrame(frame, style: UITableViewStylePlain).tap do |f|
|
7
|
+
f.register_cells
|
8
|
+
f.separatorStyle = UITableViewCellSeparatorStyleNone
|
9
|
+
|
10
|
+
f.dataSource = self
|
11
|
+
f.delegate = self
|
12
|
+
|
13
|
+
@keyboard_avoiding_delegate = Motion::KeyboardAvoiding.new(f)
|
14
|
+
listen
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def listen
|
19
|
+
observers << notification_center.addObserver(self, selector: 'did_begin_editing:', name: 'FormCellDidBeginEditing', object: nil)
|
20
|
+
observers << notification_center.addObserver(self, selector: 'did_end_editing:', name: 'FormCellDidEndEditing', object: nil)
|
21
|
+
end
|
22
|
+
|
23
|
+
def did_begin_editing(notification)
|
24
|
+
unless window.nil?
|
25
|
+
keyboard_avoiding_delegate.textFieldDidBeginEditing(notification.userInfo[:text_field])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def did_end_editing(notification)
|
30
|
+
unless window.nil?
|
31
|
+
keyboard_avoiding_delegate.textFieldDidEndEditing(notification.userInfo[:text_field])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def observers
|
36
|
+
@observers ||= []
|
37
|
+
end
|
38
|
+
|
39
|
+
def notification_center
|
40
|
+
NSNotificationCenter.defaultCenter
|
41
|
+
end
|
42
|
+
|
43
|
+
def section(title = '')
|
44
|
+
build_section(title).tap do |section|
|
45
|
+
yield section
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_section(title)
|
50
|
+
MotionForm::Section.new(title).tap do |section|
|
51
|
+
sections << section
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def sections
|
56
|
+
@sections ||= []
|
57
|
+
end
|
58
|
+
|
59
|
+
def register_cells
|
60
|
+
MotionForm.registered_cells.each do |klass|
|
61
|
+
registerClass(klass, forCellReuseIdentifier: klass::IDENTIFIER)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def numberOfSectionsInTableView(table_view)
|
66
|
+
sections.count
|
67
|
+
end
|
68
|
+
|
69
|
+
def tableView(table_view, numberOfRowsInSection: section_index)
|
70
|
+
sections[section_index].rows.count
|
71
|
+
end
|
72
|
+
|
73
|
+
def tableView(table_view, didSelectRowAtIndexPath: index_path)
|
74
|
+
section = sections[index_path.section]
|
75
|
+
row = section.rows[index_path.row]
|
76
|
+
end
|
77
|
+
|
78
|
+
def tableView(table_view, viewForHeaderInSection: section)
|
79
|
+
unless sections[section].title.blank?
|
80
|
+
SectionHeaderView.alloc.initWithFrame([[0, 0], [size.width, 44.0]]).tap do |header|
|
81
|
+
header.text = table_view.tableView(table_view, titleForHeaderInSection: section)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def tableView(table_view, titleForHeaderInSection: section)
|
87
|
+
sections[section].title
|
88
|
+
end
|
89
|
+
|
90
|
+
def tableView(table_view, heightForHeaderInSection: section)
|
91
|
+
if sections[section].title.blank?
|
92
|
+
0.0
|
93
|
+
else
|
94
|
+
44.0
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def rows
|
99
|
+
sections.map(&:rows).flatten
|
100
|
+
end
|
101
|
+
|
102
|
+
def tableView(table_view, cellForRowAtIndexPath: index_path)
|
103
|
+
section = sections[index_path.section]
|
104
|
+
row = section.rows[index_path.row]
|
105
|
+
|
106
|
+
table_view.dequeueReusableCellWithIdentifier(row.cell_identifier).tap do |cell|
|
107
|
+
row.update_cell(cell)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def render
|
112
|
+
Hash[render_values]
|
113
|
+
end
|
114
|
+
|
115
|
+
def render_values
|
116
|
+
value_rows.map { |row| [row.key, row.value] }
|
117
|
+
end
|
118
|
+
|
119
|
+
def value_rows
|
120
|
+
rows.select { |row| row.has_value? }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
class KeyboardAvoidingDelegate
|
2
|
+
KEYBOARD_ANIMATION_DURATION = 0.3
|
3
|
+
MINIMUM_SCROLL_FRACTION = 0.2
|
4
|
+
MAXIMUM_SCROLL_FRACTION = 0.8
|
5
|
+
PORTRAIT_KEYBOARD_HEIGHT = 216.0
|
6
|
+
LANDSCAPE_KEYBOARD_HEIGHT = 162.0
|
7
|
+
|
8
|
+
attr_accessor :view, :animated_distance, :active_text_field
|
9
|
+
|
10
|
+
def initialize(view)
|
11
|
+
self.view = view
|
12
|
+
|
13
|
+
add_tap_recognizer
|
14
|
+
listen_to_notifications
|
15
|
+
end
|
16
|
+
|
17
|
+
def listen_to_notifications
|
18
|
+
observers << notification_center.addObserver(self, selector: 'did_begin_editing:', name: 'FormCellDidBeginEditing', object: nil)
|
19
|
+
observers << notification_center.addObserver(self, selector: 'did_end_editing:', name: 'FormCellDidEndEditing', object: nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
def notification_center
|
23
|
+
NSNotificationCenter.defaultCenter
|
24
|
+
end
|
25
|
+
|
26
|
+
def did_begin_editing(notification)
|
27
|
+
textFieldDidBeginEditing(notification.userInfo[:text_field])
|
28
|
+
end
|
29
|
+
|
30
|
+
def did_end_editing(notification)
|
31
|
+
textFieldDidEndEditing(notification.userInfo[:text_field])
|
32
|
+
end
|
33
|
+
|
34
|
+
def observers
|
35
|
+
@observers ||= []
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove_all_observers
|
39
|
+
observers.each do |observer|
|
40
|
+
notification_center.removeObserver(observer)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def dealloc
|
45
|
+
remove_all_observers
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_tap_recognizer
|
49
|
+
view.addGestureRecognizer(tap_recognizer)
|
50
|
+
end
|
51
|
+
|
52
|
+
def tap_recognizer
|
53
|
+
UITapGestureRecognizer.alloc.initWithTarget(self, action: 'dismiss_keyboard')
|
54
|
+
end
|
55
|
+
|
56
|
+
def dismiss_keyboard
|
57
|
+
active_text_field.resignFirstResponder if active_text_field
|
58
|
+
end
|
59
|
+
|
60
|
+
def textFieldDidBeginEditing(text_field)
|
61
|
+
self.active_text_field = text_field
|
62
|
+
|
63
|
+
text_field_rect = view.window.convertRect(text_field.bounds, fromView: text_field)
|
64
|
+
view_rect = view.window.convertRect(view.bounds, fromView: view)
|
65
|
+
midline = text_field_rect.origin.y + 0.5 * text_field_rect.size.height
|
66
|
+
numerator = midline - view_rect.origin.y - MINIMUM_SCROLL_FRACTION * view_rect.size.height
|
67
|
+
denominator = (MAXIMUM_SCROLL_FRACTION - MINIMUM_SCROLL_FRACTION) * view_rect.size.height
|
68
|
+
height_fraction = numerator / denominator
|
69
|
+
|
70
|
+
if height_fraction < 0.0
|
71
|
+
height_fraction = 0.0
|
72
|
+
elsif height_fraction > 1.0
|
73
|
+
height_fraction = 1.0
|
74
|
+
end
|
75
|
+
|
76
|
+
orientation = UIApplication.sharedApplication.statusBarOrientation
|
77
|
+
if orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown
|
78
|
+
self.animated_distance = (PORTRAIT_KEYBOARD_HEIGHT * height_fraction).floor
|
79
|
+
else
|
80
|
+
self.animated_distance = (LANDSCAPE_KEYBOARD_HEIGHT * height_fraction).floor
|
81
|
+
end
|
82
|
+
|
83
|
+
view_frame = view.frame
|
84
|
+
view_frame.origin.y -= animated_distance
|
85
|
+
|
86
|
+
UIView.beginAnimations(nil, context: nil)
|
87
|
+
UIView.setAnimationBeginsFromCurrentState(true)
|
88
|
+
UIView.setAnimationDuration(KEYBOARD_ANIMATION_DURATION)
|
89
|
+
|
90
|
+
view.setFrame(view_frame)
|
91
|
+
|
92
|
+
UIView.commitAnimations
|
93
|
+
end
|
94
|
+
|
95
|
+
def textFieldDidEndEditing(text_field)
|
96
|
+
view_frame = view.frame
|
97
|
+
view_frame.origin.y += animated_distance
|
98
|
+
|
99
|
+
UIView.beginAnimations(nil, context: nil)
|
100
|
+
UIView.setAnimationBeginsFromCurrentState(true)
|
101
|
+
UIView.setAnimationDuration(KEYBOARD_ANIMATION_DURATION)
|
102
|
+
|
103
|
+
view.setFrame(view_frame)
|
104
|
+
|
105
|
+
UIView.commitAnimations
|
106
|
+
end
|
107
|
+
|
108
|
+
def textFieldShouldReturn(text_field)
|
109
|
+
text_field.resignFirstResponder
|
110
|
+
|
111
|
+
true
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module MotionForm
|
2
|
+
class << self
|
3
|
+
attr_accessor :icon_font
|
4
|
+
|
5
|
+
def config
|
6
|
+
yield self
|
7
|
+
end
|
8
|
+
|
9
|
+
def form_for(view)
|
10
|
+
MotionForm::Base.new.tap do |form|
|
11
|
+
|
12
|
+
yield form if block_given?
|
13
|
+
|
14
|
+
form.frame = view.bounds
|
15
|
+
|
16
|
+
view.addSubview(form)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def register_cell(cell)
|
21
|
+
registered_cells << cell
|
22
|
+
end
|
23
|
+
|
24
|
+
def registered_cells
|
25
|
+
@registered_cells ||= included_cells
|
26
|
+
end
|
27
|
+
|
28
|
+
def included_cells
|
29
|
+
[TextFieldCell, ButtonCell]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class BaseRow
|
2
|
+
attr_reader :icon, :key, :options
|
3
|
+
|
4
|
+
def initialize(key, options)
|
5
|
+
@key = key
|
6
|
+
@options = options
|
7
|
+
|
8
|
+
@icon = options.fetch(:icon, nil)
|
9
|
+
@value = options.fetch(:value, nil)
|
10
|
+
end
|
11
|
+
|
12
|
+
def observers
|
13
|
+
@observers ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove_all_observers
|
17
|
+
observers.each do |observer|
|
18
|
+
notification_center.removeObserver(observer)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def notification_center
|
23
|
+
NSNotificationCenter.defaultCenter
|
24
|
+
end
|
25
|
+
|
26
|
+
def cell_identifier
|
27
|
+
cell_type::IDENTIFIER
|
28
|
+
end
|
29
|
+
|
30
|
+
def has_value?
|
31
|
+
cell_type.has_value?
|
32
|
+
end
|
33
|
+
|
34
|
+
def update_cell(cell)
|
35
|
+
cell.key = key
|
36
|
+
cell.label = name
|
37
|
+
cell.icon = icon
|
38
|
+
end
|
39
|
+
|
40
|
+
def name
|
41
|
+
key.to_s.titleize
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class ButtonRow < TextFieldRow
|
2
|
+
attr_reader :on_tap_callback
|
3
|
+
|
4
|
+
def initialize(key, options)
|
5
|
+
super
|
6
|
+
|
7
|
+
@on_tap_callback = options.fetch(:action, lambda {})
|
8
|
+
end
|
9
|
+
|
10
|
+
def cell_type
|
11
|
+
ButtonCell
|
12
|
+
end
|
13
|
+
|
14
|
+
def accessory
|
15
|
+
options.fetch(:accessory, nil)
|
16
|
+
end
|
17
|
+
|
18
|
+
def listen
|
19
|
+
observers << notification_center.addObserver(self, selector: 'tapped:', name: 'FormCellWasTapped', object: nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
def tapped(notification)
|
23
|
+
if notification.userInfo[:key] == key
|
24
|
+
on_tap_callback.call
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class TextFieldRow < BaseRow
|
2
|
+
attr_accessor :value
|
3
|
+
|
4
|
+
def initialize(key, options)
|
5
|
+
super
|
6
|
+
|
7
|
+
listen
|
8
|
+
end
|
9
|
+
|
10
|
+
def listen
|
11
|
+
observers << notification_center.addObserver(self, selector: 'did_end_editing:', name: 'FormCellDidEndEditing', object: nil)
|
12
|
+
end
|
13
|
+
|
14
|
+
def did_end_editing(notification)
|
15
|
+
self.value = notification.userInfo[:value] if notification.userInfo[:key] == key
|
16
|
+
end
|
17
|
+
|
18
|
+
def textFieldDidEndEditing(text_field)
|
19
|
+
self.value = text_field.text
|
20
|
+
end
|
21
|
+
|
22
|
+
def cell_type
|
23
|
+
TextFieldCell
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class MotionForm
|
2
|
+
class Section
|
3
|
+
attr_reader :title
|
4
|
+
|
5
|
+
def initialize(title = '')
|
6
|
+
@title = title
|
7
|
+
end
|
8
|
+
|
9
|
+
def input(key, options = {})
|
10
|
+
rows << TextFieldRow.new(key, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def button(key, options = {})
|
14
|
+
rows << ButtonRow.new(key, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def rows
|
18
|
+
@rows ||= []
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class IconView < UILabel
|
2
|
+
attr_reader :name
|
3
|
+
|
4
|
+
def init
|
5
|
+
super.tap do |view|
|
6
|
+
view.font = MotionForm.icon_font
|
7
|
+
view.backgroundColor = UIColor.clearColor
|
8
|
+
view.textColor = UIColor.grayColor
|
9
|
+
view.textAlignment = NSTextAlignmentCenter
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def name=(name)
|
14
|
+
@name = name
|
15
|
+
|
16
|
+
self.text = hex
|
17
|
+
end
|
18
|
+
|
19
|
+
def hex
|
20
|
+
unicode ? unicode.hex.chr(Encoding::UTF_8) : ''
|
21
|
+
end
|
22
|
+
|
23
|
+
def unicode
|
24
|
+
icon_mappings[name]
|
25
|
+
end
|
26
|
+
|
27
|
+
def icon_mappings
|
28
|
+
@_icon_mappings ||= BW::JSON.parse(json_data.to_str)
|
29
|
+
end
|
30
|
+
|
31
|
+
def json_data
|
32
|
+
NSData.dataWithContentsOfFile(json_path)
|
33
|
+
end
|
34
|
+
|
35
|
+
def json_path
|
36
|
+
NSBundle.mainBundle.pathForResource('font_icons', ofType: 'json')
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class PaddedLabel < UILabel
|
2
|
+
def drawTextInRect(rect)
|
3
|
+
insets = UIEdgeInsets.new(0, padding, 0, padding)
|
4
|
+
|
5
|
+
padded_rect = UIEdgeInsetsInsetRect(rect, insets)
|
6
|
+
|
7
|
+
super(padded_rect)
|
8
|
+
end
|
9
|
+
|
10
|
+
def padding=(padding)
|
11
|
+
@padding = padding
|
12
|
+
end
|
13
|
+
|
14
|
+
def padding
|
15
|
+
@padding || 0
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class SectionHeaderView < UIView
|
2
|
+
def initWithFrame(frame)
|
3
|
+
super.tap do |view|
|
4
|
+
view.addSubview(section_title)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def text=(text)
|
9
|
+
section_title.text = text
|
10
|
+
end
|
11
|
+
|
12
|
+
def section_title
|
13
|
+
@section_title ||= PaddedLabel.alloc.initWithFrame(bounds).tap do |label|
|
14
|
+
label.padding = 10
|
15
|
+
label.backgroundColor = UIColor.redColor
|
16
|
+
label.adjustsFontSizeToFitWidth = true
|
17
|
+
label.textColor = UIColor.whiteColor
|
18
|
+
label.font = UIFont.fontWithName('Helvetica Neue', size: 20.0)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: motion-form
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Devon Blandin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-07-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: motion-keyboard-avoiding
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: RubyMotion Forms made easy.
|
42
|
+
email:
|
43
|
+
- dblandin@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- README.md
|
49
|
+
- lib/motion-form.rb
|
50
|
+
- lib/project/cells/base_cell.rb
|
51
|
+
- lib/project/cells/button_cell.rb
|
52
|
+
- lib/project/cells/text_field_cell.rb
|
53
|
+
- lib/project/controllers/form_controller.rb
|
54
|
+
- lib/project/form/base.rb
|
55
|
+
- lib/project/keyboard_avoiding_delegate.rb
|
56
|
+
- lib/project/motion-form.rb
|
57
|
+
- lib/project/rows/base_row.rb
|
58
|
+
- lib/project/rows/button_row.rb
|
59
|
+
- lib/project/rows/text_field_row.rb
|
60
|
+
- lib/project/section/section.rb
|
61
|
+
- lib/project/string.rb
|
62
|
+
- lib/project/views/icon_view.rb
|
63
|
+
- lib/project/views/padded_label.rb
|
64
|
+
- lib/project/views/section_header_view.rb
|
65
|
+
homepage: https://github.com/dblandin/motion-form
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 2.0.0
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: Simple DSL for RubyMotion form creation
|
89
|
+
test_files: []
|