rm-extensions 0.1.5 → 0.1.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2ba4ba13ce05f6391b658a6d6968fd368d6eb66e
4
- data.tar.gz: 9140c139f8dc4c75d01183e46019df8ebac3a7c2
3
+ metadata.gz: 5d64ca280c2914d4e0e8ebda0af7b8900c4a71d0
4
+ data.tar.gz: 422760a452010841d394d296a59090c5967b43cd
5
5
  SHA512:
6
- metadata.gz: 4ba45a55bbee4a13e6d0d72326208e4c704377ad24b28c6e04569e857cfeb74e869e9d846e9744367432e34068f8808dd502326f79b9cf242dfcebdb541b7c6c
7
- data.tar.gz: 03222f43b0510a630fd09b33f30bc59815c06ad6d54e41384f76880d0f645bb449ff297ee689d01988235ff66d4a8be31df1385d60b174b6c7222be4c18d7c4e
6
+ metadata.gz: 613afb58ed64e974e51cdfc117e8627ecc381084c2d1cf6e571b2d0df6a95ba071fab76efeda59cf9eb0e6a203a3d9cce1908a5e3cdf05be8b9179e8c947c4dd
7
+ data.tar.gz: 41375da6369ebe8921c5f47d2576ea774cc353b8701774e092ffaf7df6948e6b65b87bf203f10c2a055603e324c93b8ee4689205f76f5597ee267ee38a45f86d
data/Gemfile CHANGED
@@ -1,4 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in rm-extensions.gemspec
3
+ group :test, :development do
4
+ gem 'rake'
5
+ end
6
+
4
7
  gemspec
data/README.md CHANGED
@@ -1,7 +1,204 @@
1
1
  RMExtensions
2
2
  -----------------
3
3
 
