motion-form 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 09ef894ca746f523ff535a24fc6a087cea8a81d2
4
- data.tar.gz: e9f23285416fc9fcdc676952c90fbcd3aa438a5e
3
+ metadata.gz: c793153ac5710719c85021fde056751ffe3e2396
4
+ data.tar.gz: 031ba7b22c0c0a3a0ef0161d6e258b35fb1e491b
5
5
  SHA512:
6
- metadata.gz: f0971e202bc441add4a481064d56bdb29b2e971fcc79b1b5374ba40fd20250829d66dc1444965c94f81b14d20b06538ddf8a054bee95de96fef35236b72b8711
7
- data.tar.gz: d12f57fe38dadab75f55d55f2e1d35b9ba9adae4d37649d92cf3a4cae71068512e656e7922271d11cbe158ef1fe301d6fdbd9ac4b9ed7615eabaa4122a5ae1cc
6
+ metadata.gz: 238174d7a0399ad62ec129e827b171ed326f091adf3e48ec9d8b88d06aa2709d171de2088c79a64d418b2aadb14ef1c5cea5ba70f53c98f1c26b34aba9d039bf
7
+ data.tar.gz: 1958358b9767a1e39532443c316fb13714978704ed765a227bb15294eeb0885535d661a2b768898d96b85c80856b8c7c8f82e61889963052831fbe22e08e7b1d
data/lib/motion-form.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  require 'bubble-wrap/core'
2
2
  require 'motion-require'
3
3
 
4
- Motion::Require.all(Dir.glob(File.expand_path('../project/**/*.rb', __FILE__)))
4
+ unless defined?(Motion::Project::Config)
5
+ raise 'This file must be required within a RubyMotion project Rakefile.'
6
+ end
5
7
 
8
+ Motion::Require.all(Dir.glob(File.expand_path('../project/**/*.rb', __FILE__)))
@@ -1,6 +1,6 @@
1
1
  motion_require './text_field_cell'
2
2
 
3
- class ButtonCell < TextFieldCell
3
+ class ButtonCell < TextInputCell
4
4
  IDENTIFIER = 'ButtonCell'
5
5
 
6
6
  class << self
@@ -6,9 +6,11 @@ class TextFieldCell < BaseCell
6
6
 
7
7
  def initWithStyle(style, reuseIdentifier: reuse_identifier)
8
8
  super.tap do |cell|
9
- cell.observe('ButtonCallbackWillFire', 'resign_textfield:')
10
- cell.observe('FormWillValidate', 'resign_textfield:')
11
- cell.observe('FormWillRender', 'resign_textfield:')
9
+ cell.observe('ButtonCallbackWillFire', 'resign_text_view:')
10
+ cell.observe('FormWillValidate', 'resign_text_view:')
11
+ cell.observe('FormWillRender', 'resign_text_view:')
12
+
13
+ cell.setup_constraints
12
14
  end
13
15
  end
14
16
 
@@ -18,69 +20,89 @@ class TextFieldCell < BaseCell
18
20
  end
19
21
  end
20
22
 
21
- def resign_textfield(notification)
22
- text_field.resignFirstResponder if text_field.isFirstResponder
23
+ def setup_constraints
24
+ Motion::Layout.new do |layout|
25
+ layout.view contentView
26
+ layout.subviews 'text_view' => text_view
27
+ layout.horizontal '|[text_view]|'
28
+ layout.vertical '|[text_view]|'
29
+ end
30
+ end
31
+
32
+ def resign_text_view(notification)
33
+ text_view.resignFirstResponder if text_view.isFirstResponder
23
34
  end
24
35
 
25
36
  def label=(label)
26
- text_field.placeholder = label
27
37
  end
28
38
 
29
39
  def secure=(secure)
30
- text_field.secureTextEntry = secure
31
40
  end
32
41
 
33
42
  def icon=(icon)
34
43
  left_view.name = icon
35
44
  end
36
45
 
