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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cbf506f0e146d719368d97d86a04b4fede0df97b
4
- data.tar.gz: 6d8b171e1add24ee6c63d27ee4057c6051f944a4
3
+ metadata.gz: dddb35f12d5cf8cc9de4cb3b39df6644497bf550
4
+ data.tar.gz: 93e202d15df61498e5432194d8e2466d37ece16d
5
5
  SHA512:
6
- metadata.gz: 3204fde1f0f0e9c903aa2fdda8a638f4c91544e2a81b404b728eb9bd100200ce8c81fa5d69cc6d2a57002a1182d968d331b8164e629fa30227b6f426e2ccdb18
7
- data.tar.gz: 8fc6a988442c02b7ba501b7c360a1825d20ed40899b6707d3d4b179cb8e51c865f2552ab59357ea0a64368341a3345be2918aabdb7dc0f9f618c6ca11fcb825c
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
- ## Equation-style AutoLayout Constraints
6
+ #### [Documentation](https://github.com/joenoon/rm-extensions/wiki/RMExtensions-Documentation)
7
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
-
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, [joenoon](https://github.com/joenoon)
40
+ [Joe Noon](https://github.com/joenoon)
@@ -8,26 +8,22 @@ module RMExtensions
8
8
  @rmext_observation_proxy ||= ObservationProxy.new(self)
9
9
  end
10
10
 
11
- # observe an object.key. takes a block that will be called with the
12
- # new value upon change.
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
- # like +rmext_observe_passive+ but additionally fires the callback immediately.
22
- def rmext_observe(object, key, &block)
23
- rmext_observe_passive(object, key, &block)
24
- block.call(object.send(key)) unless block.nil?
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, &block)
43
- rmext_observation_proxy.on(event, &block)
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, &block)
43
+ def rmext_off(event, inContext:context, withBlock:block)
48
44
  if @rmext_observation_proxy
49
- @rmext_observation_proxy.off(event, &block)
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 args on this object
61
- def rmext_trigger(event, *args)
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, *args)
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 for #{@desc}"
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 for #{@desc}"
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, &block)
113
- target.addObserver(self, forKeyPath:key_path, options:DEFAULT_OPTIONS, context:nil) unless registered?(target, key_path)
114
- add_observer_block(target, key_path, &block)
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
- key_paths = @targets[target]
127
- if !key_paths.nil? && key_paths.has_key?(key_path.to_s)
128
- key_paths.removeObject(key_path.to_s)
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.clone
134
- while keys.size > 0
135
- target = keys.pop
137
+ keys = [] + @targets.keys
138
+ while target = keys.pop
136
139
  target_hash = @targets[target]
137
- paths = target_hash.keys.clone
138
- while paths.size > 0
139
- key_path = paths.pop
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, &block)
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.to_s] ||= NSHashTable.weakObjectsHashTable
154
- @targets[target][key_path.to_s].addObject block
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
- rmext_on_main_q do
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
- blocks = key_paths[key_path] || []
165
- blocks.each do |block|
166
- args = [ change[NSKeyValueChangeNewKey] ]
167
- args << change[NSKeyValueChangeIndexesKey] if collection?(change)
168
- block.call(*args)
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 collection?(change)
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
- @events[event.to_s] ||= []
180
- @events[event.to_s].addObject block
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, &block)
184
- return if event.nil? || block.nil? || !@events.key?(event.to_s)
185
- @events[event.to_s].removeObject block
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, *args)
194
- return if event.nil? || !@events.key?(event.to_s)
195
- @events[event.to_s].each do |block|
196
- block.call(*args)
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
@@ -1,3 +1,3 @@
1
1
  module RMExtensions
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  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.2.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-26 00:00:00.000000000 Z
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
@@ -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.