4
- #### Extensions and helpers for dealing with various areas of rubymotion.
4
+ #### Extensions and helpers for dealing with various areas of rubymotion and iOS.
5
+
6
+ ## Equation-style AutoLayout Constraints
7
+
8
+ AutoLayout is a great way to lay out views, but writing constraints manually is
9
+ confusing and verbose.
10
+
11
+ Using Apple's "Visual Format Language" ASCII-inspired
12
+ strings can improve things, but it has drawbacks: **1)** It returns an array
13
+ of constraints. This means if you plan on altering one of them later, or removing
14
+ one in particular, you would have to loop through all of the constraints, testing
15
+ each one to see if its the one you want. **2)** The `options` argument adds additional
16
+ constraints. For example, if you specify NSLayoutAttributeCenterY to a horizontal string,
17
+ an additional constraint will be added for each view to set their centerY's equal to each
18
+ other. This compounds problem #1. The chances you will get an error that the layout system
19
+ cannot satisfy your constraints is probably because of these "extra" constraints. **3)**
20
+ It can't handle complex constraints, so you end up needing to supplement it with
21
+ verbose low-level constraint creation anyway.
22
+
23
+ Apple makes note of how constraints can be thought of like a linear equation:
24
+
25
+ http://developer.apple.com/library/ios/documentation/AppKit/Reference/NSLayoutConstraint_Class/NSLayoutConstraint/NSLayoutConstraint.html
26
+
27
+ **Remember the formula:**
28
+
29
+ ```ruby
30
+ view1.attr1 == view2.attr2 * multiplier + constant @ priority
31
+ ```
32
+
33
+ **We can actually use this super-simple formula to write ALL of our constraints, simple OR complex!**
34
+
35
+ Once you get the hang of the formula and visualization of how the geometry works, it becomes easy to create complex layouts with little
36
+ effort. And, in my opinion, its only slightly more verbose than the visual format language, but much clearer, and you only end up
37
+ with the exact constraints you want.
38
+
39
+ Available values for `attr1` and `attr2` are:
40
+
41
+ - left
42
+ - right
43
+ - top
44
+ - bottom
45
+ - leading
46
+ - trailing
47
+ - width
48
+ - height
49
+ - centerX
50
+ - centerY
51
+ - baseline
52
+
53
+ Available `relation` values are:
54
+
55
+ - ==
56
+ - <=
57
+ - >=
58
+
59
+ Available `priority` values are:
60
+
61
+ - required (1000 is the default)
62
+ - high (750)
63
+ - low (250)
64
+ - fit (50)
65
+ - or, you can use your own value between 1-1000
66
+
67
+
68
+ ### Examples
69
+
70
+ Here is a real example. The Layout instance is created just like the motion-layout gem. `layout.view` sets the view
71
+ that will act as the 'superview' to the views set in `layout.subviews`. Thats where the similarities end with
72
+ motion-layout. With RMExtensions::Layout, there are two methods: `eq` and `eqs`, short for equation and equations.
73
+
74
+ - `layout.eq` takes one string, and returns *one constraint*
75
+ - `layout.eqs` takes one string, assumes multiple constraints are separated by newlines, and returns an *array of constraints*
76
+
77
+ ```ruby
78
+ RMExtensions::Layout.new do |layout|
79
+ layout.view view
80
+ layout.subviews({
81
+ "calendar" => calendarView,
82
+ "table" => tableView,
83
+ "shadow" => line
84
+ })
85
+
86
+ layout.eqs %Q{
87
+ calendar.left == 0
88
+ calendar.right == 0
89
+ table.left == 7
90
+ table.right == -7
91
+ shadow.left == 0
92
+ shadow.right == 0
93
+
94
+ calendar.top == 0
95
+ table.top == calendar.bottom
96
+ table.bottom == 0
97
+ shadow.top == table.top
98
+ }
99
+
100
+ @calendar_height_constraint = layout.eq "calendar.height == 0"
101
+ end
102
+ ```
103
+
104
+ Above, **calendar.left == 0** is short for **calendar.left == view.left * 1.0 + 0 @ 1000**. If no view2 is given, the superview ('view') is assumed.
105
+ If no multiplier is given, 1.0 is assumed. If no constant is given, 0 is assumed. If no priority is given, "required" (1000) is assumed.
106
+ The last constraint is created separately and stored in @calendar_height_constraint, because I want to be able to change the calendar's height
107
+ any time I want.
108
+
109
+ Here is another example:
110
+
111
+ ```ruby
112
+ RMExtensions::Layout.new do |layout|
113
+ layout.view self
114
+ layout.subviews({
115
+ "timeLabel" => @timeLabel,
116
+ "titleLabel" => @titleLabel,
117
+ "trackingImage" => @trackingImage,
118
+ "inOutStatusInImage" => @inOutStatusInImage,
119
+ "inOutStatusOutImage" => @inOutStatusOutImage,
120
+ "plannerImage" => @plannerImage,
121
+ "shareButton" => @shareButton,
122
+ "cancelledLabel" => @cancelledLabel,
123
+ "unreadImage" => @unreadImage
124
+ })
125
+
126
+ layout.eqs %Q{
127
+ unreadImage.left == 6
128
+ unreadImage.top == 6
129
+ plannerImage.left == 14
130
+ plannerImage.centerY == 0
131
+ plannerImage.width == 30
132
+ plannerImage.height == 30
133
+ trackingImage.left == timeLabel.right + 5
134
+ inOutStatusOutImage.left == trackingImage.right + 5
135
+ inOutStatusInImage.left == inOutStatusOutImage.right + 5
136
+ timeLabel.left == plannerImage.right + 5
137
+ timeLabel.baseline == plannerImage.bottom + 1
138
+ trackingImage.centerY == timeLabel.centerY
139
+ inOutStatusOutImage.centerY == timeLabel.centerY
140
+ inOutStatusInImage.centerY == timeLabel.centerY
141
+ titleLabel.left == cancelledLabel.right
142
+ cancelledLabel.left == plannerImage.right + 5
143
+ titleLabel.top == plannerImage.top - 4
144
+ cancelledLabel.centerY == titleLabel.centerY
145
+ shareButton.right == -10
146
+ shareButton.centerY == 0
147
+ titleLabel.resistH == low
148
+ shareButton.left >= titleLabel.right + 5
149
+ timeLabel.resistH == low
150
+ shareButton.left >= inOutStatusInImage.right + 5
151
+ }
152
+
153
+ end
154
+ ```
155
+
156
+ Keep in mind none of these lines are using the multiplier, and thats OK. I've only needed it on one constraint in my entire app so far,
157
+ so don't think its odd if you can't find a use for it.
158
+
159
+ There are two special cases at the moment. **titleLabel.resistH == low** is not a "real" constraint. Its a shortcut to
160
+ `setContentCompressionResistancePriority`, and since its common when dealing with autolayout, its nice to include it
161
+ in our layout code. The same is done for `setContentHuggingPriority`. The full list of "special cases" at the moment:
162
+
163
+ - view1.resistH == priority
164
+ - view1.resistV == priority
165
+ - view1.hugH == priority
166
+ - view1.hugV == priority
167
+
168
+ "priority" can be one of the values listed earlier, or your own number between 1-1000.
169
+
170
+ ### Debugging constraints
171
+
172
+ - You can include a **?** on any line, and debug output will be printed when that constraint is built:
173
+
174
+ ```ruby
175
+ layout.eqs %Q{
176
+ label.left == photo.right + 5 ?
177
+ }
178
+ ```
179
+
180
+ - Since layout.eqs allows you to write many constraints in one string, and sometimes its nice to
181
+ keep comments next to constraints, **comments are allowed**:
182
+
183
+ ```ruby
184
+ layout.eqs %Q{
185
+ commentsCount.width == likesCount.width @ low # the widths of the labels prefer to be the same
186
+ }
187
+ ```
188
+
189
+ ### Things to remember
190
+
191
+ - Remember you usually want negative constants for right and bottom. For example: label.right == -10 means "label's right should be 10 away from the right side of the superview". But if you accidentally said label.right == 10, you would have created "label's right should be 10 PAST the right side of the superview". It may require you to adjust your thinking.
192
+
193
+ - The formula is just shorthand for `constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:`. You should **really** read up on constraints and
194
+ understand this method, to fully understand the power and simplicity the shorthand formula gives you.
195
+
196
+
197
+
198
+
199
+
200
+
201
+
5
202
 
