motion-xray 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +21 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +28 -0
- data/README.md +426 -0
- data/Rakefile +11 -0
- data/app/app_delegate.rb +44 -0
- data/lib/motion-xray.rb +37 -0
- data/lib/motion-xray/plugins/accessibility_plugin.rb +129 -0
- data/lib/motion-xray/plugins/log_plugin.rb +301 -0
- data/lib/motion-xray/plugins/save_ui_plugin.rb +142 -0
- data/lib/motion-xray/plugins/ui_plugin.rb +41 -0
- data/lib/motion-xray/version.rb +5 -0
- data/lib/motion-xray/views/xray_color_swatch.rb +234 -0
- data/lib/motion-xray/views/xray_dpad.rb +142 -0
- data/lib/motion-xray/views/xray_gradient_view.rb +23 -0
- data/lib/motion-xray/views/xray_headers.rb +101 -0
- data/lib/motion-xray/views/xray_lock_button.rb +50 -0
- data/lib/motion-xray/views/xray_scroll_view.rb +12 -0
- data/lib/motion-xray/views/xray_toolbar.rb +173 -0
- data/lib/motion-xray/views/xray_window.rb +13 -0
- data/lib/motion-xray/xray.rb +56 -0
- data/lib/motion-xray/xray_constants.rb +5 -0
- data/lib/motion-xray/xray_dummy.rb +8 -0
- data/lib/motion-xray/xray_editors.rb +62 -0
- data/lib/motion-xray/xray_ext.rb +125 -0
- data/lib/motion-xray/xray_plugin.rb +40 -0
- data/lib/motion-xray/xray_typewriter.rb +217 -0
- data/lib/motion-xray/xray_ui.rb +723 -0
- data/lib/motion-xray/z_editors/xray_boolean_editor.rb +24 -0
- data/lib/motion-xray/z_editors/xray_color_editor.rb +119 -0
- data/lib/motion-xray/z_editors/xray_frame_editor.rb +108 -0
- data/lib/motion-xray/z_editors/xray_image_editor.rb +78 -0
- data/lib/motion-xray/z_editors/xray_text_editor.rb +94 -0
- data/lib/resources/xray_button_bg@2x.png +0 -0
- data/lib/resources/xray_choose_button@2x.png +0 -0
- data/lib/resources/xray_clear_button@2x.png +0 -0
- data/lib/resources/xray_detail_button@2x.png +0 -0
- data/lib/resources/xray_dpad@2x.png +0 -0
- data/lib/resources/xray_dpad_center@2x.png +0 -0
- data/lib/resources/xray_dpad_down@2x.png +0 -0
- data/lib/resources/xray_dpad_left@2x.png +0 -0
- data/lib/resources/xray_dpad_right@2x.png +0 -0
- data/lib/resources/xray_dpad_up@2x.png +0 -0
- data/lib/resources/xray_drawer_left@2x.png +0 -0
- data/lib/resources/xray_drawer_right@2x.png +0 -0
- data/lib/resources/xray_edit_button@2x.png +0 -0
- data/lib/resources/xray_email_button@2x.png +0 -0
- data/lib/resources/xray_lock_button_horizontal@2x.png +0 -0
- data/lib/resources/xray_lock_button_locked@2x.png +0 -0
- data/lib/resources/xray_lock_button_unlocked@2x.png +0 -0
- data/lib/resources/xray_lock_button_vertical@2x.png +0 -0
- data/motion-xray.gemspec +40 -0
- data/resources/Default-568h@2x.png +0 -0
- data/spec/xray_view_spec.rb +43 -0
- data/vendor/Podfile.lock +11 -0
- metadata +177 -0
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
.repl_history
|
2
|
+
build
|
3
|
+
tags
|
4
|
+
app/pixate_code.rb
|
5
|
+
resources/*.nib
|
6
|
+
resources/*.momd
|
7
|
+
resources/*.storyboardc
|
8
|
+
.DS_Store
|
9
|
+
nbproject
|
10
|
+
.redcar
|
11
|
+
#*#
|
12
|
+
*~
|
13
|
+
*.sw[po]
|
14
|
+
.eprj
|
15
|
+
.sass-cache
|
16
|
+
.idea
|
17
|
+
*.gem
|
18
|
+
*.pxm
|
19
|
+
/vendor/Pods/
|
20
|
+
/vendor/Pods
|
21
|
+
.dat*
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
motion-xray (1.0.3)
|
5
|
+
geomotion (>= 0.10.0)
|
6
|
+
sugarcube (>= 0.20.1)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
diff-lcs (1.1.3)
|
12
|
+
geomotion (0.10.0)
|
13
|
+
rspec (2.12.0)
|
14
|
+
rspec-core (~> 2.12.0)
|
15
|
+
rspec-expectations (~> 2.12.0)
|
16
|
+
rspec-mocks (~> 2.12.0)
|
17
|
+
rspec-core (2.12.2)
|
18
|
+
rspec-expectations (2.12.1)
|
19
|
+
diff-lcs (~> 1.1.3)
|
20
|
+
rspec-mocks (2.12.2)
|
21
|
+
sugarcube (0.20.1)
|
22
|
+
|
23
|
+
PLATFORMS
|
24
|
+
ruby
|
25
|
+
|
26
|
+
DEPENDENCIES
|
27
|
+
motion-xray!
|
28
|
+
rspec
|
data/README.md
ADDED
@@ -0,0 +1,426 @@
|
|
1
|
+
Motion-Xray
|
2
|
+
====
|
3
|
+
|
4
|
+
Developer tools for iOS. Runs on the device, no browser or computer needed.
|
5
|
+
|
6
|
+
(think Firebug or Webkit Developer Tools)
|
7
|
+
|
8
|
+
TL;DR
|
9
|
+
-----
|
10
|
+
|
11
|
+
1. `gem install motion-xray`
|
12
|
+
2. Replace `UIWindow` with `Motion::Xray::XrayWindow`
|
13
|
+
|
14
|
+
The Problem
|
15
|
+
-----------
|
16
|
+
|
17
|
+
During development we rely heavily on the simulator to quickly view and test
|
18
|
+
features, but often when we finally install our app on a device, the experience
|
19
|
+
is not up-to-snuff with what was going on in the simulator. Views are off by a
|
20
|
+
few pixels, performance is not what we expect, and crashes occur where we never
|
21
|
+
saw them in the simulator. Sometimes these are device problems, sometimes it
|
22
|
+
has to do with dropping in and out of signal, all sorts of scenarios that we
|
23
|
+
cannot easily test for in the simulator.
|
24
|
+
|
25
|
+
And of course there is the problem that iOS devices have more features than the
|
26
|
+
simulator! Bluetooth 4, for example, is not easy to get setup in the simulator
|
27
|
+
(and you have to buy a USB bluetooth module).
|
28
|
+
|
29
|
+
My thesis is that we need to make on-device testing a more enjoyable and useful
|
30
|
+
testing environment, so that we are compelled to test on it sooner and more
|
31
|
+
often.
|
32
|
+
|
33
|
+
My Proposal
|
34
|
+
-----------
|
35
|
+
|
36
|
+
Motion-Xray is such a solution. During development you can use Xray as a UI
|
37
|
+
inspector, or to monitor the console log, preview how accessibile your app is
|
38
|
+
(to blind and color blind developers), or you can create a plugin that provides
|
39
|
+
information specifically useful to your app. Below I'll show how to create a
|
40
|
+
new plugin. Check out the [plugins folder][] for some examples.
|
41
|
+
|
42
|
+
Features
|
43
|
+
-----
|
44
|
+
|
45
|
+
If you clone and run Xray in the simulator, you will see a very boring app:
|
46
|
+
|
47
|
+
![Xray Screenshot](http://media.colinta.com/xray/xray_app.png)
|
48
|
+
|
49
|
+
Activate a "shake" gesture by pressing ⌘⌃Z and Xray will activate, which
|
50
|
+
displays this:
|
51
|
+
|
52
|
+
![Xray Screenshot](http://media.colinta.com/xray/xray.png)
|
53
|
+
|
54
|
+
The application shrinks down to a quarter size, and the development environment
|
55
|
+
takes up the remaining space. That is Xray, an in-app debugging and development
|
56
|
+
environment! :-D
|
57
|
+
|
58
|
+
Features
|
59
|
+
------------
|
60
|
+
|
61
|
+
That's enough to have the `Motion::Xray.toggle` command fired whenever you shake
|
62
|
+
the device. If you want to use some other mechanism that launches Xray (a
|
63
|
+
complicated gesture recognizer would be a good candidate), you can call
|
64
|
+
`Xray.toggle` (which calls either `Xray.fire_up` or `Xray.cool_down`). The
|
65
|
+
`Motion::Xray::XrayWindow` class is only used to listen for the shake event, so
|
66
|
+
using it will not affect your app in any other way.
|
67
|
+
|
68
|
+
When you shake your phone and activate Xray, you are presented with three panes
|
69
|
+
and a toolbar at the bottom:
|
70
|
+
|
71
|
+
![Preview panes](http://media.colinta.com/xray/xray_panes.png)
|
72
|
+
|
73
|
+
### 1. Preview
|
74
|
+
|
75
|
+
All the views under the main window are placed in the `Preview` area:
|
76
|
+
|
77
|
+
![Preview pane](http://media.colinta.com/xray/xray_preview.png)
|
78
|
+
|
79
|
+
If you touch this area, you can get a quick preview of the view, or you can
|
80
|
+
quickly change to another view, or change orientation. After a few seconds,
|
81
|
+
Xray will automatically be displayed again. If you want to leave the Xray debug
|
82
|
+
area, you should shake the phone again.
|
83
|
+
|
84
|
+
### 2. UI Selector
|
85
|
+
|
86
|
+
This pane shows the view hierarchy of your app:
|
87
|
+
|
88
|
+
![UI selector pane](http://media.colinta.com/xray/xray_uiselector.png)
|
89
|
+
|
90
|
+
All the views on screen can be selected here, and a red box will show the bounds
|
91
|
+
of that view in the `Preview` pane. If you touch it again, that view will be
|
92
|
+
sent to whatever plugin you have visible, or you can press the "down" button in
|
93
|
+
the bottom-right corner of this pane.
|
94
|
+
|
95
|
+
Not all plugins respond to the selected view. For instance the accessibility
|
96
|
+
plugin will always display the entire screen, regardless of which view is
|
97
|
+
selected. The log plugin, on the other hand, displays the `inspect` information
|
98
|
+
about the view. And of course the UI plugin will change so that you can edit
|
99
|
+
the properties of that view.
|
100
|
+
|
101
|
+
The button in the upper-left corner expands this view, so that you can see the
|
102
|
+
tree easier.
|
103
|
+
|
104
|
+
![Expanded tree view](http://media.colinta.com/xray/xray_tree.png)
|
105
|
+
|
106
|
+
In the upper-right corner is the button to activate a visual view selector:
|
107
|
+
|
108
|
+
![UIView selector](http://media.colinta.com/xray/xray_uiselector.png)
|
109
|
+
|
110
|
+
You can tap a view to get information about it, or press and hold to make that
|
111
|
+
view "go away" so that you can choose the view *behind* it, or double-tap to
|
112
|
+
select that view.
|
113
|
+
|
114
|
+
### 3. Plugin Canvas
|
115
|
+
|
116
|
+
Here's where the inspector and other plugins live, with a toolbar at the bottom
|
117
|
+
to select what plugin you want to view:
|
118
|
+
|
119
|
+
![Plugin pane](http://media.colinta.com/xray/xray_canvas.png)
|
120
|
+
|
121
|
+
It is very easy to create new plugins, I'll go over that below. After you
|
122
|
+
create a new plugin, you register it with Xray:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
Xray.register(YourPlugin.new)
|
126
|
+
```
|
127
|
+
|
128
|
+
Built-in plugins
|
129
|
+
----------------
|
130
|
+
|
131
|
+
### UI (`Motion::Xray::UIPlugin`)
|
132
|
+
|
133
|
+
**included automatically**
|
134
|
+
|
135
|
+
The original idea for Xray was just this UI plugin. The other plugins came
|
136
|
+
later. I realized that it could (and should) be a generic "development
|
137
|
+
environment" instead of a "UI editor". Also, some early feedback from the
|
138
|
+
HipByte team helped open up this world of possibilities. :-)
|
139
|
+
|
140
|
+
`UIPlugin` uses a pluggable architecture. First, there are the editors:
|
141
|
+
|
142
|
+
- `Motion::Xray::TextEditor`
|
143
|
+
- `Motion::Xray::ColorEditor`
|
144
|
+
- `Motion::Xray::BooleanEditor`
|
145
|
+
- `Motion::Xray::FrameEditor`
|
146
|
+
|
147
|
+
Second, these editors get associated with the view properties in a `Hash` that
|
148
|
+
is returned by the class method `UIView##xray`. In custom views you only need
|
149
|
+
to return the properties that *your custom view uses*; any editable properties
|
150
|
+
in views you inherit from will be included. Don't do any merging in your `xray`
|
151
|
+
method, that is handled by the plugin (by `UIView##build_xray`, in
|
152
|
+
`xray_ext.rb`)
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
class << UILabel
|
156
|
+
def xray
|
157
|
+
{
|
158
|
+
'Content': { # section name
|
159
|
+
text: Motion::Xray::TextEditor, # property => editor class
|
160
|
+
}
|
161
|
+
}
|
162
|
+
end
|
163
|
+
end
|
164
|
+
```
|
165
|
+
|
166
|
+
If you inherit from a view and you want to *disable* one of the editors, assign
|
167
|
+
`nil` as the editor for that property. `UIWindow` does this to prevent editing
|
168
|
+
`frame`, `hidden`, and `userInteractionEnabled` properties from getting changed.
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
class << UIWindow
|
172
|
+
def xray
|
173
|
+
{
|
174
|
+
'TurnOff' => {
|
175
|
+
frame: nil,
|
176
|
+
hidden: nil,
|
177
|
+
userInteractionEnabled: nil,
|
178
|
+
},
|
179
|
+
}
|
180
|
+
end
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
Writing custom editors can be time consuming, because they are often very UI
|
185
|
+
heavy (check out the `ColorEditor` to see what I mean). That said, the concept
|
186
|
+
is very easy:
|
187
|
+
|
188
|
+
1. extend the `Motion::Xray::PropertyEditor` class.
|
189
|
+
2. Return your editor in the `edit_view(container_width)` method. You don't have
|
190
|
+
to use the entire width, but your editor view can't be any wider.
|
191
|
+
|
192
|
+
If you want, you can return a "preview" that just shows the value, with a
|
193
|
+
button that opens a much larger editor. `ColorEditor` and `TextEditor`
|
194
|
+
behave this way.
|
195
|
+
3. To get the value of the property being edited, use the method `get_value`. It
|
196
|
+
will introspect `self.target` looking for a the appropriate getter method.
|
197
|
+
4. Whenever the value changes, assign the new value to `set_value`, and that
|
198
|
+
will fire a `XrayTargetDidChangeNotification` notification, which is used by
|
199
|
+
`Motion::Xray::SaveUIPlugin`. `set_value` will, like `get_value`, look for an
|
200
|
+
appropriate setter.
|
201
|
+
|
202
|
+
The editors should be able to be used for many properties, but if you're writing
|
203
|
+
a one-off editor, I suppose you could call the getters and setters directly, but
|
204
|
+
you should post the `XrayTargetDidChangeNotification` notification if you do
|
205
|
+
this.
|
206
|
+
|
207
|
+
### Save UI (`Motion::Xray::SaveUIPlugin`)
|
208
|
+
|
209
|
+
After you have made your changes to your `UIView`s, you will want to save those
|
210
|
+
changes, right? This plugin is your friend. It is not included by default,
|
211
|
+
though, because not everyone uses [teacup][] or [pixate][].
|
212
|
+
|
213
|
+
Many of the properties that you'll be editing will already have the appropriate
|
214
|
+
output in this plugin (it uses `#inspect`), but the way that Xray records your
|
215
|
+
changes can be customized in two ways:
|
216
|
+
|
217
|
+
1. Change the `type` of output that you want. The default is `teacup`, but it
|
218
|
+
is possible to setup the `SaveUIPlugin` to record NUI or Pixate changes as
|
219
|
+
well.
|
220
|
+
|
221
|
+
```ruby
|
222
|
+
Motion::Xray.registerPlugin(Motion::Xray::SaveUIPlugin.new) # use teacup
|
223
|
+
Motion::Xray.registerPlugin(Motion::Xray::SaveUIPlugin.new(:pixate))
|
224
|
+
```
|
225
|
+
2. Register custom output, by class. This will be used for any property, for
|
226
|
+
instance if you want `UIColor` objects to be persisted as an array of RGB
|
227
|
+
values, you could register that output like this:
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
register(:teacup, UIColor) { |color| "[#{color.red}, #{color.blue}, #{color.green}]" }
|
231
|
+
```
|
232
|
+
|
233
|
+
Because Xray uses SugarCube, a lot of the hard work is done for us there
|
234
|
+
(because SugarCube implements lots of useful `to_s` and `inspect` methods)
|
235
|
+
|
236
|
+
### Accessibility (`Motion::Xray::AccessibilityPlugin`)
|
237
|
+
|
238
|
+
**included automatically**
|
239
|
+
|
240
|
+
This plugin provides two screenshots of the current screen. One that mimics how
|
241
|
+
a sightless person would "see" your app, and another that mimics how a (very)
|
242
|
+
color blind person would see it. Each one is *at best*, an *approximation*, but
|
243
|
+
the goal is that having this quick metric handy will encourage more developers
|
244
|
+
to spend some time on accessibility. A little goes a long way!
|
245
|
+
|
246
|
+
![Accessibility Plugin](http://media.colinta.com/xray/xray_accessibility.png)
|
247
|
+
|
248
|
+
This plugin generated a lot of excitement when I announced Xray at the
|
249
|
+
RubyMotion conference, #inspect2013. We had all heard [Austin
|
250
|
+
Seraphin's][austinseraphin] talk the previous day, about how to improve
|
251
|
+
accessibility. This plugin tries to provide a visualization of the
|
252
|
+
recommendations he gave us - first and foremost, he recommended that you should
|
253
|
+
at least set the `accessibilityLabel` on custom views.
|
254
|
+
|
255
|
+
The left side shows a screenshot of your app with only red and green squares.
|
256
|
+
Green squares mean "you're doing OK". It does NOT mean that your app has "good"
|
257
|
+
accessibility, but at a minimum you should at least get all your screens "in the
|
258
|
+
green" before you send your app to an accessibility consultant.
|
259
|
+
|
260
|
+
The other screenshot is a your app in black and white, with colors desaturated.
|
261
|
+
An attempt to mimic how a color blind person would see your app. There are
|
262
|
+
*many* types of color blindness, and down the road I would love to see a few
|
263
|
+
different screen shots for each specific type in this pane. For now, it takes
|
264
|
+
the "common denominator" approach, which is to remove *all* color.
|
265
|
+
|
266
|
+
### Log (`Motion::Xray::LogPlugin`)
|
267
|
+
|
268
|
+
**included automatically**
|
269
|
+
|
270
|
+
![Log Plugin](http://media.colinta.com/xray/xray_log.png)
|
271
|
+
|
272
|
+
This plugin requires more involvement in your application code, if you want to
|
273
|
+
make it useful. You basically need to use the `Motion::Xray::Log.log` family of methods,
|
274
|
+
and each of them will write to the `Motion::Xray::LogPlugin.log` buffer. Here's a quick
|
275
|
+
way to do this:
|
276
|
+
|
277
|
+
```ruby
|
278
|
+
Log = Motion::Xray::Log
|
279
|
+
|
280
|
+
Log.info('info!')
|
281
|
+
Log.error('an error occurred!')
|
282
|
+
# available methods:
|
283
|
+
# Log.error, Log.warning, Log.log, Log.notice, Log.info, Log.ok, Log.debug
|
284
|
+
|
285
|
+
# only log information greater than or equal to log level "warning"
|
286
|
+
Log.level = Log::Warning
|
287
|
+
```
|
288
|
+
|
289
|
+
Or you can write a log method yourself that calls one of the Motion::Xray::Log methods.
|
290
|
+
If you use CocoaLumberjack, it should be very easy to hook up `Motion::Xray::Log`, but
|
291
|
+
it will have to be done in Obj-C I think (I took a stab at it, but gave up when
|
292
|
+
I couldn't access the `message` property).
|
293
|
+
|
294
|
+
The upside to using these `Motion::Xray::Log` methods is that they use pretty coloring,
|
295
|
+
they output to both the console *and* the Xray log, and I'm planning on
|
296
|
+
including some [awesome-print][]-like features to the log methods in the future
|
297
|
+
(or, more likely, delegate to awesome-print if it's available).
|
298
|
+
|
299
|
+
Writing an Xray plugin
|
300
|
+
----------------
|
301
|
+
|
302
|
+
My hope is that you will identify places in your app where you would benefit
|
303
|
+
from on-device feedback. Here are just some ideas as examples:
|
304
|
+
|
305
|
+
1. **Building an app that interacts with bluetooth devices:** How about
|
306
|
+
signal strength? Devices detected? Connect and disconnect buttons?
|
307
|
+
2. **Interfacing with an API:** Logging requests, logging parameters sent and
|
308
|
+
responses, interface to send arbitrary requests
|
309
|
+
3. **Building a game:** framerate, number of textures on screen. To find out
|
310
|
+
when the performance breaks down on the device, you can't trust the
|
311
|
+
simulator!
|
312
|
+
|
313
|
+
So, let's get to it. I will use some code from `AccessibilityPlugin` in this
|
314
|
+
example.
|
315
|
+
|
316
|
+
First, the most basic plugin structure:
|
317
|
+
|
318
|
+
```ruby
|
319
|
+
class AccessibilityPlugin < Plugin
|
320
|
+
name 'Accessibility' # as you want it to appear in the toolbar
|
321
|
+
|
322
|
+
# canvas is the view where the plugin will be placed. You do not need to
|
323
|
+
# call `addSubview` on this object.
|
324
|
+
def plugin_view(canvas)
|
325
|
+
return UIView.initWithFrame(canvas.bounds)
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
329
|
+
```
|
330
|
+
|
331
|
+
So far we have:
|
332
|
+
|
333
|
+
- named our plugin 'Accessibility'
|
334
|
+
- returned an empty container
|
335
|
+
|
336
|
+
Let's add our two image views. We'll make use of geomotion, which is required
|
337
|
+
by Xray:
|
338
|
+
|
339
|
+
```ruby
|
340
|
+
def plugin_view(canvas)
|
341
|
+
return UIView.alloc.initWithFrame(canvas.bounds).tap do |view|
|
342
|
+
view.backgroundColor = :black.uicolor
|
343
|
+
|
344
|
+
@accessibility = UIButton.alloc.initWithFrame(view.bounds
|
345
|
+
.thinner(view.bounds.width / 2))
|
346
|
+
@colorblind = UIButton.alloc.initWithFrame(view.bounds
|
347
|
+
.thinner(view.bounds.width / 2)
|
348
|
+
.right(view.bounds.width / 2))
|
349
|
+
|
350
|
+
view << @accessibility
|
351
|
+
view << @colorblind
|
352
|
+
end
|
353
|
+
end
|
354
|
+
```
|
355
|
+
|
356
|
+
When the plugin is activated, we should grab a screenshot of the app and assign
|
357
|
+
it to each view. The `show` method is called on a plugin when it is selected.
|
358
|
+
|
359
|
+
```ruby
|
360
|
+
def show
|
361
|
+
Dispatch::Queue.main.async do
|
362
|
+
@colorblind.setImage(get_colorblind_image, forState: :normal.uicontrolstate)
|
363
|
+
end
|
364
|
+
Dispatch::Queue.main.async do
|
365
|
+
@accessibility.setImage(get_accessibility_image, forState: :normal.uicontrolstate)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
```
|
369
|
+
|
370
|
+
The `AccessibilityPlugin` does a few more things like show spinners, display a
|
371
|
+
big screenshot image on touch, and I haven't implemented the
|
372
|
+
`get_{accessibility,colorblind}_image` methods here, but hopefully this is
|
373
|
+
enough for you to get the gist of writing a plugin. Here is the entire list of
|
374
|
+
methods that you can call, or get called, on a plugin:
|
375
|
+
|
376
|
+
#### Properties
|
377
|
+
|
378
|
+
- `name` - the name as it appears in the toolbar
|
379
|
+
- `view` - stores the plugin view that is returned by `plugin_view`. This
|
380
|
+
method is only created *once* (much like `UIViewController#loadView`)
|
381
|
+
- `target` - the view that has been selected in the UI picker
|
382
|
+
|
383
|
+
#### Methods you must implement
|
384
|
+
|
385
|
+
- `plugin_view(canvas_view)` - the view returned by this method will be placed
|
386
|
+
in `canvas_view` when your plugin is selected
|
387
|
+
- `edit(target)` - called when a new view is double-tapped in the UI picker.
|
388
|
+
You should call `super`, which assigns this view to the `target` property.
|
389
|
+
Then you can update `self.view` with any changes that you need to apply.
|
390
|
+
- `show` - called when your plugin is selected (this will always be after
|
391
|
+
`edit(target)`)
|
392
|
+
- `hide` - called just before your plugin is removed from the canvas
|
393
|
+
|
394
|
+
#### Registering your plugin
|
395
|
+
|
396
|
+
Register your new plugin in the
|
397
|
+
`AppDelegate#application(didFinishLaunchingWithOptions:)` method.
|
398
|
+
|
399
|
+
```ruby
|
400
|
+
class AppDelegate
|
401
|
+
def application(application, didFinishLaunchingWithOptions:launchOptions)
|
402
|
+
@window = Motion::Xray::XrayWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
|
403
|
+
@window.makeKeyAndVisible
|
404
|
+
|
405
|
+
# include the SaveUIPlugin, which is not included by default
|
406
|
+
Motion::Xray.register(Motion::Xray::SaveUIPlugin.new)
|
407
|
+
|
408
|
+
# include a custom plugin
|
409
|
+
Motion::Xray.register(CustomPlugin.new)
|
410
|
+
|
411
|
+
return true
|
412
|
+
end
|
413
|
+
end
|
414
|
+
```
|
415
|
+
|
416
|
+
Dependencies
|
417
|
+
------------
|
418
|
+
|
419
|
+
Xray depends on geomotion, which I don't feel bad about, and SugarCube. I would
|
420
|
+
consider removing the SugarCube dependency, because not everyone uses it, but
|
421
|
+
SugarCube adds a ton of benefit (like `#to_s` and `UIColor` additions).
|
422
|
+
|
423
|
+
[plugins folder]: https://github.com/colinta/motion-xray/tree/master
|
424
|
+
[awesome-print]: https://github.com/michaeldv/awesome_print_motion
|
425
|
+
[teacup]: https://github.com/rubymotion/teacup
|
426
|
+
[pixate]: http://www.pixate.com
|