37
- def text_field
38
- @text_field ||= UITextField.alloc.init.tap do |field|
39
- field.autocorrectionType = UITextAutocorrectionTypeNo
40
- field.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
41
- field.backgroundColor = UIColor.clearColor
42
- field.clearButtonMode = UITextFieldViewModeWhileEditing
43
- field.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter
44
- field.leftView = left_view
45
- field.leftViewMode = UITextFieldViewModeAlways
46
- field.textColor = UIColor.grayColor
47
-
48
- field.delegate = self
46
+ def text_view
47
+ @text_view ||= SZTextView.alloc.init.tap do |text_view|
48
+ text_view.font = UIFont.fontWithName('HelveticaNeue-Light', size: 14.0)
49
+ text_view.placeholder = 'Write a short bio'
50
+ text_view.delegate = self
49
51
  end
50
52
  end
53
+ alias_method :text_field, :text_view
51
54
 
52
55
  def left_view
53
56
  @left_view ||= IconView.alloc.init
54
57
  end
55
58
 
56
59
  def value
57
- text_field.text
60
+ text_view.text
58
61
  end
59
62
 
60
63
  def value=(value)
61
- text_field.text = value
64
+ text_view.text = value
62
65
  end
63
66
 
64
- def layoutSubviews
65
- text_field.frame = [[10, 0], [300, 43]]
66
- left_view.frame = [[0, 0], [36, 43]]
67
- end
68
-
69
- def textFieldDidBeginEditing(text_field)
67
+ def textViewDidBeginEditing(text_view)
70
68
  post('FormCellDidBeginEditing', notification_payload)
71
69
  end
72
70
 
73
- def textFieldDidEndEditing(text_field)
71
+ def textViewDidEndEditing(text_view)
74
72
  post('FormCellDidEndEditing', notification_payload)
75
73
  end
76
74
 
77
- def textFieldShouldReturn(text_field)
78
- text_field.resignFirstResponder
75
+ def textFieldShouldReturn(text_view)
76
+ text_view.resignFirstResponder
79
77
 
80
78
  true
81
79
  end
82
80
 
81
+ def textView(text_view, shouldChangeTextInRange: range, replacementText: text)
82
+ if text == "\n"
83
+ text_view.resignFirstResponder
84
+
85
+ false
86
+ else
87
+ true
88
+ end
89
+ end
90
+
91
+ def textViewDidChange(text_view)
92
+ line = text_view.caretRectForPosition(text_view.selectedTextRange.start)
93
+ overflow = line.origin.y + line.size.height - (text_view.contentOffset.y + text_view.bounds.size.height - text_view.contentInset.bottom - text_view.contentInset.top )
94
+
95
+ if overflow > 0
96
+ offset = text_view.contentOffset
97
+ offset.y += overflow + 7
98
+
99
+ UIView.animateWithDuration(0.2, animations: -> {
100
+ text_view.setContentOffset(offset)
101
+ })
102
+ end
103
+ end
104
+
83
105
  def notification_payload
84
- { key: key, value: value, text_field: text_field }
106
+ { key: key, value: value, text_field: text_view }
85
107
  end
86
108
  end