6
203
  ## Observation/KVO, Events
7
204
 
data/Rakefile CHANGED
@@ -1 +1,16 @@
1
1
  require "bundler/gem_tasks"
2
+ $:.unshift('/Library/RubyMotion/lib')
3
+ if ENV.fetch('platform', 'ios') == 'ios'
4
+ require 'motion/project/template/ios'
5
+ elsif ENV['platform'] == 'osx'
6
+ require 'motion/project/template/osx'
7
+ else
8
+ raise "Unsupported platform #{ENV['platform']}"
9
+ end
10
+ require 'bundler'
11
+ Bundler.require
12
+
13
+ Motion::Project::App.setup do |app|
14
+ # Use `rake config' to see complete project settings.
15
+ app.name = 'rm-extensions'
16
+ end
@@ -0,0 +1,5 @@
1
+ class AppDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ true
4
+ end
5
+ end
@@ -1,3 +1,3 @@
1
1
  module RMExtensions
2
- VERSION = "0.1.5"
2
+ VERSION = "0.1.6"
3
3
  end
Binary file
data/spec/main_spec.rb ADDED
@@ -0,0 +1,240 @@
1
+ describe "Equation-style AutoLayout Constraints" do
2
+
3
+ def self.test_constraint_equation(c1_str, optional_desc=nil, &block)
4
+ it "#{optional_desc || c1_str}" do
5
+ c1 = @layout.eq c1_str
6
+ c2 = block.call
7
+ c1.priority.should == c2.priority
8
+ c1.firstItem.should == c2.firstItem
9
+ c1.firstAttribute.should == c2.firstAttribute
10
+ c1.relation.should == c2.relation
11
+ c1.secondItem.should == c2.secondItem
12
+ c1.secondAttribute.should == c2.secondAttribute
13
+ c1.multiplier.should == c2.multiplier
14
+ c1.constant.should == c2.constant
15
+ end
16
+ end
17
+
18
+ before do
19
+ @view = UIView.new
20
+ @label1 = UILabel.new
21
+ @label2 = UILabel.new
22
+ @layout = RMExtensions::Layout.new
23
+ @layout.view @view
24
+ @layout.subviews({
25
+ "label1" => @label1,
26
+ "label2" => @label2
27
+ })
28
+ end
29
+
30
+ test_constraint_equation("label1.left == 0") do
31
+ NSLayoutConstraint.constraintWithItem(@label1,
32
+ attribute:NSLayoutAttributeLeft,
33
+ relatedBy:NSLayoutRelationEqual,
34
+ toItem:@view,
35
+ attribute:NSLayoutAttributeLeft,
36
+ multiplier:1.0,
37
+ constant:0)
38
+ end
39
+
40
+ test_constraint_equation("label1.left == 8") do
41
+ NSLayoutConstraint.constraintWithItem(@label1,
42
+ attribute:NSLayoutAttributeLeft,
43
+ relatedBy:NSLayoutRelationEqual,
44
+ toItem:@view,
45
+ attribute:NSLayoutAttributeLeft,
46
+ multiplier:1.0,
47
+ constant:8)
48
+ end
49
+
50
+ test_constraint_equation("label1.right == -20") do
51
+ NSLayoutConstraint.constraintWithItem(@label1,
52
+ attribute:NSLayoutAttributeRight,
53
+ relatedBy:NSLayoutRelationEqual,
54
+ toItem:@view,
55
+ attribute:NSLayoutAttributeRight,
56
+ multiplier:1.0,
57
+ constant:-20)
58
+ end
59
+
60
+ test_constraint_equation("label1.top == 0") do
61
+ NSLayoutConstraint.constraintWithItem(@label1,
62
+ attribute:NSLayoutAttributeTop,
63
+ relatedBy:NSLayoutRelationEqual,
64
+ toItem:@view,
65
+ attribute:NSLayoutAttributeTop,
66
+ multiplier:1.0,
67
+ constant:0)
68
+ end
69
+
70
+ test_constraint_equation("label1.bottom == -36") do
71
+ NSLayoutConstraint.constraintWithItem(@label1,
72
+ attribute:NSLayoutAttributeBottom,
73
+ relatedBy:NSLayoutRelationEqual,
74
+ toItem:@view,
75
+ attribute:NSLayoutAttributeBottom,
76
+ multiplier:1.0,
77
+ constant:-36)
78
+ end
79
+
80
+ test_constraint_equation("label1.height == 1") do
81
+ NSLayoutConstraint.constraintWithItem(@label1,
82
+ attribute:NSLayoutAttributeHeight,
83
+ relatedBy:NSLayoutRelationEqual,
84
+ toItem:nil,
85
+ attribute:NSLayoutAttributeNotAnAttribute,
86
+ multiplier:1.0,
87
+ constant:1)
88
+ end
89
+
90
+ test_constraint_equation("label1.centerX == 0") do
91
+ NSLayoutConstraint.constraintWithItem(@label1,
92
+ attribute:NSLayoutAttributeCenterX,
93
+ relatedBy:NSLayoutRelationEqual,
94
+ toItem:@view,
95
+ attribute:NSLayoutAttributeCenterX,
96
+ multiplier:1.0,
97
+ constant:0)
98
+ end
99
+
100
+ test_constraint_equation("label1.width == 1.5") do
101
+ NSLayoutConstraint.constraintWithItem(@label1,
102
+ attribute:NSLayoutAttributeWidth,
103
+ relatedBy:NSLayoutRelationEqual,
104
+ toItem:nil,
105
+ attribute:NSLayoutAttributeNotAnAttribute,
106
+ multiplier:1.0,
107
+ constant:1.5)
108
+ end
109
+
110
+ test_constraint_equation("label1.left >= 6") do
111
+ NSLayoutConstraint.constraintWithItem(@label1,
112
+ attribute:NSLayoutAttributeLeft,
113
+ relatedBy:NSLayoutRelationGreaterThanOrEqual,
114
+ toItem:@view,
115
+ attribute:NSLayoutAttributeLeft,
116
+ multiplier:1.0,
117
+ constant:6)
118
+ end
119
+
120
+ test_constraint_equation("label1.width == 0 @ low # prefer box hugs content") do
121
+ NSLayoutConstraint.constraintWithItem(@label1,
122
+ attribute:NSLayoutAttributeWidth,
123
+ relatedBy:NSLayoutRelationEqual,
124
+ toItem:nil,
125
+ attribute:NSLayoutAttributeNotAnAttribute,
126
+ multiplier:1.0,
127
+ constant:0).tap { |x| x.priority = UILayoutPriorityDefaultLow }
128
+ end
129
+
130
+ test_constraint_equation("label1.left == 24 @ low # this button prefers to hug the left") do
131
+ NSLayoutConstraint.constraintWithItem(@label1,
132
+ attribute:NSLayoutAttributeLeft,
133
+ relatedBy:NSLayoutRelationEqual,
134
+ toItem:@view,
135
+ attribute:NSLayoutAttributeLeft,
136
+ multiplier:1.0,
137
+ constant:24).tap { |x| x.priority = UILayoutPriorityDefaultLow }
138
+ end
139
+
140
+ test_constraint_equation("label1.top == view.top + 8") do
141
+ NSLayoutConstraint.constraintWithItem(@label1,
142
+ attribute:NSLayoutAttributeTop,
143
+ relatedBy:NSLayoutRelationEqual,
144
+ toItem:@view,
145
+ attribute:NSLayoutAttributeTop,
146
+ multiplier:1.0,
147
+ constant:8)
148
+ end
149
+
150
+ test_constraint_equation("label1.left == label2.left * 0.5 - 5") do
151
+ NSLayoutConstraint.constraintWithItem(@label1,
152
+ attribute:NSLayoutAttributeLeft,
153
+ relatedBy:NSLayoutRelationEqual,
154
+ toItem:@label2,
155
+ attribute:NSLayoutAttributeLeft,
156
+ multiplier:0.5,
157
+ constant:-5)
158
+ end
159
+
160
+ test_constraint_equation("label1.top == label2.top + 5") do
161
+ NSLayoutConstraint.constraintWithItem(@label1,
162
+ attribute:NSLayoutAttributeTop,
163
+ relatedBy:NSLayoutRelationEqual,
164
+ toItem:@label2,
165
+ attribute:NSLayoutAttributeTop,
166
+ multiplier:1.0,
167
+ constant:5)
168
+ end
169
+
170
+ test_constraint_equation("label1.top == label2.bottom") do
171
+ NSLayoutConstraint.constraintWithItem(@label1,
172
+ attribute:NSLayoutAttributeTop,
173
+ relatedBy:NSLayoutRelationEqual,
174
+ toItem:@label2,
175
+ attribute:NSLayoutAttributeBottom,
176
+ multiplier:1.0,
177
+ constant:0)
178
+ end
179
+
180
+ test_constraint_equation("label1.width == label2.width @ low # the widths of the labels prefer to be the same") do
181
+ NSLayoutConstraint.constraintWithItem(@label1,
182
+ attribute:NSLayoutAttributeWidth,
183
+ relatedBy:NSLayoutRelationEqual,
184
+ toItem:@label2,
185
+ attribute:NSLayoutAttributeWidth,
186
+ multiplier:1.0,
187
+ constant:0).tap { |x| x.priority = UILayoutPriorityDefaultLow }
188
+ end
189
+
190
+ test_constraint_equation("label1.left == label2.right + 20") do
191
+ NSLayoutConstraint.constraintWithItem(@label1,
192
+ attribute:NSLayoutAttributeLeft,
193
+ relatedBy:NSLayoutRelationEqual,
194
+ toItem:@label2,
195
+ attribute:NSLayoutAttributeRight,
196
+ multiplier:1.0,
197
+ constant:20)
198
+ end
199
+
200
+ test_constraint_equation("label1.centerY == label2.centerY") do
201
+ NSLayoutConstraint.constraintWithItem(@label1,
202
+ attribute:NSLayoutAttributeCenterY,
203
+ relatedBy:NSLayoutRelationEqual,
204
+ toItem:@label2,
205
+ attribute:NSLayoutAttributeCenterY,
206
+ multiplier:1.0,
207
+ constant:0)
208
+ end
209
+
210
+ test_constraint_equation("label1.left >= label2.right + 6") do
211
+ NSLayoutConstraint.constraintWithItem(@label1,
212
+ attribute:NSLayoutAttributeLeft,
213
+ relatedBy:NSLayoutRelationGreaterThanOrEqual,
214
+ toItem:@label2,
215
+ attribute:NSLayoutAttributeRight,
216
+ multiplier:1.0,
217
+ constant:6)
218
+ end
219
+
220
+ test_constraint_equation("label1.right <= view.right - 8") do
221
+ NSLayoutConstraint.constraintWithItem(@label1,
222
+ attribute:NSLayoutAttributeRight,
223
+ relatedBy:NSLayoutRelationLessThanOrEqual,
224
+ toItem:@view,
225
+ attribute:NSLayoutAttributeRight,
226
+ multiplier:1.0,
227
+ constant:-8)
228
+ end
229
+
230
+ test_constraint_equation("label1.right <= label2.left - 10") do
231
+ NSLayoutConstraint.constraintWithItem(@label1,
232
+ attribute:NSLayoutAttributeRight,
233
+ relatedBy:NSLayoutRelationLessThanOrEqual,
234
+ toItem:@label2,
235
+ attribute:NSLayoutAttributeLeft,
236
+ multiplier:1.0,
237
+ constant:-10)
238
+ end
239
+
240
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rm-extensions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Noon
@@ -22,6 +22,7 @@ files:
22
22
  - LICENSE.txt
23
23
  - README.md
24
24
  - Rakefile
25
+ - app/app_delegate.rb
25
26
  - lib/motion/accessors.rb
26
27
  - lib/motion/layout.rb
27
28
  - lib/motion/observation.rb
@@ -29,7 +30,9 @@ files:
29
30
  - lib/motion/util.rb
30
31
  - lib/rm-extensions.rb
31
32
  - lib/rm-extensions/version.rb
33
+ - resources/Default-568h@2x.png
32
34
  - rm-extensions.gemspec
35
+ - spec/main_spec.rb
33
36
  homepage: https://github.com/joenoon/rm-extensions
34
37
  licenses: []
35
38
  metadata: {}
@@ -53,4 +56,5 @@ rubygems_version: 2.0.3
53
56
  signing_key:
54
57
  specification_version: 4
55
58
  summary: Extensions and helpers for dealing with various areas of rubymotion
56
- test_files: []
59
+ test_files:
60
+ - spec/main_spec.rb