Simulator ですでに動作している iOS アプリの view をちょっといじりたい時に便利な手段を紹介します。
手順
アプリを一時停止する
ます、メニューの Debug > Pause か、デバッグバーの Pause ボタンでアプリケーションを一時停止させます。

UIApplication
が見えるスタックへ移動する
画面操作慣れしている方向け
Debug Navigator の Process View Display から、Thread 1 の main をクリックします。
LLDBer 向け
main thread を選択
(lldb) thread list
Process 16234 stopped
* thread #1: tid = 0x5345c, 0x000000010b1b4f72 libsystem_kernel.dylib`mach_msg_trap + 10, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
thread #4: tid = 0x53536, 0x000000010b1bbefa libsystem_kernel.dylib`kevent_qos + 10, queue = 'com.apple.libdispatch-manager'
thread #6: tid = 0x53538, 0x000000010b1b4f72 libsystem_kernel.dylib`mach_msg_trap + 10, name = 'com.apple.uikit.eventfetch-thread'
(lldb) thread select 1
main 関数の frame を選択
(lldb) thread backtrace
* thread #1: tid = 0x5345c, 0x000000010b1b4f72 libsystem_kernel.dylib`mach_msg_trap + 10, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x000000010b1b4f72 libsystem_kernel.dylib`mach_msg_trap + 10
frame #1: 0x000000010b1b445e libsystem_kernel.dylib`mach_msg + 226
frame #2: 0x0000000109e69ba4 CoreFoundation`__CFRunLoopServiceMachPort + 212
frame #3: 0x0000000109e69021 CoreFoundation`__CFRunLoopRun + 1345
frame #4: 0x0000000109e68884 CoreFoundation`CFRunLoopRunSpecific + 420
frame #5: 0x000000010be19a6f GraphicsServices`GSEventRunModal + 161
frame #6: 0x00000001076c1c68 UIKit`UIApplicationMain + 159
* frame #7: 0x0000000106bf614f TableViewTraining`main + 111 at AppDelegate.swift:12
frame #8: 0x000000010ae8f68d libdyld.dylib`start + 1
frame #9: 0x000000010ae8f68d libdyld.dylib`start + 1
(lldb) frame select 7
frame #7: 0x0000000106bf614f TableViewTraining`main + 111 at AppDelegate.swift:12
9 import UIKit
10
11 @UIApplicationMain
-> 12 class AppDelegate: UIResponder, UIApplicationDelegate {
13
14 var window: UIWindow?
15
View の一覧を確認する
Console から LLDB を実行する
デバッグコンソールから、Xcode に組み込まれているデバッガ LLDB を実行します。
LLDB を使うと、コマンドラインから様々なデバッグ操作ができます。試しに help
と入力すると、次のようにヘルプが表示されます:
(lldb) help
Debugger commands:
apropos -- List debugger commands related to a word or subject.
breakpoint -- Commands for operating on breakpoints (see 'help b' for
...
ここからは、LLDB の expression
コマンドを使って、View の一覧を確認していきます。なお、-O
も一緒に指定しますが、これは人間の読みやすい形式で表示するオプションです1。
UIApplication
は、アプリが起動している間存在するシングルトンなクラスです。このクラスのインスタンスから、現在表示されている View までたどることができます:
(lldb) expression -O -- UIApplication.shared
<UIApplication: 0x7f9772d00d40>
UIApplication
は、現在有効な UIWindow
を UIApplication#keyWIndow
に持っています:
(lldb) expression -O -- UIApplication.shared.keyWindow!
<UIWindow: 0x7f977660c400; frame = (0 0; 414 736); gestureRecognizers = <NSArray: 0x6000000556c0>; layer = <UIWindowLayer: 0x6000002210e0>>
この UIWindow
は、UIWindow#rootViewController
に ViewController
を持っています:
(lldb) expression -O -- UIApplication.shared.keyWindow!.rootViewController!
<UINavigationController: 0x7f9774031800>
この UIViewController#view
から、UIView
を入手できます:
(lldb) expression -O -- UIApplication.shared.keyWindow!.rootViewController!.view!
<UILayoutContainerView: 0x7f9772d09a30; frame = (0 0; 414 736); autoresize = W+H; gestureRecognizers = <NSArray: 0x600000056c80>; layer = <CALayer: 0x618000221060>>
さらに、この UIView
の子孫 View を追いかけてみましょう。UIView
は、recursiveDescription
というデバッグ用のメソッドを備えています。ただ、Swift からは直接呼び出せないので、UIView
の親クラスであるNSObject#value(forKey:)
経由で呼び出す必要があります:
(lldb) expression -O -- UIApplication.shared.keyWindow!.rootViewController!.view!.value(forKey: "recursiveDescription")!
<UILayoutContainerView: 0x7f9772d09a30; frame = (0 0; 414 736); autoresize = W+H; gestureRecognizers = <NSArray: 0x600000056c80>; layer = <CALayer: 0x618000221060>>
| <UINavigationTransitionView: 0x7f9776607490; frame = (0 0; 414 736); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x600000221b40>>
| | <UIViewControllerWrapperView: 0x7f9772f0b4a0; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x610000220c80>>
| | | <UITableView: 0x7f9773007a00; frame = (0 0; 414 736); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x6080000577c0>; layer = <CALayer: 0x608000222d20>; contentOffset: {0, -64}; contentSize: {414, 270}>
| | | | <UITableViewWrapperView: 0x7f9773007400; frame = (0 0; 414 736); gestureRecognizers = <NSArray: 0x608000057af0>; layer = <CALayer: 0x608000222e00>; contentOffset: {0, 0}; contentSize: {414, 736}>
| | | | | <TableViewTraining.MealTableViewCell: 0x7f977486b000; baseClass = UITableViewCell; frame = (0 180; 414 90); clipsToBounds = YES; autoresize = W; layer = <CALayer: 0x6000002215a0>>
| | | | | | <UITableViewCellContentView: 0x7f977660ca50; frame = (0 0; 414 89.6667); opaque = NO; gestureRecognizers = <NSArray: 0x600000058c00>; layer = <CALayer: 0x600000221ac0>>
| | | | | | | <UIStackView: 0x7f9776609ab0; frame = (0 0; 414 89.6667); opaque = NO; autoresize = RM+BM; layer = <CATransformLayer: 0x600000221e60>>
| | | | | | | | <UIImageView: 0x7f97766056c0; frame = (0 0; 90 89.6667); clipsToBounds = YES; autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x600000221c00>>
| | | | | | | | <UIStackView: 0x7f9776604dd0; frame = (100 0; 314 89.6667); opaque = NO; autoresize = RM+BM; layer = <CATransformLayer: 0x600000221ce0>>
| | | | | | | | | <UILabel: 0x7f9776611b90; frame = (0 0; 314 20.3333); text = 'Kuniwak3'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60000008f910>>
| | | | | | | | | <UIView: 0x7f977660ceb0; frame = (0 30.3333; 314 59.3333); autoresize = RM+BM; layer = <CALayer: 0x6000002226a0>>
| | | | | | | | | | <TableViewTraining.RatingControl: 0x7f97766123f0; frame = (37 7.66667; 240 44); autoresize = RM+BM; layer = <CALayer: 0x600000222700>>
| | | | | | | | | | | <UIButton: 0x7f97766125b0; frame = (0 0; 44 44); opaque = NO; layer = <CALayer: 0x600000222a40>>
| | | | | | | | | | | | <UIImageView: 0x7f9776614340; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600000223b20>>
| | | | | | | | | | | <UIButton: 0x7f97766128b0; frame = (49 0; 44 44); opaque = NO; layer = <CALayer: 0x600000222b00>>
| | | | | | | | | | | | <UIImageView: 0x7f9776614520; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600000223c20>>
| | | | | | | | | | | <UIButton: 0x7f9776612b70; frame = (98 0; 44 44); opaque = NO; layer = <CALayer: 0x600000222b60>>
| | | | | | | | | | | | <UIImageView: 0x7f9776614700; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600000223cc0>>
| | | | | | | | | | | <UIButton: 0x7f9776612e30; frame = (147 0; 44 44); opaque = NO; layer = <CALayer: 0x600000222c00>>
| | | | | | | | | | | | <UIImageView: 0x7f97766148e0; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600000223d60>>
| | | | | | | | | | | <UIButton: 0x7f97766130f0; frame = (196 0; 44 44); opaque = NO; layer = <CALayer: 0x600000222ca0>>
| | | | | | | | | | | | <UIImageView: 0x7f9776614ac0; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600000223e00>>
| | | | | | <_UITableViewCellSeparatorView: 0x7f97766133b0; frame = (15 89.5; 399 0.5); layer = <CALayer: 0x600000223000>>
| | | | | | <_UITableViewCellSeparatorView: 0x7f9776614060; frame = (20 89.6667; 394 0.333333); layer = <CALayer: 0x600000221b20>>
| | | | | <TableViewTraining.MealTableViewCell: 0x7f977503ba00; baseClass = UITableViewCell; frame = (0 90; 414 90); clipsToBounds = YES; autoresize = W; layer = <CALayer: 0x610000220f60>>
| | | | | | <UITableViewCellContentView: 0x7f9772f08dc0; frame = (0 0; 414 89.6667); opaque = NO; gestureRecognizers = <NSArray: 0x60800005b3f0>; layer = <CALayer: 0x610000221fc0>>
| | | | | | | <UIStackView: 0x7f9772f0f520; frame = (0 0; 414 89.6667); opaque = NO; autoresize = RM+BM; layer = <CATransformLayer: 0x610000221300>>
| | | | | | | | <UIImageView: 0x7f9772f0f6e0; frame = (0 0; 90 89.6667); clipsToBounds = YES; autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x610000221000>>
| | | | | | | | <UIStackView: 0x7f9772f0f8c0; frame = (100 0; 314 89.6667); opaque = NO; autoresize = RM+BM; layer = <CATransformLayer: 0x610000221180>>
| | | | | | | | | <UILabel: 0x7f9772f0fa80; frame = (0 0; 314 20.3333); text = 'Kuniwak2'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x61000008feb0>>
| | | | | | | | | <UIView: 0x7f9772c10be0; frame = (0 30.3333; 314 59.3333); autoresize = RM+BM; layer = <CALayer: 0x608000225340>>
| | | | | | | | | | <TableViewTraining.RatingControl: 0x7f9772c12b00; frame = (37 7.66667; 240 44); autoresize = RM+BM; layer = <CALayer: 0x608000225280>>
| | | | | | | | | | | <UIButton: 0x7f9772c12780; frame = (0 0; 44 44); opaque = NO; layer = <CALayer: 0x608000225240>>
| | | | | | | | | | | | <UIImageView: 0x7f9772c15510; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x608000226600>>
| | | | | | | | | | | <UIButton: 0x7f9772c12dc0; frame = (49 0; 44 44); opaque = NO; layer = <CALayer: 0x6080002253e0>>
| | | | | | | | | | | | <UIImageView: 0x7f9772c159d0; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x608000226700>>
| | | | | | | | | | | <UIButton: 0x7f9772c13080; frame = (98 0; 44 44); opaque = NO; layer = <CALayer: 0x608000225480>>
| | | | | | | | | | | | <UIImageView: 0x7f9772c15bb0; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6080002267a0>>
| | | | | | | | | | | <UIButton: 0x7f9772c13340; frame = (147 0; 44 44); opaque = NO; layer = <CALayer: 0x608000225520>>
| | | | | | | | | | | | <UIImageView: 0x7f9772c15d90; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x608000226840>>
| | | | | | | | | | | <UIButton: 0x7f9772c13600; frame = (196 0; 44 44); opaque = NO; layer = <CALayer: 0x6080002255c0>>
| | | | | | | | | | | | <UIImageView: 0x7f9772c15f70; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6080002268e0>>
| | | | | | <_UITableViewCellSeparatorView: 0x7f9772c13d20; frame = (15 89.5; 399 0.5); layer = <CALayer: 0x608000225920>>
| | | | | | <_UITableViewCellSeparatorView: 0x7f9772c15230; frame = (20 89.6667; 394 0.333333); layer = <CALayer: 0x608000225260>>
| | | | | <TableViewTraining.MealTableViewCell: 0x7f9774812a00; baseClass = UITableViewCell; frame = (0 0; 414 90); clipsToBounds = YES; autoresize = W; layer = <CALayer: 0x61800003fe60>>
| | | | | | <UITableViewCellContentView: 0x7f9772d02e20; frame = (0 0; 414 89.6667); opaque = NO; gestureRecognizers = <NSArray: 0x608000057070>; layer = <CALayer: 0x618000422700>>
| | | | | | | <UIStackView: 0x7f9772d093a0; frame = (0 0; 414 89.6667); opaque = NO; autoresize = RM+BM; layer = <CATransformLayer: 0x618000422740>>
| | | | | | | | <UIImageView: 0x7f9772d0b760; frame = (0 0; 90 89.6667); clipsToBounds = YES; autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x618000422760>>
| | | | | | | | <UIStackView: 0x7f9772d0c070; frame = (100 0; 314 89.6667); opaque = NO; autoresize = RM+BM; layer = <CATransformLayer: 0x618000422880>>
| | | | | | | | | <UILabel: 0x7f9772d0c440; frame = (0 0; 314 20.3333); text = 'Kuniwak1'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x61800008d7f0>>
| | | | | | | | | <UIView: 0x7f9772f0d630; frame = (0 30.3333; 314 59.3333); autoresize = RM+BM; layer = <CALayer: 0x610000220b00>>
| | | | | | | | | | <TableViewTraining.RatingControl: 0x7f9772f0d7d0; frame = (37 7.66667; 240 44); autoresize = RM+BM; layer = <CALayer: 0x6100002237c0>>
| | | | | | | | | | | <UIButton: 0x7f9772c09280; frame = (0 0; 44 44); opaque = NO; layer = <CALayer: 0x608000222b20>>
| | | | | | | | | | | | <UIImageView: 0x7f9772c10260; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x608000224460>>
| | | | | | | | | | | <UIButton: 0x7f9772d0cbf0; frame = (49 0; 44 44); opaque = NO; layer = <CALayer: 0x618000422bc0>>
| | | | | | | | | | | | <UIImageView: 0x7f9772c0fab0; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x608000224560>>
| | | | | | | | | | | <UIButton: 0x7f9772d0d1c0; frame = (98 0; 44 44); opaque = NO; layer = <CALayer: 0x618000422a80>>
| | | | | | | | | | | | <UIImageView: 0x7f9772c10440; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x608000224600>>
| | | | | | | | | | | <UIButton: 0x7f9772d0d480; frame = (147 0; 44 44); opaque = NO; layer = <CALayer: 0x618000422ba0>>
| | | | | | | | | | | | <UIImageView: 0x7f9772c10620; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6080002246a0>>
| | | | | | | | | | | <UIButton: 0x7f9772d0d740; frame = (196 0; 44 44); opaque = NO; layer = <CALayer: 0x618000422c40>>
| | | | | | | | | | | | <UIImageView: 0x7f9772c10800; frame = (0 0; 44 44); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x608000224740>>
| | | | | | <_UITableViewCellSeparatorView: 0x7f9772d0eb30; frame = (15 89.5; 399 0.5); layer = <CALayer: 0x618000422fc0>>
| | | | | | <_UITableViewCellSeparatorView: 0x7f9772c0e7f0; frame = (20 89.6667; 394 0.333333); layer = <CALayer: 0x6080002235c0>>
| | | | <UIImageView: 0x7f9772f080b0; frame = (3 666.667; 408 2.33333); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x610000222580>>
| | | | <_UITableViewCellSeparatorView: 0x7f9772f0a880; frame = (20 359.667; 394 0.333333); autoresize = W; layer = <CALayer: 0x610000220d00>>
| | | | <_UITableViewCellSeparatorView: 0x7f9772f09d40; frame = (20 449.667; 394 0.333333); autoresize = W; layer = <CALayer: 0x610000220e40>>
| | | | <_UITableViewCellSeparatorView: 0x7f9772f09610; frame = (20 539.667; 394 0.333333); autoresize = W; layer = <CALayer: 0x610000220f80>>
| | | | <_UITableViewCellSeparatorView: 0x7f9772f0fd10; frame = (20 629.667; 394 0.333333); autoresize = W; layer = <CALayer: 0x610000221a80>>
| | | | <_UITableViewCellSeparatorView: 0x7f9772f0fed0; frame = (20 719.667; 394 0.333333); autoresize = W; layer = <CALayer: 0x610000220ee0>>
| | | | <_UITableViewCellSeparatorView: 0x7f9772f10190; frame = (20 809.667; 394 0.333333); autoresize = W; layer = <CALayer: 0x6100002216c0>>
| | | | <_UITableViewCellSeparatorView: 0x7f9772f10350; frame = (20 899.667; 394 0.333333); autoresize = W; layer = <CALayer: 0x6100002213c0>>
| | | | <_UITableViewCellSeparatorView: 0x7f9772f10510; frame = (20 989.667; 394 0.333333); autoresize = W; layer = <CALayer: 0x610000221e40>>
| | | | <_UITableViewCellSeparatorView: 0x7f9772f106d0; frame = (20 1079.67; 394 0.333333); autoresize = W; layer = <CALayer: 0x6100002222a0>>
| | | | <_UITableViewCellSeparatorView: 0x7f9772f10890; frame = (20 1169.67; 394 0.333333); autoresize = W; layer = <CALayer: 0x610000222940>>
| | | | <UIImageView: 0x7f9772f0c940; frame = (408.667 405; 2.33333 264); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x610000221440>>
| <UINavigationBar: 0x7f9772f08510; frame = (0 20; 414 44); opaque = NO; autoresize = W; gestureRecognizers = <NSArray: 0x6000000560e0>; layer = <CALayer: 0x610000220d40>>
| | <_UIBarBackground: 0x7f9772f0a670; frame = (0 -20; 414 64); userInteractionEnabled = NO; layer = <CALayer: 0x610000222aa0>>
| | | <UIImageView: 0x7f9772f0ac70; frame = (0 64; 414 0.333333); userInteractionEnabled = NO; layer = <CALayer: 0x610000222100>>
| | | <UIVisualEffectView: 0x7f9772f0ae50; frame = (0 0; 414 64); layer = <CALayer: 0x610000222b00>>
| | | | <_UIVisualEffectBackdropView: 0x7f977660a010; frame = (0 0; 414 64); autoresize = W+H; userInteractionEnabled = NO; layer = <UICABackdropLayer: 0x600000221ec0>>
| | | | <_UIVisualEffectFilterView: 0x7f9772d0a000; frame = (0 0; 414 64); autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x618000221080>>
| | <<UINavigationItemView: 0x7f9772f08be0; frame = (163 8; 88 27); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x610000220ba0>>: item=<<UINavigationItem: 0x6000001de5a0>: title:'Your Meals'> title=Your Meals>
| | | <UILabel: 0x7f9772f09380; frame = (0 3; 88 21.3333); text = 'Your Meals'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x61000008dc50>>
| | <UINavigationButton: 0x7f9776608890; frame = (366 6; 39 30); opaque = NO; layer = <CALayer: 0x600000221340>>
| | | <UIImageView: 0x7f9772f089d0; frame = (11 6.33333; 17 17); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x610000221fe0>>
| | <_UINavigationBarBackIndicatorView: 0x7f9772d0a5d0; frame = (12 11.6667; 13 21); alpha = 0; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x618000422900>>
操作する UIView
を入手する
先ほど入手した UIView
の UIView#subviews
から辿って行ってもよいですが、アドレスとクラスがわかっていれば、簡単にインスタンスを入手できます。
例えば、下の UIImageView
インスタンスを操作したいとしましょう:
<UIImageView: 0x7f9772f0ac70; frame = (0 64; 414 0.333333); userInteractionEnabled = NO; layer = <CALayer: 0x610000222100>>
アドレスとは、0x7f9772f0ac70
の部分のことです。これとクラスを使うと、次のように unsafeBitCast(_:to:)
を使ってインスタンスを取得できます:
(lldb) expression -O -- unsafeBitCast(0x7f9772f0ac70, to: UIImageView.self)
<UIImageView: 0x7f9772f0ac70; frame = (0 64; 414 0.333333); userInteractionEnabled = NO; layer = <CALayer: 0x610000222100>>
入手した UIView
を料理する
ここまで出来れば、UIView
をいかようにでも料理できます。
例えば、ある UIImageView
の画像を消すには、次のようにします:
(lldb) expression -O -- unsafeBitCast(0x7f97766056c0, to: UIImageView.self).image = nil
(lldb) continue
Before

After

まとめ
Simulator で実行中の iOS アプリケーションの任意の UIView
を操作する方法として、次の2つのコマンドを紹介しました。
(lldb) expression -O -- UIApplication.shared.keyWindow!.rootViewController!.view!.value(forKey: "recursiveDescription")!
(lldb) expression -O -- unsafeBitCast(0x7f9772f0ac70, to: UIImageView.self)
Enjoy, debugging!
参考文献
- Debugging with Xcode - Guides and Sample Code
- iOS Debugging Magic - Guides and Sample Code
- Reverse-Engineering iOS Apps: Hacking on Lyft
(Conrad Kramer) - realm
-
expression -O -- hoge
はpo hoge
というエイリアスでも実行できます。 ↩