@@ -0,0 +1,86 @@
1
+ motion_require './base_cell'
2
+ motion_require '../views/icon_view'
3
+
4
+ class TextInputCell < BaseCell
5
+ IDENTIFIER = 'TextInputCell'
6
+
7
+ def initWithStyle(style, reuseIdentifier: reuse_identifier)
8
+ super.tap do |cell|
9
+ cell.observe('ButtonCallbackWillFire', 'resign_textfield:')
10
+ cell.observe('FormWillValidate', 'resign_textfield:')
11
+ cell.observe('FormWillRender', 'resign_textfield:')
12
+ end
13
+ end
14
+
15
+ class << self
16
+ def has_value?
17
+ true
18
+ end
19
+ end
20
+
21
+ def resign_textfield(notification)
22
+ text_field.resignFirstResponder if text_field.isFirstResponder
23
+ end
24
+
25
+ def label=(label)
26
+ text_field.placeholder = label
27
+ end
28
+
29
+ def secure=(secure)
30
+ text_field.secureTextEntry = secure
31
+ end
32
+
33
+ def icon=(icon)
34
+ left_view.name = icon
35
+ end
36
+
37
+ def text_field
38
+ @text_field ||= UITextField.alloc.init.tap do |field|
39
+ field.autocorrectionType = UITextAutocorrectionTypeNo
40
+ field.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
41
+ field.backgroundColor = UIColor.clearColor
42
+ field.clearButtonMode = UITextFieldViewModeWhileEditing
43
+ field.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter
44
+ field.leftView = left_view
45
+ field.leftViewMode = UITextFieldViewModeAlways
46
+ field.textColor = UIColor.grayColor
47
+
48
+ field.delegate = self
49
+ end
50
+ end
51
+
52
+ def left_view
53
+ @left_view ||= IconView.alloc.init
54
+ end
55
+
56
+ def value
57
+ text_field.text
58
+ end
59
+
60
+ def value=(value)
61
+ text_field.text = value
62
+ end
63
+
64
+ def layoutSubviews
65
+ text_field.frame = [[10, 0], [300, 43]]
66
+ left_view.frame = [[0, 0], [36, 43]]
67
+ end
68
+
69
+ def textFieldDidBeginEditing(text_field)
70
+ post('FormCellDidBeginEditing', notification_payload)
71
+ end
72
+
73
+ def textFieldDidEndEditing(text_field)
74
+ post('FormCellDidEndEditing', notification_payload)
75
+ end
76
+
77
+ def textFieldShouldReturn(text_field)
78
+ text_field.resignFirstResponder
79
+
80
+ true
81
+ end
82
+
83
+ def notification_payload
84
+ { key: key, value: value, text_field: text_field }
85
+ end
86
+ end
@@ -0,0 +1,22 @@
1
+ module MotionForm
2
+ module FontIconMapper
3
+ class << self
4
+ def call(icon)
5
+ unicode(icon).hex.chr(Encoding::UTF_8)
6
+ end
7
+
8
+ def unicode(icon)
9
+ mappings[icon] || ''
10
+ end
11
+
12
+ def mappings
13
+ @mappings ||= begin
14
+ path = NSBundle.mainBundle.pathForResource('icons', ofType:'json')
15
+ data = NSData.dataWithContentsOfFile(path)
16
+
17
+ NSJSONSerialization.JSONObjectWithData(data, options: 0, error: nil)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -13,11 +13,24 @@ module MotionForm
13
13
  f.dataSource = self
14
14
  f.delegate = self
15
15
 
16
+ f.addGestureRecognizer(tap_recognizer)
17
+
16
18
  @keyboard_avoiding_delegate = Motion::KeyboardAvoiding.new(f)
19
+
17
20
  listen
18
21
  end
19
22
  end
20
23
 
24
+ def tap_recognizer
25
+ @tap_recognizer ||= UITapGestureRecognizer.alloc.init.tap do |recognizer|
26
+ recognizer.addTarget(self, action: 'tapped:')
27
+ end
28
+ end
29
+
30
+ def tapped(recognizer)
31
+ endEditing(true)
32
+ end
33
+
21
34
  def listen
22
35
  observers << notification_center.addObserver(self, selector: 'did_begin_editing:', name: 'FormCellDidBeginEditing', object: nil)
23
36
  observers << notification_center.addObserver(self, selector: 'did_end_editing:', name: 'FormCellDidEndEditing', object: nil)
@@ -78,6 +91,13 @@ module MotionForm
78
91
  row = section.rows[index_path.row]
79
92
  end
80
93
 
94
+ def tableView(table_view, heightForRowAtIndexPath: index_path)
95
+ section = sections[index_path.section]
96
+ row = section.rows[index_path.row]
97
+
98
+ row.respondsToSelector('height') ? row.height : 44
99
+ end
100
+
81
101
  def tableView(table_view, viewForHeaderInSection: section)
