motion-form 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![Screenshot](https://github.com/dblandin/motion-form/blob/master/img/screenshot.png?raw=true)
|
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: []
|