rm-extensions 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 +4 -4
- data/README.md +3 -282
- data/lib/motion/observation.rb +135 -66
- data/lib/rm-extensions/version.rb +1 -1
- metadata +2 -3
- data/CHANGELOG.md +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dddb35f12d5cf8cc9de4cb3b39df6644497bf550
|
4
|
+
data.tar.gz: 93e202d15df61498e5432194d8e2466d37ece16d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8494d5c4ee43c3bca403556e063b66cefc671f7b6cb56a2e5b70332a3332007f285377ae3d6beafb348d031623af22eaeda30ef38bc5955d8de0517ef3245a4
|
7
|
+
data.tar.gz: ddbc5a99d7939b0c9c2ffca586d852fb783f2216a388d638dfe20c4ea1aa656cf75dfb07ed6dba3b1fa3e33eea7b88ed1b3f128fb97f3e3b75139baa8904dabc
|
data/README.md
CHANGED
@@ -3,285 +3,9 @@ RMExtensions
|
|
3
3
|
|
4
4
|
#### Extensions and helpers for dealing with various areas of rubymotion and iOS.
|
5
5
|
|
6
|
-
|
6
|
+
#### [Documentation](https://github.com/joenoon/rm-extensions/wiki/RMExtensions-Documentation)
|
7
7
|
|
8
|
-
|
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
|
-
|
202
|
-
|
203
|
-
## Observation/KVO, Events
|
204
|
-
|
205
|
-
#### Make observations without needing to clean up/unobserve
|
206
|
-
|
207
|
-
Call from anywhere on anything:
|
208
|
-
|
209
|
-
```ruby
|
210
|
-
class MyViewController < UIViewController
|
211
|
-
def viewDidLoad
|
212
|
-
super.tap do
|
213
|
-
rmext_observe(@model, "name") do |val|
|
214
|
-
p "name is #{val}"
|
215
|
-
end
|
216
|
-
foo.rmext_on(:some_event) do |val|
|
217
|
-
p "some_event called with #{val.inspect}"
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|
221
|
-
def test_trigger
|
222
|
-
foo.rmext_trigger(:some_event, "hello!")
|
223
|
-
end
|
224
|
-
end
|
225
|
-
```
|
226
|
-
|
227
|
-
Differences from BW::KVO and BW::Reactor::Eventable:
|
228
|
-
|
229
|
-
- No need to include a module in the class you wish to use it on
|
230
|
-
- the observation happens on a proxy object
|
231
|
-
- KVO: The default is to observe and immediately fire the supplied callback
|
232
|
-
- KVO: The callback only takes one argument, the new value
|
233
|
-
- KVO: the object observing is not retained, and when it is deallocated, the observation
|
234
|
-
will be removed automatically for you. there is typically no need to clean up manually
|
235
|
-
|
236
|
-
|
237
|
-
## Accessors
|
238
|
-
|
239
|
-
#### weak attr_accessors:
|
240
|
-
|
241
|
-
```ruby
|
242
|
-
|
243
|
-
class MyView < UIView
|
244
|
-
rmext_weak_attr_accessor :delegate
|
245
|
-
end
|
246
|
-
|
247
|
-
class MyViewController < UIViewController
|
248
|
-
def viewDidLoad
|
249
|
-
super.tap do
|
250
|
-
v = MyView.alloc.initWithFrame(CGRectZero)
|
251
|
-
view.addSubview(v)
|
252
|
-
v.delegate = self
|
253
|
-
end
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
```
|
258
|
-
|
259
|
-
## Queues
|
260
|
-
|
261
|
-
#### Wraps GCD:
|
262
|
-
|
263
|
-
```ruby
|
264
|
-
# note +i+ will appear in order, and the thread will never change (main)
|
265
|
-
100.times do |i|
|
266
|
-
rmext_on_main_q do
|
267
|
-
p "i: #{i} thread: #{NSThread.currentThread}"
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
# note +i+ will appear in order, and the thread will change
|
272
|
-
100.times do |i|
|
273
|
-
rmext_on_serial_q("testing") do
|
274
|
-
p "i: #{i} thread: #{NSThread.currentThread}"
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
# note +i+ will sometimes appear out of order, and the thread will change
|
279
|
-
100.times do |i|
|
280
|
-
rmext_on_concurrent_q("testing") do
|
281
|
-
p "i: #{i} thread: #{NSThread.currentThread}"
|
282
|
-
end
|
283
|
-
end
|
284
|
-
```
|
8
|
+
#### [CHANGELOG](https://github.com/joenoon/rm-extensions/wiki/CHANGELOG)
|
285
9
|
|
286
10
|
Installation
|
287
11
|
-----------------
|
@@ -294,8 +18,6 @@ And then execute:
|
|
294
18
|
|
295
19
|
$ bundle
|
296
20
|
|
297
|
-
* Currently depends on bubblewrap (for BW::KVO).
|
298
|
-
|
299
21
|
Contributing
|
300
22
|
-----------------
|
301
23
|
|
@@ -312,8 +34,7 @@ License
|
|
312
34
|
|
313
35
|
Please see [LICENSE](https://github.com/joenoon/rm-extensions/blob/master/LICENSE.txt) for licensing details.
|
314
36
|
|
315
|
-
|
316
37
|
Author
|
317
38
|
-----------------
|
318
39
|
|
319
|
-
Joe Noon
|
40
|
+
[Joe Noon](https://github.com/joenoon)
|
data/lib/motion/observation.rb
CHANGED
@@ -8,26 +8,22 @@ module RMExtensions
|
|
8
8
|
@rmext_observation_proxy ||= ObservationProxy.new(self)
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
#
|
14
|
-
# rmext_observe_passive(@model, "name") do |val|
|
15
|
-
# p "name is #{val}"
|
16
|
-
# end
|
17
|
-
def rmext_observe_passive(object, key, &block)
|
18
|
-
rmext_observation_proxy.observe(object, key, &block)
|
11
|
+
def rmext_observe_passive(object, keyPath:key, withBlock:block)
|
12
|
+
rmext_observe(object, keyPath:key, options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld), withBlock:block)
|
19
13
|
end
|
20
14
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
15
|
+
def rmext_observe(object, keyPath:key, withBlock:block)
|
16
|
+
rmext_observe(object, keyPath:key, options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionInitial), withBlock:block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def rmext_observe(object, keyPath:key, options:options, withBlock:block)
|
20
|
+
rmext_observation_proxy.observe(object, keyPath:key, options:options, withBlock:block)
|
25
21
|
end
|
26
22
|
|
27
23
|
# unobserve an existing observation
|
28
|
-
def rmext_unobserve(object, key)
|
24
|
+
def rmext_unobserve(object, keyPath:key)
|
29
25
|
if @rmext_observation_proxy
|
30
|
-
@rmext_observation_proxy.unobserve(object, key)
|
26
|
+
@rmext_observation_proxy.unobserve(object, keyPath:key)
|
31
27
|
end
|
32
28
|
end
|
33
29
|
|
@@ -39,14 +35,14 @@ module RMExtensions
|
|
39
35
|
end
|
40
36
|
|
41
37
|
# register a callback when an event is trigger on this object
|
42
|
-
def rmext_on(event,
|
43
|
-
rmext_observation_proxy.on(event,
|
38
|
+
def rmext_on(event, inContext:context, withBlock:block)
|
39
|
+
rmext_observation_proxy.on(event, inContext:context, withBlock:block)
|
44
40
|
end
|
45
41
|
|
46
42
|
# remove a specific callback for an event on this object
|
47
|
-
def rmext_off(event,
|
43
|
+
def rmext_off(event, inContext:context, withBlock:block)
|
48
44
|
if @rmext_observation_proxy
|
49
|
-
@rmext_observation_proxy.off(event,
|
45
|
+
@rmext_observation_proxy.off(event, inContext:context, withBlock:block)
|
50
46
|
end
|
51
47
|
end
|
52
48
|
|
@@ -57,10 +53,10 @@ module RMExtensions
|
|
57
53
|
end
|
58
54
|
end
|
59
55
|
|
60
|
-
# trigger an event with
|
61
|
-
def rmext_trigger(event,
|
56
|
+
# trigger an event with value on this object
|
57
|
+
def rmext_trigger(event, value=nil)
|
62
58
|
if @rmext_observation_proxy
|
63
|
-
@rmext_observation_proxy.trigger(event,
|
59
|
+
@rmext_observation_proxy.trigger(event, value)
|
64
60
|
end
|
65
61
|
end
|
66
62
|
|
@@ -75,19 +71,26 @@ module RMExtensions
|
|
75
71
|
|
76
72
|
end
|
77
73
|
|
74
|
+
class ObservationResponse
|
75
|
+
attr_accessor :context, :value, :old_value, :target, :key, :indexes, :kind
|
76
|
+
end
|
77
|
+
|
78
|
+
class EventResponse
|
79
|
+
attr_accessor :context, :value, :target, :event
|
80
|
+
end
|
81
|
+
|
78
82
|
# # Proxy class used to hold the actual observation and watches for the real
|
79
83
|
# # class intended to hold the observation to be deallocated, so the
|
80
84
|
# # observation can be cleaned up.
|
81
85
|
class ObservationProxy
|
82
|
-
COLLECTION_OPERATIONS = [ NSKeyValueChangeInsertion, NSKeyValueChangeRemoval, NSKeyValueChangeReplacement ]
|
83
|
-
DEFAULT_OPTIONS = NSKeyValueObservingOptionNew
|
84
86
|
|
85
87
|
def initialize(obj)
|
88
|
+
@weak_object = WeakRef.new(obj)
|
86
89
|
@desc = obj.inspect
|
87
|
-
@events =
|
90
|
+
@events = NSMapTable.weakToStrongObjectsMapTable
|
88
91
|
@targets = {}
|
89
92
|
if ::RMExtensions.debug?
|
90
|
-
p "created ObservationProxy
|
93
|
+
p "created ObservationProxy(#{@desc})"
|
91
94
|
end
|
92
95
|
end
|
93
96
|
|
@@ -95,26 +98,27 @@ module RMExtensions
|
|
95
98
|
@did_dealloc = true
|
96
99
|
cleanup
|
97
100
|
if ::RMExtensions.debug?
|
98
|
-
p "dealloc ObservationProxy
|
101
|
+
p "dealloc ObservationProxy(#{@desc})"
|
99
102
|
end
|
100
103
|
super
|
101
104
|
end
|
102
105
|
|
103
106
|
def cleanup
|
104
107
|
if ::RMExtensions.debug?
|
105
|
-
p "cleanup #{@desc}"
|
108
|
+
p "cleanup ObservationProxy(#{@desc})"
|
106
109
|
end
|
107
110
|
unobserve_all
|
108
111
|
off_all
|
109
112
|
true
|
110
113
|
end
|
111
114
|
|
112
|
-
def observe(target, key_path,
|
113
|
-
|
114
|
-
add_observer_block(target, key_path,
|
115
|
+
def observe(target, keyPath:key_path, options:options, withBlock:block)
|
116
|
+
already_registered = registered?(target, key_path)
|
117
|
+
add_observer_block(target, key_path, block)
|
118
|
+
target.addObserver(self, forKeyPath:key_path, options:options, context:nil) unless already_registered
|
115
119
|
end
|
116
120
|
|
117
|
-
def unobserve(target, key_path)
|
121
|
+
def unobserve(target, keyPath:key_path)
|
118
122
|
return unless registered?(target, key_path)
|
119
123
|
target.removeObserver(self, forKeyPath:key_path)
|
120
124
|
remove_observer_block(target, key_path)
|
@@ -122,22 +126,20 @@ module RMExtensions
|
|
122
126
|
|
123
127
|
def remove_observer_block(target, key_path)
|
124
128
|
return if target.nil? || key_path.nil?
|
125
|
-
|
126
|
-
|
127
|
-
if !
|
128
|
-
|
129
|
+
key_path = key_path.to_s
|
130
|
+
target_hash = @targets[target]
|
131
|
+
if !target_hash.nil? && target_hash.has_key?(key_path)
|
132
|
+
target_hash.delete(key_path)
|
129
133
|
end
|
130
134
|
end
|
131
135
|
|
132
136
|
def unobserve_all
|
133
|
-
keys = @targets.keys
|
134
|
-
while keys.
|
135
|
-
target = keys.pop
|
137
|
+
keys = [] + @targets.keys
|
138
|
+
while target = keys.pop
|
136
139
|
target_hash = @targets[target]
|
137
|
-
paths = target_hash.keys
|
138
|
-
while paths.
|
139
|
-
key_path
|
140
|
-
target.removeObserver(self, forKeyPath:key_path)
|
140
|
+
paths = [] + target_hash.keys
|
141
|
+
while key_path = paths.pop
|
142
|
+
unobserve(target, keyPath:key_path)
|
141
143
|
end
|
142
144
|
end
|
143
145
|
@targets.removeAllObjects
|
@@ -147,42 +149,80 @@ module RMExtensions
|
|
147
149
|
!target.nil? && !@targets[target].nil? && @targets[target].has_key?(key_path.to_s)
|
148
150
|
end
|
149
151
|
|
150
|
-
def add_observer_block(target, key_path,
|
152
|
+
def add_observer_block(target, key_path, block)
|
151
153
|
return if target.nil? || key_path.nil? || block.nil?
|
154
|
+
key_path = key_path.to_s
|
152
155
|
@targets[target] ||= {}
|
153
|
-
@targets[target][key_path
|
154
|
-
@targets[target][key_path
|
156
|
+
@targets[target][key_path] ||= []
|
157
|
+
@targets[target][key_path].addObject block
|
155
158
|
end
|
156
159
|
|
157
160
|
# NSKeyValueObserving Protocol
|
158
161
|
|
159
162
|
def observeValueForKeyPath(key_path, ofObject:target, change:change, context:context)
|
160
|
-
|
163
|
+
m_desc = nil
|
164
|
+
if ::RMExtensions.debug?
|
165
|
+
m_desc = "~~> ObservationProxy(#{@desc})#observeValueForKeyPath(#{key_path}, ofObject:#{target.inspect.split(" ").first}>, ...)"
|
166
|
+
p "called", m_desc
|
167
|
+
end
|
168
|
+
action = proc do
|
161
169
|
next if @did_dealloc
|
162
170
|
next if target.nil?
|
163
|
-
key_paths = @targets[target]
|
164
|
-
|
165
|
-
blocks
|
166
|
-
|
167
|
-
|
168
|
-
|
171
|
+
key_paths = @targets[target]
|
172
|
+
next if key_paths.nil?
|
173
|
+
blocks = key_paths[key_path]
|
174
|
+
next if blocks.nil?
|
175
|
+
blocks = [] + blocks # get a new array that can be popped
|
176
|
+
if ::RMExtensions.debug?
|
177
|
+
p "blocks.size", blocks.size, m_desc
|
178
|
+
end
|
179
|
+
while blk = blocks.pop
|
180
|
+
res = ObservationResponse.new
|
181
|
+
res.context = @weak_object
|
182
|
+
res.value = change[NSKeyValueChangeNewKey]
|
183
|
+
res.old_value = change[NSKeyValueChangeOldKey]
|
184
|
+
res.target = target
|
185
|
+
res.key = key_path
|
186
|
+
res.indexes = change[NSKeyValueChangeIndexesKey]
|
187
|
+
res.kind = change[NSKeyValueChangeKindKey]
|
188
|
+
blk.call(res)
|
169
189
|
end
|
170
190
|
end
|
191
|
+
if NSThread.currentThread.isMainThread
|
192
|
+
if ::RMExtensions.debug?
|
193
|
+
p "inline execution", m_desc
|
194
|
+
end
|
195
|
+
action.call
|
196
|
+
else
|
197
|
+
if ::RMExtensions.debug?
|
198
|
+
p "dispatch execution", m_desc
|
199
|
+
end
|
200
|
+
rmext_on_main_q(&action)
|
201
|
+
end
|
171
202
|
end
|
172
203
|
|
173
|
-
def
|
174
|
-
COLLECTION_OPERATIONS.include?(change[NSKeyValueChangeKindKey])
|
175
|
-
end
|
176
|
-
|
177
|
-
def on(event, &block)
|
204
|
+
def on(event, inContext:context, withBlock:block)
|
178
205
|
return if event.nil? || block.nil?
|
179
|
-
|
180
|
-
|
206
|
+
event = event.to_s
|
207
|
+
context ||= self.class
|
208
|
+
unless context_events = @events.objectForKey(context)
|
209
|
+
context_events = {}
|
210
|
+
@events.setObject(context_events, forKey:context)
|
211
|
+
end
|
212
|
+
unless context_event_blocks = context_events.objectForKey(event)
|
213
|
+
context_event_blocks = []
|
214
|
+
context_events.setObject(context_event_blocks, forKey:event)
|
215
|
+
end
|
216
|
+
context_event_blocks.addObject block
|
181
217
|
end
|
182
218
|
|
183
|
-
def off(event,
|
184
|
-
return if event.nil? || block.nil?
|
185
|
-
|
219
|
+
def off(event, inContext:context, withBlock:block)
|
220
|
+
return if event.nil? || block.nil?
|
221
|
+
event = event.to_s
|
222
|
+
context ||= self.class
|
223
|
+
return unless context_events = @events.objectForKey(context)
|
224
|
+
return unless context_event_blocks = context_events.objectForKey(event)
|
225
|
+
context_event_blocks.removeObject block
|
186
226
|
nil
|
187
227
|
end
|
188
228
|
|
@@ -190,10 +230,39 @@ module RMExtensions
|
|
190
230
|
@events.removeAllObjects
|
191
231
|
end
|
192
232
|
|
193
|
-
def trigger(event,
|
194
|
-
|
195
|
-
|
196
|
-
|
233
|
+
def trigger(event, value)
|
234
|
+
m_desc = nil
|
235
|
+
if ::RMExtensions.debug?
|
236
|
+
m_desc = "~~> ObservationProxy(#{@desc})#trigger(#{event}, #{value.inspect.split(" ").first }>)"
|
237
|
+
p "called", m_desc
|
238
|
+
end
|
239
|
+
rmext_on_main_q do
|
240
|
+
next if @did_dealloc
|
241
|
+
next if event.nil?
|
242
|
+
event = event.to_s
|
243
|
+
keyEnumerator = @events.keyEnumerator
|
244
|
+
contexts = []
|
245
|
+
while context = keyEnumerator.nextObject
|
246
|
+
contexts.push context
|
247
|
+
end
|
248
|
+
while context = contexts.pop
|
249
|
+
if context_events = @events.objectForKey(context)
|
250
|
+
if event_blocks = context_events[event]
|
251
|
+
blocks = [] + event_blocks
|
252
|
+
if ::RMExtensions.debug?
|
253
|
+
p "blocks.size", blocks.size, m_desc
|
254
|
+
end
|
255
|
+
while blk = blocks.pop
|
256
|
+
res = EventResponse.new
|
257
|
+
res.context = context
|
258
|
+
res.value = value
|
259
|
+
res.target = @weak_object
|
260
|
+
res.event = event
|
261
|
+
blk.call(res)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
197
266
|
end
|
198
267
|
nil
|
199
268
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rm-extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Noon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-12-02 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Extensions and helpers for dealing with various areas of rubymotion
|
14
14
|
email:
|
@@ -18,7 +18,6 @@ extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
20
|
- .gitignore
|
21
|
-
- CHANGELOG.md
|
22
21
|
- Gemfile
|
23
22
|
- LICENSE.txt
|
24
23
|
- README.md
|
data/CHANGELOG.md
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
## 0.2.0
|
2
|
-
|
3
|
-
[Commit history](https://github.com/joenoon/rm-extensions/compare/v0.1.10...v0.2.0)
|
4
|
-
|
5
|
-
* Experimental changes. Changelog to move to wiki soon.
|
6
|
-
|
7
|
-
## 0.1.10
|
8
|
-
|
9
|
-
[Commit history](https://github.com/joenoon/rm-extensions/compare/v0.1.9...v0.1.10)
|
10
|
-
|
11
|
-
* Layout: compact constraint(s) passed to #remove so a nil can be passed to reduce code
|
12
|
-
* Layout: adjust how to describe existing constraints
|
13
|
-
|
14
|
-
## 0.1.9
|
15
|
-
|
16
|
-
[Commit history](https://github.com/joenoon/rm-extensions/compare/v0.1.8...v0.1.9)
|
17
|
-
|
18
|
-
* Util: Added #rmext_ivar, which is a shortcut to instance_variable_get/instance_variable_set
|
19
|
-
* Layout: Added #reopen to yield the instance to a block for further processing
|
20
|
-
* Layout: Added an internal "constraint table" to keep track of normalized equations and
|
21
|
-
NSLayoutConstraint objects. equations can now be modified simply by re-applying the same
|
22
|
-
equation with a different constant.
|
23
|
-
* Layout: Added #xeq to remove a constraint by equation
|
24
|
-
* Layout: Added #remove(constraint(s)) to remove NSLayoutConstraint obects from the view
|
25
|
-
and also from the internal constraint table.
|
26
|
-
|
27
|
-
## 0.1.8
|
28
|
-
|
29
|
-
[Commit history](https://github.com/joenoon/rm-extensions/compare/v0.1.7...v0.1.8)
|
30
|
-
|
31
|
-
* Added RMExtensions::Layout#clear! which is a shortcut to view.removeConstraints(view.constraints).
|
32
|
-
It must be called after the `view` is set.
|
33
|
-
* Added 'max' as a valid priority shortcut, since it reads better than 'required' in many cases.
|
34
|
-
* Comments are now allowed on a line by themselves inside a `eqs` string, to make for easier commenting,
|
35
|
-
or to easily comment out an equation.
|
36
|
-
* Added a special `last_visible` view identifier which can be used instead of an actual view name
|
37
|
-
inside an equation. It will refer to the last view a constraint was applied to that was not hidden.
|
38
|
-
This can be used to more easily lay out constraints when some views should not be anchored against.
|
39
|
-
For example, you have 3 labels, A, B, and C, that you want stacked vertically:
|
40
|
-
A.top == 0
|
41
|
-
B.top == A.bottom + 5
|
42
|
-
C.top == B.bottom + 5
|
43
|
-
If B is hidden, there would be 10px in between A and C. Instead, you can let RMExtensions::Layout handle
|
44
|
-
this for you:
|
45
|
-
A.top == 0 # last_visible is set to A
|
46
|
-
B.top == last_visible.bottom + 5 # B is hidden, so last_visible remains A
|
47
|
-
C.top == last_visible.bottom + 5 # C now aligns to the bottom of A
|
48
|
-
It is usually easier and cleaner to build all possible views and mark some hidden than it is to have
|
49
|
-
many if/else view creation and constraint construction specific to what should be displayed.
|