-
Notifications
You must be signed in to change notification settings - Fork 66
REPL Additions
Examine your views, examine your layers, examine your controllers, your sprite kit worlds, your application menus - and any tree structure you create yourself! SugarCube::Repl is a powerful debugging and development ally.
To really be handy you'll want to require the sugarcube-repl
package, which makes the SugarCube::Repl
methods available in the REPL (it does this by adding methods to Kernel
that call the corresponding method in SugarCube::Repl
).
This is often touted as the single most useful feature of SugarCube!
(main)> tree
0: . UIWindow(#6e1f950: [[0.0, 0.0], [320.0, 480.0]])
1: `-- UIView(#8b203b0: [[0.0, 20.0], [320.0, 460.0]])
2: +-- UIButton(#d028de0: [[10.0, 10.0], [320.0, 463.400512695312]])
3: | `-- UIImageView(#d02aaa0: [[0.0, 0.0], [320.0, 463.400512695312]])
4: +-- UIRoundedRectButton(#d02adb0: [[55.0, 110.0], [210.0, 20.0]])
5: | `-- UIButtonLabel(#d02af00: [[73.0, 0.0], [63.0, 19.0]], text: "Button 1")
6: +-- UIRoundedRectButton(#d028550: [[60.0, 30.0], [200.0, 20.0]])
7: | `-- UIButtonLabel(#d02afb0: [[68.0, 0.0], [63.0, 19.0]], text: "Button 2")
8: `-- UIRoundedRectButton(#d02b220: [[70.0, 30.0], [300.0, 20.0]])
9: `-- UIButtonLabel(#d02b300: [[118.0, 0.0], [63.0, 19.0]], text: "Button 3")
The SugarCube to_s
package provides lots of to_s
methods on UIKit objects - that makes this tree view really useful to find the view you want. Once you do find the one you want, you can fetch it out of that list using the adjust
method, which is aliased to a
to make it easy on the fingers.
(main)> a 6
=> UIRoundedRectButton(#d028550: [[60.0, 30.0], [200.0, 20.0]]), child of UIView(#8b203b0)
Now that we've chosen the button, it is available in the a
method, and there are a bunch of methods in the SugarCube::Repl module functions that act on that object. Most of these methods help you adjust the frame of a view.
> up 1
> down 1 # same as `up -1`
> down # defaults to 1
> left 10
> right 10
> left # => left 1
> origin 10, 12 # move to x:10, y:12
> wider 15
> thinner 10
> taller # => taller 1
> shorter # => shorter 1
> size 100, 10 # set size to width:100, height: 10
> shadow(opacity: 0.5, offset: [0, 0], color: :black, radius: 1) # also supports path, which is a CGPath object.
> center # See `Centering` section below
> restore # original frame and shadow is saved when you first call `adjust`
Here are the short versions of those methods.
> u # up, default value=1
> d # down
> l # left
> r # right
> w # wider
> n # thiNNer
> t # taller
> s # shorter
> o 10, 12 # origin
> o [10, 12]
> o CGPoint.new(10, 12)
> z 100, 10 # siZe, also accepts an array or CGSize
# and frame
> f [[0,0], [0,0]]
# sHadow
> h opacity: 0.5, offset: [0, 0], color: :black, radius: 1
# frame, size, origin, and shadow can also be used as getters
> f
[[0, 0], [320, 568]]
> o # origin
[0, 0]
> z # size
[320, 568]
> h # this returns an object identical to what you can pass to `shadow`
{opacity: 0.5, offset: [0, 0], color: :black, radius: 1}
# and of course the `a` method returns the current object
> a
=> UITextField(#9ce6470, [[46, 214], [280, 33]], text: "hi!"), child of UIView(#10a6da20)
It is called as center(which_index, of_total_number, direction)
. The order can be changed, and all the arguments are optional. Default values are center(1, 1, 'h')
(center the item horizontally).
You can set 'direction' using a string or symbol: 'horiz', 'vert', 'x', even 'x and y'. The method searches for the letters [xyhv]
.
Here are a few examples:
(main)> center
[[145.0, 30.0], [30.0, 200.0]]
UIRoundedRectButton.origin = [145.0, 30.0]
=> "[[145.0, 30.0], [30.0, 200.0]]"
In order to place that same button in the center of the screen - horizontally and vertically - you can use this shorthand syntax:
center :xy
If you have three buttons and want them spaced evenly (vertically) across their parent frame, you can accomplish that this way:
(main)> tree
0: . UIWindow(#6e1f950: [[0.0, 0.0], [320.0, 480.0]])
1: `-- UIView(#8b203b0: [[0.0, 20.0], [320.0, 460.0]])
2: +-- UIButton(#d028de0: [[10.0, 10.0], [320.0, 464]])
3: | `-- UIImageView(#d02aaa0: [[0.0, 0.0], [320.0, 464]])
4: +-- UIRoundedRectButton(#d02adb0: [[55.0, 110.0], [210.0, 20.0]], text: "Button 1")
5: | `-- UIButtonLabel(#d02af00: [[73.0, 0.0], [63.0, 19.0]])
6: +-- UIRoundedRectButton(#d028550: [[60.0, 30.0], [200.0, 20.0]], text: "Button 2")
7: | `-- UIButtonLabel(#d02afb0: [[68.0, 0.0], [63.0, 19.0]])
8: `-- UIRoundedRectButton(#d02b220: [[70.0, 30.0], [300.0, 20.0]], text: "Button 3")
9: `-- UIButtonLabel(#d02b300: [[118.0, 0.0], [63.0, 19.0]])
=> UIWindow(#6e1f950, [[0.0, 0.0], [320.0, 480.0]])
# grab the first button, and center it vertically. It is the first of three buttons
(main)> a 4; center 1, 3, :vert; center
[[55.0, 110.0], [210.0, 20.0]]
UIRoundedRectButton.origin = [55.0, 110.0]
=> "[[55.0, 110.0], [210.0, 20.0]]"
# grab the second button. The first parameter changes to `2`, because this
# button is in the second position.
(main)> a 6; center 2, 3, :vert; center
[[60.0, 220.0], [200.0, 20.0]]
UIRoundedRectButton.origin = [60.0, 220.0]
=> "[[60.0, 220.0], [200.0, 20.0]]"
# grab the third button and place it in the third position
(main)> a 8; center 3, 3, :vert; center
[[10.0, 330.0], [300.0, 20.0]]
UIRoundedRectButton.origin = [10.0, 330.0]
=> "[[10.0, 330.0], [300.0, 20.0]]"
The calculated positions (x,y) are in the REPL output
You can analyze other hierarchies, too! See below to add selectors for your own data structures.
-
UIView
, obviously -
UIViewController
will show presented modal controllers, too. -
CALayer
is similar to the UIView output -
SKNode
for your SpriteKit games -
NSMenu, NSMenuItem
on OS X -
NSWindow, NSWindowController, NSViewController
these will all output the view hierarchy (OS X doesn't have a controller hierarchy, unfortunately)
There's even a handy root
method to grab the rootViewController
:
(main)> tree root
0: . #<MainScreenController:0xac23b80>
1: +-- #<ScheduleViewController:0x13185d00>
2: | +-- #<ScheduleTableController:0x131862f0>
3: | `-- #<ScheduleCalendarController:0x131bba90>
4: +-- #<CameraViewController:0x13191380>
5: +-- #<UINavigationController:0xac01ea0>
6: | `-- #<UITableViewController:0xac04e30>
7: +-- #<PicturesViewController:0x1403ede0>
8: `-- #<MessagesViewController:0x131a1bc0>
=> #<MainScreenController:0xac23b80>
If you have a tree structure and you want to output it using tree
, you can do so by passing either a method name (that should return an array) or a block. The block will be passed the current 'root' object, and should return its children.
class Foo
attr_accessor :children
end
(main)> foo = Foo.new
(main)> foo.children = [Foo.new,Foo.new,Foo.new]
(main)> tree foo, :children
(main)> tree foo, :children
0: . #<Foo:0x12d6e0d0>
1: +-- #<Foo:0x114146c0>
2: +-- #<Foo:0x114149d0>
3: `-- #<Foo:0x114149e0>
=> #<Foo:0x12d6e0d0 @children=[#<Foo:0x114146c0>, #<Foo:0x114149d0>, #<Foo:0x114149e0>]>
(main)> tree(foo) { |f| f.children }
0: . #<Foo:0x12d6e0d0>
1: +-- #<Foo:0x114146c0>
2: +-- #<Foo:0x114149d0>
3: `-- #<Foo:0x114149e0>
=> #<Foo:0x12d6e0d0 @children=[#<Foo:0x114146c0>, #<Foo:0x114149d0>, #<Foo:0x114149e0>]>
If you find yourself doing this a lot, consider registering your selector in the app delegate:
(main)> SugarCube::Repl.register_tree_selector(Foo, :children)
# or > SugarCube::Repl.register_tree_selector(Foo) { |f| f.children }
(main)> tree(foo)
0: . #<Foo:0x12d6e0d0>
1: +-- #<Foo:0x114146c0>
2: +-- #<Foo:0x114149d0>
3: `-- #<Foo:0x114149e0>
=> #<Foo:0x12d6e0d0 @children=[#<Foo:0x114146c0>, #<Foo:0x114149d0>, #<Foo:0x114149e0>]>
If you are debugging a SpriteKit game, you might want to have tree
output your node hierarchy, since tree(window)
will be pretty boring. Easy:
(main)> SugarCube::Repl.set_default(@spritekit_scene)
Better yet, place this in the view or controller where you create your scene(s), and tree
will always output your game info.