82
102
  unless sections[section].title.blank?
83
103
  SectionHeaderView.alloc.initWithFrame([[0, 0], [size.width, 44.0]]).tap do |header|
@@ -105,7 +125,7 @@ module MotionForm
105
125
  def valid?
106
126
  notification_center.postNotificationName('FormWillValidate', object: self, userInfo: nil)
107
127
 
108
- rows.select { |row| row.is_a? TextFieldRow }.all? { |row| row.valid? }
128
+ rows.select { |row| row.is_a? TextInputRow }.all? { |row| row.valid? }
109
129
  end
110
130
 
111
131
  def tableView(table_view, cellForRowAtIndexPath: index_path)
@@ -42,7 +42,7 @@ module MotionForm
42
42
  end
43
43
 
44
44
  def included_cells
45
- [TextFieldCell, ButtonCell]
45
+ [TextInputCell, TextFieldCell, ButtonCell]
46
46
  end
47
47
 
48
48
  def section_header_font
@@ -54,7 +54,7 @@ module MotionForm
54
54
  end
55
55
 
56
56
  def icon_mapper
57
- @icon_mapper || -> (icon_name) { icon_name }
57
+ @icon_mapper || FontIconMapper
58
58
  end
59
59
 
60
60
  def section_header_color
@@ -1,7 +1,7 @@
1
1
  motion_require '../cells/button_cell'
2
2
  motion_require './text_field_row'
3
3
 
4
- class ButtonRow < TextFieldRow
4
+ class ButtonRow < TextInputRow
5
5
  attr_reader :on_tap_callback, :accessory
6
6
 
7
7
  def initialize(key, options)
@@ -1,70 +1,12 @@
1
1
  motion_require './base_row'
2
2
  motion_require '../cells/text_field_cell'
3
3
 
