motion-wiretap 1.1.5 → 1.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +99 -48
- data/lib/motion-wiretap/ios/wiretap_gestures.rb +6 -6
- data/lib/motion-wiretap/ios/wiretap_ios.rb +33 -5
- data/lib/motion-wiretap/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d28f1e832294f594579798c62718339c9822a31
|
4
|
+
data.tar.gz: aeeec36e2613e003f5d90788ffad2b9a06c4e56c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19c5b44d1a0b39827bf145baa0664f099ea239e487936a24246609aac88422a56cd2f8809c31043281618153c3fdf78748044a4a6808dc6e22721c004e534ee2
|
7
|
+
data.tar.gz: 6043ff1c69787e815f115d616399d19ad1296dc9fc9b08b3b2a67ce50f0febf8d9fc9795bbb566a577c51a20f4cf8a68cb55a6c6d7fdd67f0829c8aecc4944c2
|
data/README.md
CHANGED
@@ -50,7 +50,9 @@ like this style, but the default is to have a non-polluting system.
|
|
50
50
|
|
51
51
|
Wiretaps can be composed all sorts of ways: you can filter them, map them,
|
52
52
|
combine multiple wiretaps into a single value, use them to respond to
|
53
|
-
notifications or control events.
|
53
|
+
notifications or control events. When you compose them in this way you only
|
54
|
+
need to retain the "bottom most" wiretap. Examples follow.
|
55
|
+
|
54
56
|
|
55
57
|
### Let's start with something practical!
|
56
58
|
|
@@ -60,17 +62,17 @@ In these examples I will use all three ways of creating a Wiretap
|
|
60
62
|
```ruby
|
61
63
|
# assign the label to the text view; changes to the text view will be reflected
|
62
64
|
# on the label.
|
63
|
-
MW(@label, :text).bind_to(MW(@text_view, :text))
|
65
|
+
@wiretap = MW(@label, :text).bind_to(MW(@text_view, :text))
|
64
66
|
|
65
67
|
# assign the attributedText of the label to the text view, doing some
|
66
68
|
# highlighting in-between.
|
67
|
-
@label.wiretap(:attributedText).bind_to(@text_view.wiretap(:text).map do |text|
|
69
|
+
@wiretap = @label.wiretap(:attributedText).bind_to(@text_view.wiretap(:text).map do |text|
|
68
70
|
NSAttributedString.alloc.initWithString(text, attributes: { NSForegroundColorAttributeName => UIColor.blueColor })
|
69
71
|
end)
|
70
72
|
|
71
73
|
# This code will set the 'enabled' property depending on whether the username
|
72
74
|
# and password are not empty.
|
73
|
-
Motion.wiretap(@login_button, :enabled).bind_to(
|
75
|
+
@wiretap = Motion.wiretap(@login_button, :enabled).bind_to(
|
74
76
|
Motion.wiretap([
|
75
77
|
Motion.wiretap(@username_field, :text),
|
76
78
|
Motion.wiretap(@password_field, :text),
|
@@ -81,6 +83,10 @@ Motion.wiretap(@login_button, :enabled).bind_to(
|
|
81
83
|
)
|
82
84
|
```
|
83
85
|
|
86
|
+
See how in the example above I only retain the "final" signal (the return value
|
87
|
+
from `#combine` is what gets retained by `@wiretap`). That's what I mean by the
|
88
|
+
"bottom most" wiretap. Don't worry, the intermediate wiretaps get retained.
|
89
|
+
|
84
90
|
### Types of wiretaps
|
85
91
|
|
86
92
|
- Key-Value Observation / KVO
|
@@ -102,35 +108,35 @@ end
|
|
102
108
|
person = Person.new
|
103
109
|
# you need to store the wiretap object in memory; when the object is
|
104
110
|
# deallocated, it will no longer receive updates.
|
105
|
-
wiretap = Motion.wiretap(person, :name)
|
111
|
+
@wiretap = Motion.wiretap(person, :name)
|
106
112
|
# react to change events
|
107
|
-
wiretap.listen do |name|
|
113
|
+
@wiretap.listen do |name|
|
108
114
|
puts "name is now #{name}"
|
109
115
|
end
|
110
116
|
person.name = 'Joe'
|
111
117
|
# puts => "name is now Joe"
|
112
118
|
|
113
119
|
# Since listening is very common, you can easily shorthand this to:
|
114
|
-
wiretap = Motion.wiretap(person, :name) do |name|
|
120
|
+
@wiretap = Motion.wiretap(person, :name) do |name|
|
115
121
|
puts "name is now #{name}"
|
116
122
|
end
|
117
123
|
|
118
124
|
# bind the property of one object to the value of another
|
119
125
|
person_1 = Person.new
|
120
126
|
person_2 = Person.new
|
121
|
-
wiretap = Motion.wiretap(person_1, :name)
|
122
|
-
wiretap.bind_to(person_2, :name) # creates a new Wiretap object for person_2; changes to person_2.name will affect person_1
|
127
|
+
@wiretap = Motion.wiretap(person_1, :name)
|
128
|
+
@wiretap.bind_to(person_2, :name) # creates a new Wiretap object for person_2; changes to person_2.name will affect person_1
|
123
129
|
person_2.name = 'Bob'
|
124
130
|
person_1.name # => "Bob"
|
125
131
|
|
126
132
|
# cancel a wiretap
|
127
|
-
wiretap.cancel!
|
133
|
+
@wiretap.cancel!
|
128
134
|
person_2.name = 'Jane'
|
129
135
|
person_1.name
|
130
136
|
# => "BOB"
|
131
137
|
|
132
138
|
# bind the property of one object to the value of another, but change it using `map`
|
133
|
-
wiretap = Motion.wiretap(person_1, :name).bind_to(Motion.wiretap(person_2, :name).map { |value| value.upcase })
|
139
|
+
@wiretap = Motion.wiretap(person_1, :name).bind_to(Motion.wiretap(person_2, :name).map { |value| value.upcase })
|
134
140
|
person_2.name = 'Bob'
|
135
141
|
person_1.name # => "BOB"
|
136
142
|
```
|
@@ -138,70 +144,82 @@ person_1.name # => "BOB"
|
|
138
144
|
### Working with arrays
|
139
145
|
|
140
146
|
```ruby
|
141
|
-
# combine the values `name` and `email
|
147
|
+
# combine the values `name` and `email`, which means they'll be sent together
|
148
|
+
# when either one changes
|
142
149
|
person = Person.new
|
143
|
-
|
150
|
+
@info = nil
|
151
|
+
@taps = Motion.wiretap([
|
144
152
|
Motion.wiretap(person, :name),
|
145
153
|
Motion.wiretap(person, :email),
|
146
154
|
]).combine do |name, email|
|
147
|
-
|
155
|
+
@info = "#{name} <#{email}>"
|
148
156
|
end
|
149
157
|
|
150
158
|
person.name = 'Kazuo'
|
151
|
-
#
|
159
|
+
# @info => "Kazuo <>"
|
152
160
|
person.email = 'kaz@example.com'
|
153
|
-
#
|
161
|
+
# @info => "Kazuo <kaz@example.com>"
|
154
162
|
|
155
|
-
# reduce
|
163
|
+
# With reduce you get a memo and a value, and you should return a new value. Be
|
164
|
+
# careful about mutable structures: the initial memo is retained, not copied.
|
156
165
|
person_1 = Person.new
|
157
166
|
person_2 = Person.new
|
158
|
-
|
167
|
+
@names = []
|
168
|
+
@taps = Motion.wiretap([
|
159
169
|
Motion.wiretap(person_1, :name),
|
160
170
|
Motion.wiretap(person_2, :name),
|
161
|
-
]).reduce do |memo, name|
|
162
|
-
memo ||= []
|
171
|
+
]).reduce([]) do |memo, name|
|
163
172
|
memo + [name]
|
164
|
-
end
|
165
|
-
|
166
|
-
puts names.inspect
|
173
|
+
end.listen do |names|
|
174
|
+
@names = names.inspect
|
167
175
|
end
|
168
176
|
person_1.name = 'Mr. White'
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
# you can provide an initial 'memo' (default: nil)
|
173
|
-
Motion.wiretap([
|
174
|
-
Motion.wiretap(person_1, :name),
|
175
|
-
Motion.wiretap(person_2, :name),
|
176
|
-
]).reduce([]) do |memo, name|
|
177
|
-
memo + [name] # you should not change memo in place, the same one will be used on every change event
|
178
|
-
end
|
177
|
+
# @names => ["Mr. White", nil]
|
178
|
+
person_2.name = 'Mr. Blue'
|
179
|
+
# @names => ["Mr. White", "Mr. Blue"]
|
179
180
|
```
|
180
181
|
|
181
182
|
### Monitoring jobs
|
182
183
|
|
184
|
+
There is a "short form" and "long form" to these wiretaps. The "short form" is
|
185
|
+
something like `Motion.wiretap(proc) do (block) end`. The `proc` will be
|
186
|
+
executed, and when it's done, the `block` will execute.
|
187
|
+
|
188
|
+
The "long form" is different only in that the block is passed to the `and_then`
|
189
|
+
method, not the initializer: `Motion.wiretap(proc).and_then do ... end`. In
|
190
|
+
this form, you must call `start` on the wiretap:
|
191
|
+
|
183
192
|
```ruby
|
184
|
-
# Monitor for background job completion:
|
185
|
-
Motion.wiretap(-> do
|
193
|
+
# Monitor for background job completion, short form:
|
194
|
+
@wiretap = Motion.wiretap(-> do
|
195
|
+
this_will_take_forever!
|
196
|
+
end) do
|
197
|
+
puts "done!"
|
198
|
+
end
|
199
|
+
|
200
|
+
# Monitor for background job completion, long form:
|
201
|
+
@wiretap = Motion.wiretap(-> do
|
186
202
|
this_will_take_forever!
|
187
203
|
end).and_then do
|
188
204
|
puts "done!"
|
189
|
-
end.start
|
205
|
+
end.start # you must call 'start' explicitly
|
190
206
|
|
191
|
-
# Same, but specifying the thread to run on. The completion block will be
|
192
|
-
# on this thread, too. The queue conveniently accepts a completion
|
193
|
-
# (delegates to the `Wiretap#and_then` method).
|
194
|
-
Motion.wiretap(-> do
|
207
|
+
# Same again, but specifying the thread to run on. The completion block will be
|
208
|
+
# called on this thread, too. The queue conveniently accepts a completion
|
209
|
+
# handler (delegates to the `Wiretap#and_then` method).
|
210
|
+
@wiretap = Motion.wiretap(-> do
|
195
211
|
this_will_take_forever!
|
196
212
|
end).queue(Dispatch::Queue.concurrent).and_then do
|
197
213
|
puts "done!"
|
198
|
-
end
|
214
|
+
end
|
215
|
+
@wiretap.start
|
199
216
|
|
200
217
|
# Send a stream of values from a block. A lambda is passed in that will forward
|
201
218
|
# change events to the `listen` block
|
202
|
-
Motion.wiretap(-> (on_change) do
|
219
|
+
@wiretap = Motion.wiretap(-> (on_change) do
|
203
220
|
5.times do |count|
|
204
221
|
on_change.call count
|
222
|
+
sleep(1)
|
205
223
|
end
|
206
224
|
end).listen do |index|
|
207
225
|
puts "...#{index}"
|
@@ -211,17 +229,50 @@ end.start
|
|
211
229
|
# => puts "...0", "...1", "...2", "...3", "...4", "done!"
|
212
230
|
```
|
213
231
|
|
214
|
-
###
|
232
|
+
### Gestures
|
233
|
+
|
234
|
+
Possible gestures:
|
235
|
+
|
236
|
+
:tap, :pinch, :rotate, :swipe, :pan, :press
|
237
|
+
|
238
|
+
Options:
|
239
|
+
|
240
|
+
:taps (:tap, :press)
|
241
|
+
:fingers (:tap, :swipe, :pan, :press)
|
242
|
+
:direction (:direction)
|
243
|
+
:min_fingers (:pan)
|
244
|
+
:max_fingers (:pan)
|
245
|
+
:duration (:press)
|
215
246
|
|
216
247
|
```ruby
|
217
|
-
|
218
|
-
|
248
|
+
@wiretap = Motion.wiretap(view).on(:tap) do |gesture|
|
249
|
+
point = gesture.locationInView(view)
|
250
|
+
puts "you tapped #{point.x}, #{point.y}"
|
219
251
|
end
|
220
|
-
# the notification observer will be removed when the Wiretap is dealloc'd
|
221
252
|
```
|
222
253
|
|
223
|
-
###
|
254
|
+
### Control events
|
255
|
+
|
256
|
+
Possible events:
|
257
|
+
|
258
|
+
:touch, :touch_up, :touch_down, :touch_start, :touch_stop, :change, :begin,
|
259
|
+
:end, :touch_down_repeat, :touch_drag_inside, :touch_drag_outside,
|
260
|
+
:touch_drag_enter, :touch_drag_exit, :touch_up_inside, :touch_up_outside,
|
261
|
+
:touch_cancel, :value_changed, :editing_did_begin, :editing_changed,
|
262
|
+
:editing_did_change, :editing_did_end, :editing_did_end_on_exit
|
263
|
+
:all_touch, :all_editing, :application, :system, :all
|
224
264
|
|
225
265
|
```ruby
|
266
|
+
@wiretap = Motion.wiretap(control).on(:touch) do |event|
|
267
|
+
end
|
268
|
+
```
|
226
269
|
|
227
|
-
|
270
|
+
### Notifications
|
271
|
+
|
272
|
+
```ruby
|
273
|
+
@wiretap = Motion.wiretap('NotificationName') do |object, user_info|
|
274
|
+
puts 'notification received!'
|
275
|
+
end
|
276
|
+
|
277
|
+
NSNotificationCenter.defaultCenter.postNotificationName('NotificationName', object: nil, userInfo: info)
|
278
|
+
```
|
@@ -23,7 +23,7 @@ module MotionWiretap
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
recognizer = UITapGestureRecognizer.alloc.initWithTarget(target, action: :
|
26
|
+
recognizer = UITapGestureRecognizer.alloc.initWithTarget(target, action: 'handle_gesture:')
|
27
27
|
recognizer.numberOfTapsRequired = taps if taps
|
28
28
|
recognizer.numberOfTouchesRequired = fingers if fingers
|
29
29
|
return recognizer
|
@@ -31,13 +31,13 @@ module MotionWiretap
|
|
31
31
|
|
32
32
|
# @yield [recognizer] Handles the gesture event, and passes the recognizer instance to the block.
|
33
33
|
def pinch(target)
|
34
|
-
recognizer = UIPinchGestureRecognizer.alloc.initWithTarget(target, action: :
|
34
|
+
recognizer = UIPinchGestureRecognizer.alloc.initWithTarget(target, action: 'handle_gesture:')
|
35
35
|
return recognizer
|
36
36
|
end
|
37
37
|
|
38
38
|
# @yield [recognizer] Handles the gesture event, and passes the recognizer instance to the block.
|
39
39
|
def rotate(target)
|
40
|
-
recognizer = UIRotationGestureRecognizer.alloc.initWithTarget(target, action: :
|
40
|
+
recognizer = UIRotationGestureRecognizer.alloc.initWithTarget(target, action: 'handle_gesture:')
|
41
41
|
return recognizer
|
42
42
|
end
|
43
43
|
|
@@ -71,7 +71,7 @@ module MotionWiretap
|
|
71
71
|
direction = UISwipeGestureRecognizerDirectionDown
|
72
72
|
end
|
73
73
|
|
74
|
-
recognizer = UISwipeGestureRecognizer.alloc.initWithTarget(target, action: :
|
74
|
+
recognizer = UISwipeGestureRecognizer.alloc.initWithTarget(target, action: 'handle_gesture:')
|
75
75
|
recognizer.direction = direction if direction
|
76
76
|
recognizer.numberOfTouchesRequired = fingers if fingers
|
77
77
|
return recognizer
|
@@ -103,7 +103,7 @@ module MotionWiretap
|
|
103
103
|
min_fingers ||= fingers
|
104
104
|
max_fingers ||= fingers
|
105
105
|
|
106
|
-
recognizer = UIPanGestureRecognizer.alloc.initWithTarget(target, action: :
|
106
|
+
recognizer = UIPanGestureRecognizer.alloc.initWithTarget(target, action: 'handle_gesture:')
|
107
107
|
recognizer.maximumNumberOfTouches = min_fingers if min_fingers
|
108
108
|
recognizer.minimumNumberOfTouches = max_fingers if max_fingers
|
109
109
|
return recognizer
|
@@ -131,7 +131,7 @@ module MotionWiretap
|
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
134
|
-
recognizer = UILongPressGestureRecognizer.alloc.initWithTarget(target, action: :
|
134
|
+
recognizer = UILongPressGestureRecognizer.alloc.initWithTarget(target, action: 'handle_gesture:')
|
135
135
|
recognizer.minimumPressDuration = duration if duration
|
136
136
|
recognizer.numberOfTapsRequired = taps if taps
|
137
137
|
recognizer.numberOfTouchesRequired = fingers if fingers
|
@@ -4,6 +4,11 @@ module MotionWiretap
|
|
4
4
|
|
5
5
|
class WiretapView < WiretapTarget
|
6
6
|
|
7
|
+
def initialize(target, &block)
|
8
|
+
super
|
9
|
+
@gesture_recognizers = []
|
10
|
+
end
|
11
|
+
|
7
12
|
def on(recognizer, options=nil, &block)
|
8
13
|
if recognizer
|
9
14
|
case recognizer
|
@@ -24,6 +29,7 @@ module MotionWiretap
|
|
24
29
|
end
|
25
30
|
|
26
31
|
self.target.addGestureRecognizer(recognizer)
|
32
|
+
@gesture_recognizers << recognizer
|
27
33
|
end
|
28
34
|
|
29
35
|
listen(&block)
|
@@ -31,14 +37,27 @@ module MotionWiretap
|
|
31
37
|
return self
|
32
38
|
end
|
33
39
|
|
34
|
-
def handle_gesture
|
35
|
-
trigger_changed
|
40
|
+
def handle_gesture(gesture)
|
41
|
+
trigger_changed(gesture)
|
42
|
+
end
|
43
|
+
|
44
|
+
def teardown
|
45
|
+
remove_gesture = (-> (recognizer) {
|
46
|
+
self.target.removeGestureRecognizer(recognizer)
|
47
|
+
}).weak!
|
48
|
+
@gesture_recognizers.each(&remove_gesture)
|
49
|
+
super
|
36
50
|
end
|
37
51
|
|
38
52
|
end
|
39
53
|
|
40
54
|
class WiretapControl < WiretapView
|
41
55
|
|
56
|
+
def initialize(target, &block)
|
57
|
+
super
|
58
|
+
@control_events = []
|
59
|
+
end
|
60
|
+
|
42
61
|
# control_event can be any UIControlEventConstant, or any symbol found in
|
43
62
|
# wiretap_control_events.rb, or an array of UIControlEventConstants or
|
44
63
|
# symbols. Since UIView implements `on` to accept a gesture, this method
|
@@ -46,7 +65,8 @@ module MotionWiretap
|
|
46
65
|
def on(control_event, options={}, &block)
|
47
66
|
begin
|
48
67
|
control_event = ControlEvents.convert(control_event)
|
49
|
-
self.target.addTarget(self, action: :
|
68
|
+
self.target.addTarget(self, action: 'handle_event:', forControlEvents: control_event)
|
69
|
+
@control_events << control_event
|
50
70
|
rescue ControlEventNotFound
|
51
71
|
super(control_event, options, &block)
|
52
72
|
else
|
@@ -56,8 +76,16 @@ module MotionWiretap
|
|
56
76
|
return self
|
57
77
|
end
|
58
78
|
|
59
|
-
def handle_event
|
60
|
-
trigger_changed
|
79
|
+
def handle_event(event)
|
80
|
+
trigger_changed(event)
|
81
|
+
end
|
82
|
+
|
83
|
+
def teardown
|
84
|
+
remove_event = (-> (event) {
|
85
|
+
self.target.removeTarget(self, action: 'handle_event:', forControlEvents: event)
|
86
|
+
}).weak!
|
87
|
+
@control_events.each(&remove_event)
|
88
|
+
super
|
61
89
|
end
|
62
90
|
|
63
91
|
end
|