4
- class TextFieldRow < BaseRow
5
- EMAIL_REGEX = /^[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/
6
- URL_REGEX = /https?:\/\/[\S]+/
7
-
8
- attr_accessor :value
9
-
10
- def initialize(key, options)
11
- super
12
-
13
- setup_validation
14
-
15
- listen
16
- end
17
-
18
- def setup_validation
19
- if options[:required]
20
- validation_rules << lambda { |value| value && value != '' }
21
- end
22
-
23
- if options[:email]
24
- validation_rules << lambda { |value| value && value[EMAIL_REGEX] }
25
- end
26
-
27
- if options[:url]
28
- validation_rules << lambda { |value| value && value[URL_REGEX] }
29
- end
30
-
31
- if options[:format] && options[:format].is_a?(Regexp)
32
- validation_rules << lambda { |value| value && value[options[:format]] }
33
- end
34
-
35
- if options[:validate_with] && options[:validate_with].is_a?(Proc)
36
- validation_rules << options[:validate_with]
37
- end
38
- end
39
-
40
- def valid?
41
- validation_rules.all? { |rule| rule.call(value) }
42
- end
43
-
44
- def validation_rules
45
- @validation_rules ||= []
46
- end
47
-
48
- def listen
49
- observe('FormCellDidEndEditing', 'did_end_editing:')
50
- end
51
-
52
- def did_end_editing(notification)
53
- self.value = notification.userInfo[:value] if notification.userInfo[:key] == key
54
- end
55
-
56
- def textFieldDidEndEditing(text_field)
57
- self.value = text_field.text
58
- end
59
-
60
- def update_cell(cell)
61
- super
62
-
63
- cell.secure = options[:secure]
64
- cell.value = options[:value]
65
- end
66
-
4
+ class TextFieldRow < TextInputRow
67
5
  def cell_type
68
6
  TextFieldCell
69
7
  end
8
+
9
+ def height
10
+ 100
11
+ end
70
12
  end
@@ -0,0 +1,70 @@
1
+ motion_require './base_row'
2
+ motion_require '../cells/text_input_cell'
3
+
4
+ class TextInputRow < BaseRow
5
+ EMAIL_REGEX = /^[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/
6
+ URL_REGEX = /https?:\/\/[\S]+/
7
+
8
+ attr_accessor :value
9
+
10
+ def initialize(key, options)
11
+ super
12
+
13
+ setup_validation
14
+
15
+ listen
16
+ end
17
+
18
+ def setup_validation
19
+ if options[:required]
20
+ validation_rules << lambda { |value| value && value != '' }
21
+ end
22
+
23
+ if options[:email]
24
+ validation_rules << lambda { |value| value && value[EMAIL_REGEX] }
25
+ end
26
+
27
+ if options[:url]
28
+ validation_rules << lambda { |value| value && value[URL_REGEX] }
29
+ end
30
+
31
+ if options[:format] && options[:format].is_a?(Regexp)
32
+ validation_rules << lambda { |value| value && value[options[:format]] }
33
+ end
34
+
35
+ if options[:validate_with] && options[:validate_with].is_a?(Proc)
36
+ validation_rules << options[:validate_with]
37
+ end
38
+ end
39
+
40
+ def valid?
41
+ validation_rules.all? { |rule| rule.call(value) }
42
+ end
43
+
44
+ def validation_rules
45
+ @validation_rules ||= []
46
+ end
47
+
48
+ def listen
49
+ observe('FormCellDidEndEditing', 'did_end_editing:')
50
+ end
51
+
52
+ def did_end_editing(notification)
53
+ self.value = notification.userInfo[:value] if notification.userInfo[:key] == key
54
+ end
55
+
56
+ def textFieldDidEndEditing(text_field)
57
+ self.value = text_field.text
58
+ end
59
+
60
+ def update_cell(cell)
61
+ super
62
+
63
+ cell.secure = options[:secure]
64
+ cell.value = options[:value]
65
+ end
66
+
67
+ def cell_type
68
+ TextInputCell
69
+ end
70
+ end
@@ -10,6 +10,10 @@ class MotionForm
10
10
  end
11
11
 
12
12
  def input(key, options = {})
13
+ rows << TextInputRow.new(key, options)
14
+ end
15
+
16
+ def text(key, options = {})
13
17
  rows << TextFieldRow.new(key, options)
14
18
  end
15
19
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: motion-form
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Devon Blandin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-31 00:00:00.000000000 Z
11
+ date: 2014-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: motion-keyboard-avoiding
@@ -38,6 +38,48 @@ dependencies:
38
38
  - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: 0.0.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: cocoapods
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 0.27.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.27.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: motion-cocoapods
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.3.7
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 1.3.7
69
+ - !ruby/object:Gem::Dependency
70
+ name: motion-layout
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 0.0.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 0.0.1
41
83
  - !ruby/object:Gem::Dependency
42
84
  name: rake
43
85
  requirement: !ruby/object:Gem::Requirement
@@ -64,12 +106,15 @@ files:
64
106
  - lib/project/cells/base_cell.rb
65
107
  - lib/project/cells/button_cell.rb
66
108
  - lib/project/cells/text_field_cell.rb
109
+ - lib/project/cells/text_input_cell.rb
67
110
  - lib/project/controllers/form_controller.rb
111
+ - lib/project/font_icon_mapper.rb
68
112
  - lib/project/form/base.rb
69
113
  - lib/project/motion-form.rb
70
114
  - lib/project/rows/base_row.rb
71
115
  - lib/project/rows/button_row.rb
72
116
  - lib/project/rows/text_field_row.rb
117
+ - lib/project/rows/text_input_row.rb
73
118
  - lib/project/section/section.rb
74
119
  - lib/project/string.rb
75
120
  - lib/project/views/icon_view.rb
@@ -95,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
140
  version: '0'
96
141
  requirements: []
97
142
  rubyforge_project:
98
- rubygems_version: 2.1.1
143
+ rubygems_version: 2.1.3
99
144
  signing_key:
100
145
  specification_version: 4
101
146
  summary: Simple DSL for RubyMotion form creation