appjam 0.1.8.9 → 0.1.8.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/appjam/generators/templates/blank/EiffelApplication.xcodeproj/project.pbxproj +114 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication.xcodeproj/project.xcworkspace/xcuserdata/eiffel.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/AppDelegate.m.tt +13 -1
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/MyLauncher/CustomBadge.h +52 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/MyLauncher/CustomBadge.m +241 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/MyLauncher/MyLauncherItem.h +58 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/MyLauncher/MyLauncherItem.m +227 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/MyLauncher/MyLauncherPageControl.h +37 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/MyLauncher/MyLauncherPageControl.m +117 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/MyLauncher/MyLauncherScrollView.h +23 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/MyLauncher/MyLauncherScrollView.m +42 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/MyLauncher/MyLauncherView.h +70 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/MyLauncher/MyLauncherView.m +830 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/MyLauncher/MyLauncherViewController.h +38 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/MyLauncher/MyLauncherViewController.m +302 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/AESCrypt-ObjC/AESCrypt.h +37 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/AESCrypt-ObjC/AESCrypt.m +50 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/AESCrypt-ObjC/NSData+Base64.h +17 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/AESCrypt-ObjC/NSData+Base64.m +110 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/AESCrypt-ObjC/NSData+CommonCrypto.h +112 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/AESCrypt-ObjC/NSData+CommonCrypto.m +546 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/AESCrypt-ObjC/NSString+Base64.h +15 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/AESCrypt-ObjC/NSString+Base64.m +82 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/DCIntrospect/DCCrossHairView.h +16 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/DCIntrospect/DCCrossHairView.m +43 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/DCIntrospect/DCFrameView.h +55 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/DCIntrospect/DCFrameView.m +234 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/DCIntrospect/DCIntrospect.h +155 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/DCIntrospect/DCIntrospect.m +1654 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/DCIntrospect/DCIntrospectSettings.h +56 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/DCIntrospect/DCStatusBarOverlay.h +34 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/DCIntrospect/DCStatusBarOverlay.m +107 -0
- data/lib/appjam/version.rb +1 -1
- metadata +32 -3
@@ -0,0 +1,155 @@
|
|
1
|
+
//
|
2
|
+
// DCIntrospect.h
|
3
|
+
//
|
4
|
+
// Created by Domestic Cat on 29/04/11.
|
5
|
+
//
|
6
|
+
|
7
|
+
#define kDCIntrospectNotificationIntrospectionDidStart @"kDCIntrospectNotificationIntrospectionDidStart"
|
8
|
+
#define kDCIntrospectNotificationIntrospectionDidEnd @"kDCIntrospectNotificationIntrospectionDidEnd"
|
9
|
+
#define kDCIntrospectAnimationDuration 0.08
|
10
|
+
|
11
|
+
#import <objc/runtime.h>
|
12
|
+
#include "TargetConditionals.h"
|
13
|
+
|
14
|
+
#import "DCIntrospectSettings.h"
|
15
|
+
#import "DCFrameView.h"
|
16
|
+
#import "DCStatusBarOverlay.h"
|
17
|
+
|
18
|
+
#ifdef DEBUG
|
19
|
+
|
20
|
+
@interface UIView (debug)
|
21
|
+
|
22
|
+
- (NSString *)recursiveDescription;
|
23
|
+
|
24
|
+
@end
|
25
|
+
|
26
|
+
#endif
|
27
|
+
|
28
|
+
@interface DCIntrospect : NSObject <DCFrameViewDelegate, UITextViewDelegate, UIWebViewDelegate>
|
29
|
+
{
|
30
|
+
}
|
31
|
+
|
32
|
+
@property (nonatomic) BOOL keyboardBindingsOn; // default: YES
|
33
|
+
@property (nonatomic) BOOL showStatusBarOverlay; // default: YES
|
34
|
+
@property (nonatomic, retain) UIGestureRecognizer *invokeGestureRecognizer; // default: nil
|
35
|
+
|
36
|
+
@property (nonatomic) BOOL on;
|
37
|
+
@property (nonatomic) BOOL handleArrowKeys;
|
38
|
+
@property (nonatomic) BOOL viewOutlines;
|
39
|
+
@property (nonatomic) BOOL highlightNonOpaqueViews;
|
40
|
+
@property (nonatomic) BOOL flashOnRedraw;
|
41
|
+
@property (nonatomic, retain) DCFrameView *frameView;
|
42
|
+
@property (nonatomic, retain) UITextView *inputTextView;
|
43
|
+
@property (nonatomic, retain) DCStatusBarOverlay *statusBarOverlay;
|
44
|
+
|
45
|
+
@property (nonatomic, retain) NSMutableDictionary *objectNames;
|
46
|
+
|
47
|
+
@property (nonatomic, assign) UIView *currentView;
|
48
|
+
@property (nonatomic) CGRect originalFrame;
|
49
|
+
@property (nonatomic) CGFloat originalAlpha;
|
50
|
+
@property (nonatomic, retain) NSMutableArray *currentViewHistory;
|
51
|
+
|
52
|
+
@property (nonatomic) BOOL showingHelp;
|
53
|
+
|
54
|
+
///////////
|
55
|
+
// Setup //
|
56
|
+
///////////
|
57
|
+
|
58
|
+
+ (DCIntrospect *)sharedIntrospector; // this returns nil when NOT in DEGBUG mode
|
59
|
+
- (void)start; // NOTE: call setup AFTER [window makeKeyAndVisible] so statusBarOrientation is reported correctly.
|
60
|
+
|
61
|
+
////////////////////
|
62
|
+
// Custom Setters //
|
63
|
+
////////////////////
|
64
|
+
|
65
|
+
- (void)setInvokeGestureRecognizer:(UIGestureRecognizer *)newGestureRecognizer;
|
66
|
+
- (void)setKeyboardBindingsOn:(BOOL)keyboardBindingsOn;
|
67
|
+
|
68
|
+
//////////////////
|
69
|
+
// Main Actions //
|
70
|
+
//////////////////
|
71
|
+
|
72
|
+
- (void)invokeIntrospector; // can be called manually
|
73
|
+
- (void)touchAtPoint:(CGPoint)point; // can be called manually
|
74
|
+
- (void)selectView:(UIView *)view;
|
75
|
+
- (void)statusBarTapped;
|
76
|
+
|
77
|
+
//////////////////////
|
78
|
+
// Keyboard Capture //
|
79
|
+
//////////////////////
|
80
|
+
|
81
|
+
- (void)textViewDidChangeSelection:(UITextView *)textView;
|
82
|
+
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)string;
|
83
|
+
|
84
|
+
/////////////////////////////////
|
85
|
+
// Logging Code & Object Names //
|
86
|
+
/////////////////////////////////
|
87
|
+
|
88
|
+
- (void)logCodeForCurrentViewChanges;
|
89
|
+
|
90
|
+
// make sure all names that are added are removed at dealloc or else they will be retained here!
|
91
|
+
- (void)setName:(NSString *)name forObject:(id)object accessedWithSelf:(BOOL)accessedWithSelf;
|
92
|
+
- (NSString *)nameForObject:(id)object;
|
93
|
+
- (void)removeNamesForViewsInView:(UIView *)view;
|
94
|
+
- (void)removeNameForObject:(id)object;
|
95
|
+
|
96
|
+
////////////
|
97
|
+
// Layout //
|
98
|
+
////////////
|
99
|
+
|
100
|
+
- (void)updateFrameView;
|
101
|
+
- (void)updateStatusBar;
|
102
|
+
- (void)updateViews;
|
103
|
+
- (void)showTemporaryStringInStatusBar:(NSString *)string;
|
104
|
+
|
105
|
+
/////////////
|
106
|
+
// Actions //
|
107
|
+
/////////////
|
108
|
+
|
109
|
+
- (void)logRecursiveDescriptionForCurrentView;
|
110
|
+
- (void)logRecursiveDescriptionForView:(UIView *)view;
|
111
|
+
- (void)forceSetNeedsDisplay;
|
112
|
+
- (void)forceSetNeedsLayout;
|
113
|
+
- (void)forceReloadOfView;
|
114
|
+
- (void)toggleOutlines;
|
115
|
+
- (void)addOutlinesToFrameViewFromSubview:(UIView *)view;
|
116
|
+
- (void)toggleNonOpaqueViews;
|
117
|
+
- (void)setBackgroundColor:(UIColor *)color ofNonOpaqueViewsInSubview:(UIView *)view;
|
118
|
+
- (void)toggleRedrawFlashing;
|
119
|
+
- (void)callDrawRectOnViewsInSubview:(UIView *)subview;
|
120
|
+
- (void)flashRect:(CGRect)rect inView:(UIView *)view;
|
121
|
+
|
122
|
+
/////////////////////////////
|
123
|
+
// (Somewhat) Experimental //
|
124
|
+
/////////////////////////////
|
125
|
+
|
126
|
+
- (void)logPropertiesForCurrentView;
|
127
|
+
- (void)logPropertiesForObject:(id)object;
|
128
|
+
- (void)logAccessabilityPropertiesForObject:(id)object;
|
129
|
+
- (NSArray *)subclassesOfClass:(Class)parentClass;
|
130
|
+
|
131
|
+
/////////////////////////
|
132
|
+
// Description Methods //
|
133
|
+
/////////////////////////
|
134
|
+
|
135
|
+
- (NSString *)describeProperty:(NSString *)propertyName value:(id)value;
|
136
|
+
- (NSString *)describeColor:(UIColor *)color;
|
137
|
+
|
138
|
+
/////////////////////////
|
139
|
+
// DCIntrospector Help //
|
140
|
+
/////////////////////////
|
141
|
+
|
142
|
+
- (void)toggleHelp;
|
143
|
+
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
|
144
|
+
|
145
|
+
////////////////////
|
146
|
+
// Helper Methods //
|
147
|
+
////////////////////
|
148
|
+
|
149
|
+
- (UIWindow *)mainWindow;
|
150
|
+
- (NSMutableArray *)viewsAtPoint:(CGPoint)touchPoint inView:(UIView *)view;
|
151
|
+
- (void)fadeView:(UIView *)view toAlpha:(CGFloat)alpha;
|
152
|
+
- (BOOL)view:(UIView *)view containsSubview:(UIView *)subview;
|
153
|
+
- (BOOL)shouldIgnoreView:(UIView *)view;
|
154
|
+
|
155
|
+
@end
|
@@ -0,0 +1,1654 @@
|
|
1
|
+
//
|
2
|
+
// DCIntrospect.m
|
3
|
+
//
|
4
|
+
// Created by Domestic Cat on 29/04/11.
|
5
|
+
//
|
6
|
+
|
7
|
+
#import "DCIntrospect.h"
|
8
|
+
#import <dlfcn.h>
|
9
|
+
|
10
|
+
#include <assert.h>
|
11
|
+
#include <stdbool.h>
|
12
|
+
#include <sys/types.h>
|
13
|
+
#include <unistd.h>
|
14
|
+
#include <sys/sysctl.h>
|
15
|
+
|
16
|
+
// break into GDB code complied from following sources:
|
17
|
+
// http://blog.timac.org/?p=190, http://developer.apple.com/library/mac/#qa/qa1361/_index.html, http://cocoawithlove.com/2008/03/break-into-debugger.html
|
18
|
+
|
19
|
+
// Returns true if the current process is being debugged (either
|
20
|
+
// running under the debugger or has a debugger attached post facto).
|
21
|
+
static bool AmIBeingDebugged(void)
|
22
|
+
{
|
23
|
+
int junk;
|
24
|
+
int mib[4];
|
25
|
+
struct kinfo_proc info;
|
26
|
+
size_t size;
|
27
|
+
|
28
|
+
// Initialize the flags so that, if sysctl fails for some bizarre
|
29
|
+
// reason, we get a predictable result.
|
30
|
+
|
31
|
+
info.kp_proc.p_flag = 0;
|
32
|
+
|
33
|
+
// Initialize mib, which tells sysctl the info we want, in this case
|
34
|
+
// we're looking for information about a specific process ID.
|
35
|
+
|
36
|
+
mib[0] = CTL_KERN;
|
37
|
+
mib[1] = KERN_PROC;
|
38
|
+
mib[2] = KERN_PROC_PID;
|
39
|
+
mib[3] = getpid();
|
40
|
+
|
41
|
+
// Call sysctl.
|
42
|
+
|
43
|
+
size = sizeof(info);
|
44
|
+
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
|
45
|
+
assert(junk == 0);
|
46
|
+
|
47
|
+
// We're being debugged if the P_TRACED flag is set.
|
48
|
+
|
49
|
+
return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
|
50
|
+
}
|
51
|
+
|
52
|
+
#if TARGET_CPU_ARM
|
53
|
+
#define DEBUGSTOP(signal) __asm__ __volatile__ ("mov r0, %0\nmov r1, %1\nmov r12, %2\nswi 128\n" : : "r"(getpid ()), "r"(signal), "r"(37) : "r12", "r0", "r1", "cc");
|
54
|
+
#define DEBUGGER do { int trapSignal = AmIBeingDebugged () ? SIGINT : SIGSTOP; DEBUGSTOP(trapSignal); if (trapSignal == SIGSTOP) { DEBUGSTOP (SIGINT); } } while (false);
|
55
|
+
#else
|
56
|
+
#define DEBUGGER do { int trapSignal = AmIBeingDebugged () ? SIGINT : SIGSTOP; __asm__ __volatile__ ("pushl %0\npushl %1\npush $0\nmovl %2, %%eax\nint $0x80\nadd $12, %%esp" : : "g" (trapSignal), "g" (getpid ()), "n" (37) : "eax", "cc"); } while (false);
|
57
|
+
#endif
|
58
|
+
|
59
|
+
@interface DCIntrospect ()
|
60
|
+
|
61
|
+
- (void)takeFirstResponder;
|
62
|
+
|
63
|
+
@end
|
64
|
+
|
65
|
+
|
66
|
+
DCIntrospect *sharedInstance = nil;
|
67
|
+
|
68
|
+
@implementation DCIntrospect
|
69
|
+
@synthesize keyboardBindingsOn, showStatusBarOverlay, invokeGestureRecognizer;
|
70
|
+
@synthesize on;
|
71
|
+
@synthesize handleArrowKeys;
|
72
|
+
@synthesize viewOutlines, highlightNonOpaqueViews, flashOnRedraw;
|
73
|
+
@synthesize statusBarOverlay;
|
74
|
+
@synthesize inputTextView;
|
75
|
+
@synthesize frameView;
|
76
|
+
@synthesize objectNames;
|
77
|
+
@synthesize currentView, originalFrame, originalAlpha;
|
78
|
+
@synthesize currentViewHistory;
|
79
|
+
@synthesize showingHelp;
|
80
|
+
|
81
|
+
#pragma mark Setup
|
82
|
+
|
83
|
+
+ (void)load
|
84
|
+
{
|
85
|
+
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
86
|
+
|
87
|
+
NSString *simulatorRoot = [[[NSProcessInfo processInfo] environment] objectForKey:@"IPHONE_SIMULATOR_ROOT"];
|
88
|
+
if (simulatorRoot)
|
89
|
+
{
|
90
|
+
void *AppSupport = dlopen([[simulatorRoot stringByAppendingPathComponent:@"/System/Library/PrivateFrameworks/AppSupport.framework/AppSupport"] fileSystemRepresentation], RTLD_LAZY);
|
91
|
+
CFStringRef (*CPCopySharedResourcesPreferencesDomainForDomain)(CFStringRef domain) = (CFStringRef (*)())dlsym(AppSupport, "CPCopySharedResourcesPreferencesDomainForDomain");
|
92
|
+
if (CPCopySharedResourcesPreferencesDomainForDomain)
|
93
|
+
{
|
94
|
+
CFStringRef accessibilityDomain = CPCopySharedResourcesPreferencesDomainForDomain(CFSTR("com.apple.Accessibility"));
|
95
|
+
if (accessibilityDomain)
|
96
|
+
{
|
97
|
+
// This must be done *before* UIApplicationMain, hence +load
|
98
|
+
CFPreferencesSetValue(CFSTR("ApplicationAccessibilityEnabled"), kCFBooleanTrue, accessibilityDomain, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
|
99
|
+
CFRelease(accessibilityDomain);
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
[pool drain];
|
105
|
+
}
|
106
|
+
|
107
|
+
static void *originalValueForKeyIMPKey = &originalValueForKeyIMPKey;
|
108
|
+
|
109
|
+
id UITextInputTraits_valueForKey(id self, SEL _cmd, NSString *key);
|
110
|
+
id UITextInputTraits_valueForKey(id self, SEL _cmd, NSString *key)
|
111
|
+
{
|
112
|
+
static NSMutableSet *textInputTraitsProperties = nil;
|
113
|
+
if (!textInputTraitsProperties)
|
114
|
+
{
|
115
|
+
textInputTraitsProperties = [[NSMutableSet alloc] init];
|
116
|
+
unsigned int count = 0;
|
117
|
+
objc_property_t *properties = protocol_copyPropertyList(@protocol(UITextInputTraits), &count);
|
118
|
+
for (unsigned int i = 0; i < count; i++)
|
119
|
+
{
|
120
|
+
objc_property_t property = properties[i];
|
121
|
+
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
|
122
|
+
[textInputTraitsProperties addObject:propertyName];
|
123
|
+
}
|
124
|
+
free(properties);
|
125
|
+
}
|
126
|
+
|
127
|
+
IMP valueForKey = (IMP)[objc_getAssociatedObject([self class], originalValueForKeyIMPKey) pointerValue];
|
128
|
+
if ([textInputTraitsProperties containsObject:key])
|
129
|
+
{
|
130
|
+
id textInputTraits = valueForKey(self, _cmd, @"textInputTraits");
|
131
|
+
return valueForKey(textInputTraits, _cmd, key);
|
132
|
+
}
|
133
|
+
else
|
134
|
+
{
|
135
|
+
return valueForKey(self, _cmd, key);
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
// See http://stackoverflow.com/questions/6617472/why-does-valueforkey-on-a-uitextfield-throws-an-exception-for-uitextinputtraits
|
140
|
+
+ (void)workaroundUITextInputTraitsPropertiesBug
|
141
|
+
{
|
142
|
+
Method valueForKey = class_getInstanceMethod([NSObject class], @selector(valueForKey:));
|
143
|
+
const char *valueForKeyTypeEncoding = method_getTypeEncoding(valueForKey);
|
144
|
+
|
145
|
+
unsigned int count = 0;
|
146
|
+
Class *classes = objc_copyClassList(&count);
|
147
|
+
for (unsigned int i = 0; i < count; i++)
|
148
|
+
{
|
149
|
+
Class class = classes[i];
|
150
|
+
if (class_getInstanceMethod(class, NSSelectorFromString(@"textInputTraits")))
|
151
|
+
{
|
152
|
+
IMP originalValueForKey = class_replaceMethod(class, @selector(valueForKey:), (IMP)UITextInputTraits_valueForKey, valueForKeyTypeEncoding);
|
153
|
+
if (!originalValueForKey)
|
154
|
+
originalValueForKey = (IMP)[objc_getAssociatedObject([class superclass], originalValueForKeyIMPKey) pointerValue];
|
155
|
+
if (!originalValueForKey)
|
156
|
+
originalValueForKey = class_getMethodImplementation([class superclass], @selector(valueForKey:));
|
157
|
+
|
158
|
+
objc_setAssociatedObject(class, originalValueForKeyIMPKey, [NSValue valueWithPointer:(void *)originalValueForKey], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
159
|
+
}
|
160
|
+
}
|
161
|
+
free(classes);
|
162
|
+
}
|
163
|
+
|
164
|
+
+ (DCIntrospect *)sharedIntrospector
|
165
|
+
{
|
166
|
+
#ifdef DEBUG
|
167
|
+
if (!sharedInstance)
|
168
|
+
{
|
169
|
+
sharedInstance = [[DCIntrospect alloc] init];
|
170
|
+
sharedInstance.keyboardBindingsOn = YES;
|
171
|
+
sharedInstance.showStatusBarOverlay = ![UIApplication sharedApplication].statusBarHidden;
|
172
|
+
[self workaroundUITextInputTraitsPropertiesBug];
|
173
|
+
}
|
174
|
+
#endif
|
175
|
+
return sharedInstance;
|
176
|
+
}
|
177
|
+
|
178
|
+
- (void)start
|
179
|
+
{
|
180
|
+
UIWindow *mainWindow = [self mainWindow];
|
181
|
+
if (!mainWindow)
|
182
|
+
{
|
183
|
+
NSLog(@"DCIntrospect: Couldn't setup. No main window?");
|
184
|
+
return;
|
185
|
+
}
|
186
|
+
|
187
|
+
if (!self.statusBarOverlay)
|
188
|
+
{
|
189
|
+
self.statusBarOverlay = [[[DCStatusBarOverlay alloc] init] autorelease];
|
190
|
+
}
|
191
|
+
|
192
|
+
if (!self.inputTextView)
|
193
|
+
{
|
194
|
+
self.inputTextView = [[[UITextView alloc] initWithFrame:CGRectZero] autorelease];
|
195
|
+
self.inputTextView.delegate = self;
|
196
|
+
self.inputTextView.autocorrectionType = UITextAutocorrectionTypeNo;
|
197
|
+
self.inputTextView.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
198
|
+
self.inputTextView.inputView = [[[UIView alloc] init] autorelease];
|
199
|
+
self.inputTextView.scrollsToTop = NO;
|
200
|
+
[mainWindow addSubview:self.inputTextView];
|
201
|
+
}
|
202
|
+
|
203
|
+
if (self.keyboardBindingsOn)
|
204
|
+
{
|
205
|
+
if (![self.inputTextView becomeFirstResponder])
|
206
|
+
{
|
207
|
+
[self performSelector:@selector(takeFirstResponder) withObject:nil afterDelay:0.5];
|
208
|
+
}
|
209
|
+
}
|
210
|
+
|
211
|
+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarTapped) name:kDCIntrospectNotificationStatusBarTapped object:nil];
|
212
|
+
|
213
|
+
// reclaim the keyboard after dismissal if it is taken
|
214
|
+
[[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillHideNotification
|
215
|
+
object:nil
|
216
|
+
queue:nil
|
217
|
+
usingBlock:^(NSNotification *notification) {
|
218
|
+
if (self.keyboardBindingsOn)
|
219
|
+
{
|
220
|
+
[self performSelector:@selector(takeFirstResponder)
|
221
|
+
withObject:nil
|
222
|
+
afterDelay:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]];
|
223
|
+
}
|
224
|
+
}];
|
225
|
+
|
226
|
+
// dirty hack for UIWebView keyboard problems
|
227
|
+
[[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillShowNotification
|
228
|
+
object:nil
|
229
|
+
queue:nil
|
230
|
+
usingBlock:^(NSNotification *notification) {
|
231
|
+
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(takeFirstResponder) object:nil];
|
232
|
+
}];
|
233
|
+
|
234
|
+
// listen for device orientation changes to adjust the status bar
|
235
|
+
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
|
236
|
+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateViews) name:UIDeviceOrientationDidChangeNotification object:nil];
|
237
|
+
|
238
|
+
if (!self.currentViewHistory)
|
239
|
+
self.currentViewHistory = [[[NSMutableArray alloc] init] autorelease];
|
240
|
+
|
241
|
+
NSLog(@"DCIntrospect is setup. %@ to start.", [kDCIntrospectKeysInvoke isEqualToString:@" "] ? @"Push the space bar" : [NSString stringWithFormat:@"Type '%@'", kDCIntrospectKeysInvoke]);
|
242
|
+
}
|
243
|
+
|
244
|
+
- (void)takeFirstResponder
|
245
|
+
{
|
246
|
+
if (![self.inputTextView becomeFirstResponder])
|
247
|
+
NSLog(@"DCIntrospect: Couldn't reclaim keyboard input. Is the keyboard used elsewhere?");
|
248
|
+
}
|
249
|
+
|
250
|
+
- (void)resetInputTextView
|
251
|
+
{
|
252
|
+
self.inputTextView.text = @"\n2 4567 9\n";
|
253
|
+
self.handleArrowKeys = NO;
|
254
|
+
self.inputTextView.selectedRange = NSMakeRange(5, 0);
|
255
|
+
self.handleArrowKeys = YES;
|
256
|
+
}
|
257
|
+
|
258
|
+
#pragma mark Custom Setters
|
259
|
+
- (void)setInvokeGestureRecognizer:(UIGestureRecognizer *)newGestureRecognizer
|
260
|
+
{
|
261
|
+
UIWindow *mainWindow = [self mainWindow];
|
262
|
+
[mainWindow removeGestureRecognizer:invokeGestureRecognizer];
|
263
|
+
|
264
|
+
[invokeGestureRecognizer release];
|
265
|
+
invokeGestureRecognizer = nil;
|
266
|
+
invokeGestureRecognizer = [newGestureRecognizer retain];
|
267
|
+
[invokeGestureRecognizer addTarget:self action:@selector(invokeIntrospector)];
|
268
|
+
[mainWindow addGestureRecognizer:invokeGestureRecognizer];
|
269
|
+
}
|
270
|
+
|
271
|
+
- (void)setKeyboardBindingsOn:(BOOL)areKeyboardBindingsOn
|
272
|
+
{
|
273
|
+
keyboardBindingsOn = areKeyboardBindingsOn;
|
274
|
+
if (self.keyboardBindingsOn)
|
275
|
+
[self.inputTextView becomeFirstResponder];
|
276
|
+
else
|
277
|
+
[self.inputTextView resignFirstResponder];
|
278
|
+
}
|
279
|
+
|
280
|
+
#pragma mark Main Actions
|
281
|
+
|
282
|
+
- (void)invokeIntrospector
|
283
|
+
{
|
284
|
+
self.on = !self.on;
|
285
|
+
|
286
|
+
if (self.on)
|
287
|
+
{
|
288
|
+
[self updateViews];
|
289
|
+
[self updateStatusBar];
|
290
|
+
[self updateFrameView];
|
291
|
+
|
292
|
+
if (self.keyboardBindingsOn)
|
293
|
+
[self.inputTextView becomeFirstResponder];
|
294
|
+
else
|
295
|
+
[self.inputTextView resignFirstResponder];
|
296
|
+
|
297
|
+
[self resetInputTextView];
|
298
|
+
|
299
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:kDCIntrospectNotificationIntrospectionDidStart
|
300
|
+
object:nil];
|
301
|
+
}
|
302
|
+
else
|
303
|
+
{
|
304
|
+
if (self.viewOutlines)
|
305
|
+
[self toggleOutlines];
|
306
|
+
if (self.highlightNonOpaqueViews)
|
307
|
+
[self toggleNonOpaqueViews];
|
308
|
+
if (self.showingHelp)
|
309
|
+
[self toggleHelp];
|
310
|
+
|
311
|
+
self.statusBarOverlay.hidden = YES;
|
312
|
+
self.frameView.alpha = 0;
|
313
|
+
self.currentView = nil;
|
314
|
+
|
315
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:kDCIntrospectNotificationIntrospectionDidEnd
|
316
|
+
object:nil];
|
317
|
+
}
|
318
|
+
}
|
319
|
+
|
320
|
+
- (void)touchAtPoint:(CGPoint)point
|
321
|
+
{
|
322
|
+
// convert the point into the main window
|
323
|
+
CGPoint convertedTouchPoint = [[self mainWindow] convertPoint:point fromView:self.frameView];
|
324
|
+
|
325
|
+
// find all the views under that point – will be added in order on screen, ie mainWindow will be index 0, main view controller at index 1 etc.
|
326
|
+
NSMutableArray *views = [self viewsAtPoint:convertedTouchPoint inView:[self mainWindow]];
|
327
|
+
if (views.count == 0)
|
328
|
+
return;
|
329
|
+
|
330
|
+
// get the topmost view and setup the UI
|
331
|
+
[self.currentViewHistory removeAllObjects];
|
332
|
+
UIView *newView = [views lastObject];
|
333
|
+
[self selectView:newView];
|
334
|
+
}
|
335
|
+
|
336
|
+
- (void)selectView:(UIView *)view
|
337
|
+
{
|
338
|
+
self.currentView = view;
|
339
|
+
self.originalFrame = self.currentView.frame;
|
340
|
+
self.originalAlpha = self.currentView.alpha;
|
341
|
+
|
342
|
+
if (self.frameView.rectsToOutline.count > 0)
|
343
|
+
{
|
344
|
+
[self.frameView.rectsToOutline removeAllObjects];
|
345
|
+
[self.frameView setNeedsDisplay];
|
346
|
+
self.viewOutlines = NO;
|
347
|
+
}
|
348
|
+
|
349
|
+
[self updateFrameView];
|
350
|
+
[self updateStatusBar];
|
351
|
+
|
352
|
+
if (![self.currentViewHistory containsObject:self.currentView])
|
353
|
+
[self.currentViewHistory addObject:self.currentView];
|
354
|
+
}
|
355
|
+
|
356
|
+
- (void)statusBarTapped
|
357
|
+
{
|
358
|
+
if (self.showingHelp)
|
359
|
+
{
|
360
|
+
[self toggleHelp];
|
361
|
+
return;
|
362
|
+
}
|
363
|
+
}
|
364
|
+
|
365
|
+
#pragma mark Keyboard Capture
|
366
|
+
|
367
|
+
- (void)textViewDidChangeSelection:(UITextView *)textView
|
368
|
+
{
|
369
|
+
if (!(self.on && self.handleArrowKeys))
|
370
|
+
return;
|
371
|
+
|
372
|
+
NSUInteger selectionLocation = textView.selectedRange.location;
|
373
|
+
NSUInteger selectionLength = textView.selectedRange.length;
|
374
|
+
BOOL shiftKey = selectionLength != 0;
|
375
|
+
BOOL optionKey = selectionLocation % 2 == 1;
|
376
|
+
|
377
|
+
CGRect frame = self.currentView.frame;
|
378
|
+
if (shiftKey)
|
379
|
+
{
|
380
|
+
if (selectionLocation == 4 && selectionLength == 1)
|
381
|
+
frame.origin.x -= 10.0f;
|
382
|
+
else if (selectionLocation == 5 && selectionLength == 1)
|
383
|
+
frame.origin.x += 10.0f;
|
384
|
+
else if (selectionLocation == 0 && selectionLength == 5)
|
385
|
+
frame.origin.y -= 10.0f;
|
386
|
+
else if (selectionLocation == 5 && selectionLength == 5)
|
387
|
+
frame.origin.y += 10.0f;
|
388
|
+
}
|
389
|
+
else if (optionKey)
|
390
|
+
{
|
391
|
+
if (selectionLocation == 7)
|
392
|
+
frame.size.width += 1.0f;
|
393
|
+
else if (selectionLocation == 3)
|
394
|
+
frame.size.width -= 1.0f;
|
395
|
+
else if (selectionLocation == 9)
|
396
|
+
frame.size.height += 1.0f;
|
397
|
+
else if (selectionLocation == 1)
|
398
|
+
frame.size.height -= 1.0f;
|
399
|
+
}
|
400
|
+
else
|
401
|
+
{
|
402
|
+
if (selectionLocation == 4)
|
403
|
+
frame.origin.x -= 1.0f;
|
404
|
+
else if (selectionLocation == 6)
|
405
|
+
frame.origin.x += 1.0f;
|
406
|
+
else if (selectionLocation == 0)
|
407
|
+
frame.origin.y -= 1.0f;
|
408
|
+
else if (selectionLocation == 10)
|
409
|
+
frame.origin.y += 1.0f;
|
410
|
+
}
|
411
|
+
|
412
|
+
self.currentView.frame = CGRectMake(floorf(frame.origin.x),
|
413
|
+
floorf(frame.origin.y),
|
414
|
+
floorf(frame.size.width),
|
415
|
+
floorf(frame.size.height));
|
416
|
+
|
417
|
+
[self updateFrameView];
|
418
|
+
[self updateStatusBar];
|
419
|
+
|
420
|
+
[self resetInputTextView];
|
421
|
+
}
|
422
|
+
|
423
|
+
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)string
|
424
|
+
{
|
425
|
+
if ([string isEqualToString:kDCIntrospectKeysDisableForPeriod])
|
426
|
+
{
|
427
|
+
[self setKeyboardBindingsOn:NO];
|
428
|
+
[[self inputTextView] resignFirstResponder];
|
429
|
+
NSLog(@"DCIntrospect: Disabled for %.1f seconds", kDCIntrospectTemporaryDisableDuration);
|
430
|
+
[self performSelector:@selector(setKeyboardBindingsOn:) withObject:[NSNumber numberWithFloat:YES] afterDelay:kDCIntrospectTemporaryDisableDuration];
|
431
|
+
return NO;
|
432
|
+
}
|
433
|
+
|
434
|
+
if ([string isEqualToString:kDCIntrospectKeysInvoke])
|
435
|
+
{
|
436
|
+
[self invokeIntrospector];
|
437
|
+
return NO;
|
438
|
+
}
|
439
|
+
|
440
|
+
if (!self.on)
|
441
|
+
return NO;
|
442
|
+
|
443
|
+
if (self.showingHelp)
|
444
|
+
{
|
445
|
+
[self toggleHelp];
|
446
|
+
return NO;
|
447
|
+
}
|
448
|
+
|
449
|
+
if ([string isEqualToString:kDCIntrospectKeysToggleViewOutlines])
|
450
|
+
{
|
451
|
+
[self toggleOutlines];
|
452
|
+
return NO;
|
453
|
+
}
|
454
|
+
else if ([string isEqualToString:kDCIntrospectKeysToggleNonOpaqueViews])
|
455
|
+
{
|
456
|
+
[self toggleNonOpaqueViews];
|
457
|
+
return NO;
|
458
|
+
}
|
459
|
+
else if ([string isEqualToString:kDCIntrospectKeysToggleFlashViewRedraws])
|
460
|
+
{
|
461
|
+
[self toggleRedrawFlashing];
|
462
|
+
return NO;
|
463
|
+
}
|
464
|
+
else if ([string isEqualToString:kDCIntrospectKeysToggleShowCoordinates])
|
465
|
+
{
|
466
|
+
[UIView animateWithDuration:0.15
|
467
|
+
delay:0
|
468
|
+
options:UIViewAnimationOptionAllowUserInteraction
|
469
|
+
animations:^{
|
470
|
+
self.frameView.touchPointLabel.alpha = !self.frameView.touchPointLabel.alpha;
|
471
|
+
} completion:^(BOOL finished) {
|
472
|
+
NSString *coordinatesString = [NSString stringWithFormat:@"Coordinates are %@", (self.frameView.touchPointLabel.alpha) ? @"on" : @"off"];
|
473
|
+
if (self.showStatusBarOverlay)
|
474
|
+
[self showTemporaryStringInStatusBar:coordinatesString];
|
475
|
+
else
|
476
|
+
NSLog(@"DCIntrospect: %@", coordinatesString);
|
477
|
+
}];
|
478
|
+
return NO;
|
479
|
+
}
|
480
|
+
else if ([string isEqualToString:kDCIntrospectKeysToggleHelp])
|
481
|
+
{
|
482
|
+
[self toggleHelp];
|
483
|
+
return NO;
|
484
|
+
}
|
485
|
+
|
486
|
+
if (self.on && self.currentView)
|
487
|
+
{
|
488
|
+
if ([string isEqualToString:kDCIntrospectKeysLogProperties])
|
489
|
+
{
|
490
|
+
[self logPropertiesForObject:self.currentView];
|
491
|
+
return NO;
|
492
|
+
}
|
493
|
+
else if ([string isEqualToString:kDCIntrospectKeysLogAccessibilityProperties])
|
494
|
+
{
|
495
|
+
[self logAccessabilityPropertiesForObject:self.currentView];
|
496
|
+
return NO;
|
497
|
+
}
|
498
|
+
else if ([string isEqualToString:kDCIntrospectKeysLogViewRecursive])
|
499
|
+
{
|
500
|
+
[self logRecursiveDescriptionForView:self.currentView];
|
501
|
+
return NO;
|
502
|
+
}
|
503
|
+
else if ([string isEqualToString:kDCIntrospectKeysSetNeedsDisplay])
|
504
|
+
{
|
505
|
+
[self forceSetNeedsDisplay];
|
506
|
+
return NO;
|
507
|
+
}
|
508
|
+
else if ([string isEqualToString:kDCIntrospectKeysSetNeedsLayout])
|
509
|
+
{
|
510
|
+
[self forceSetNeedsLayout];
|
511
|
+
return NO;
|
512
|
+
}
|
513
|
+
else if ([string isEqualToString:kDCIntrospectKeysReloadData])
|
514
|
+
{
|
515
|
+
[self forceReloadOfView];
|
516
|
+
return NO;
|
517
|
+
}
|
518
|
+
else if ([string isEqualToString:kDCIntrospectKeysMoveUpInViewHierarchy])
|
519
|
+
{
|
520
|
+
if (self.currentView.superview)
|
521
|
+
{
|
522
|
+
[self selectView:self.currentView.superview];
|
523
|
+
}
|
524
|
+
else
|
525
|
+
{
|
526
|
+
NSLog(@"DCIntrospect: At top of view hierarchy.");
|
527
|
+
return NO;
|
528
|
+
}
|
529
|
+
return NO;
|
530
|
+
}
|
531
|
+
else if ([string isEqualToString:kDCIntrospectKeysMoveBackInViewHierarchy])
|
532
|
+
{
|
533
|
+
if (self.currentViewHistory.count == 0)
|
534
|
+
return NO;
|
535
|
+
|
536
|
+
int indexOfCurrentView = [self.currentViewHistory indexOfObject:self.currentView];
|
537
|
+
if (indexOfCurrentView == 0)
|
538
|
+
{
|
539
|
+
NSLog(@"DCIntrospect: At bottom of view history.");
|
540
|
+
return NO;
|
541
|
+
}
|
542
|
+
|
543
|
+
[self selectView:[self.currentViewHistory objectAtIndex:indexOfCurrentView - 1]];
|
544
|
+
}
|
545
|
+
else if ([string isEqualToString:kDCIntrospectKeysMoveDownToFirstSubview])
|
546
|
+
{
|
547
|
+
if (self.currentView.subviews.count>0) {
|
548
|
+
[self selectView:[self.currentView.subviews objectAtIndex:0]];
|
549
|
+
}else{
|
550
|
+
NSLog(@"DCIntrospect: No subviews.");
|
551
|
+
return NO;
|
552
|
+
}
|
553
|
+
return NO;
|
554
|
+
}
|
555
|
+
else if ([string isEqualToString:kDCIntrospectKeysMoveToNextSiblingView])
|
556
|
+
{
|
557
|
+
NSUInteger currentViewsIndex = [self.currentView.superview.subviews indexOfObject:self.currentView];
|
558
|
+
|
559
|
+
if (currentViewsIndex==NSNotFound) {
|
560
|
+
NSLog(@"DCIntrospect: BROKEN HIERARCHY.");
|
561
|
+
} else if (self.currentView.superview.subviews.count>currentViewsIndex + 1) {
|
562
|
+
[self selectView:[self.currentView.superview.subviews objectAtIndex:currentViewsIndex + 1]];
|
563
|
+
}else{
|
564
|
+
NSLog(@"DCIntrospect: No next sibling views.");
|
565
|
+
return NO;
|
566
|
+
}
|
567
|
+
return NO;
|
568
|
+
}
|
569
|
+
else if ([string isEqualToString:kDCIntrospectKeysMoveToPrevSiblingView])
|
570
|
+
{
|
571
|
+
NSUInteger currentViewsIndex = [self.currentView.superview.subviews indexOfObject:self.currentView];
|
572
|
+
if (currentViewsIndex==NSNotFound) {
|
573
|
+
NSLog(@"DCIntrospect: BROKEN HIERARCHY.");
|
574
|
+
} else if (currentViewsIndex!=0) {
|
575
|
+
[self selectView:[self.currentView.superview.subviews objectAtIndex:currentViewsIndex - 1]];
|
576
|
+
} else {
|
577
|
+
NSLog(@"DCIntrospect: No previous sibling views.");
|
578
|
+
}
|
579
|
+
return NO;
|
580
|
+
}
|
581
|
+
else if ([string isEqualToString:kDCIntrospectKeysLogCodeForCurrentViewChanges])
|
582
|
+
{
|
583
|
+
[self logCodeForCurrentViewChanges];
|
584
|
+
return NO;
|
585
|
+
}
|
586
|
+
|
587
|
+
CGRect frame = self.currentView.frame;
|
588
|
+
if ([string isEqualToString:kDCIntrospectKeysNudgeViewLeft])
|
589
|
+
frame.origin.x -= 1.0f;
|
590
|
+
else if ([string isEqualToString:kDCIntrospectKeysNudgeViewRight])
|
591
|
+
frame.origin.x += 1.0f;
|
592
|
+
else if ([string isEqualToString:kDCIntrospectKeysNudgeViewUp])
|
593
|
+
frame.origin.y -= 1.0f;
|
594
|
+
else if ([string isEqualToString:kDCIntrospectKeysNudgeViewDown])
|
595
|
+
frame.origin.y += 1.0f;
|
596
|
+
else if ([string isEqualToString:kDCIntrospectKeysCenterInSuperview])
|
597
|
+
frame = CGRectMake(floorf((self.currentView.superview.frame.size.width - frame.size.width) / 2.0f),
|
598
|
+
floorf((self.currentView.superview.frame.size.height - frame.size.height) / 2.0f),
|
599
|
+
frame.size.width,
|
600
|
+
frame.size.height);
|
601
|
+
else if ([string isEqualToString:kDCIntrospectKeysIncreaseWidth])
|
602
|
+
frame.size.width += 1.0f;
|
603
|
+
else if ([string isEqualToString:kDCIntrospectKeysDecreaseWidth])
|
604
|
+
frame.size.width -= 1.0f;
|
605
|
+
else if ([string isEqualToString:kDCIntrospectKeysIncreaseHeight])
|
606
|
+
frame.size.height += 1.0f;
|
607
|
+
else if ([string isEqualToString:kDCIntrospectKeysDecreaseHeight])
|
608
|
+
frame.size.height -= 1.0f;
|
609
|
+
else if ([string isEqualToString:kDCIntrospectKeysIncreaseViewAlpha])
|
610
|
+
{
|
611
|
+
if (self.currentView.alpha < 1.0f)
|
612
|
+
self.currentView.alpha += 0.05f;
|
613
|
+
}
|
614
|
+
else if ([string isEqualToString:kDCIntrospectKeysDecreaseViewAlpha])
|
615
|
+
{
|
616
|
+
if (self.currentView.alpha > 0.0f)
|
617
|
+
self.currentView.alpha -= 0.05f;
|
618
|
+
}
|
619
|
+
else if ([string isEqualToString:kDCIntrospectKeysEnterGDB])
|
620
|
+
{
|
621
|
+
UIView *view = self.currentView;
|
622
|
+
view.tag = view.tag; // suppress the xcode warning about an unused variable.
|
623
|
+
NSLog(@"DCIntrospect: access current view using local 'view' variable.");
|
624
|
+
DEBUGGER;
|
625
|
+
return NO;
|
626
|
+
}
|
627
|
+
|
628
|
+
self.currentView.frame = CGRectMake(floorf(frame.origin.x),
|
629
|
+
floorf(frame.origin.y),
|
630
|
+
floorf(frame.size.width),
|
631
|
+
floorf(frame.size.height));
|
632
|
+
|
633
|
+
[self updateFrameView];
|
634
|
+
[self updateStatusBar];
|
635
|
+
}
|
636
|
+
|
637
|
+
return NO;
|
638
|
+
}
|
639
|
+
|
640
|
+
#pragma mark Object Names
|
641
|
+
|
642
|
+
- (void)logCodeForCurrentViewChanges
|
643
|
+
{
|
644
|
+
if (!self.currentView)
|
645
|
+
return;
|
646
|
+
|
647
|
+
NSString *varName = [self nameForObject:self.currentView];
|
648
|
+
if ([varName isEqualToString:[NSString stringWithFormat:@"%@", self.currentView.class]])
|
649
|
+
varName = @"<#view#>";
|
650
|
+
|
651
|
+
NSMutableString *outputString = [NSMutableString string];
|
652
|
+
if (!CGRectEqualToRect(self.originalFrame, self.currentView.frame))
|
653
|
+
{
|
654
|
+
[outputString appendFormat:@"%@.frame = CGRectMake(%.1f, %.1f, %.1f, %.1f);\n", varName, self.currentView.frame.origin.x, self.currentView.frame.origin.y, self.currentView.frame.size.width, self.currentView.frame.size.height];
|
655
|
+
}
|
656
|
+
|
657
|
+
if (self.originalAlpha != self.currentView.alpha)
|
658
|
+
{
|
659
|
+
[outputString appendFormat:@"%@.alpha = %.2f;\n", varName, self.currentView.alpha];
|
660
|
+
}
|
661
|
+
|
662
|
+
if (outputString.length == 0)
|
663
|
+
NSLog(@"DCIntrospect: No changes made to %@.", self.currentView.class);
|
664
|
+
else
|
665
|
+
printf("\n\n%s\n", [outputString UTF8String]);
|
666
|
+
}
|
667
|
+
|
668
|
+
- (void)setName:(NSString *)name forObject:(id)object accessedWithSelf:(BOOL)accessedWithSelf
|
669
|
+
{
|
670
|
+
if (!self.objectNames)
|
671
|
+
self.objectNames = [NSMutableDictionary dictionary];
|
672
|
+
|
673
|
+
if (accessedWithSelf)
|
674
|
+
name = [@"self." stringByAppendingString:name];
|
675
|
+
|
676
|
+
[self.objectNames setValue:object forKey:name];
|
677
|
+
}
|
678
|
+
|
679
|
+
- (NSString *)nameForObject:(id)object
|
680
|
+
{
|
681
|
+
__block NSString *objectName = [NSString stringWithFormat:@"%@", [object class]];
|
682
|
+
if (!self.objectNames)
|
683
|
+
return objectName;
|
684
|
+
|
685
|
+
[self.objectNames enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
686
|
+
if (obj == object)
|
687
|
+
{
|
688
|
+
objectName = (NSString *)key;
|
689
|
+
*stop = YES;
|
690
|
+
}
|
691
|
+
}];
|
692
|
+
|
693
|
+
return objectName;
|
694
|
+
}
|
695
|
+
|
696
|
+
- (void)removeNamesForViewsInView:(UIView *)view
|
697
|
+
{
|
698
|
+
if (!self.objectNames)
|
699
|
+
return;
|
700
|
+
|
701
|
+
NSMutableArray *objectsToRemove = [NSMutableArray array];
|
702
|
+
[self.objectNames enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
703
|
+
if ([[obj class] isSubclassOfClass:[UIView class]])
|
704
|
+
{
|
705
|
+
UIView *subview = (UIView *)obj;
|
706
|
+
if ([self view:view containsSubview:subview])
|
707
|
+
[objectsToRemove addObject:key];
|
708
|
+
}
|
709
|
+
}];
|
710
|
+
|
711
|
+
[objectsToRemove enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
|
712
|
+
NSString *key = (NSString *)obj;
|
713
|
+
[self.objectNames removeObjectForKey:key];
|
714
|
+
}];
|
715
|
+
}
|
716
|
+
|
717
|
+
- (void)removeNameForObject:(id)object
|
718
|
+
{
|
719
|
+
if (!self.objectNames)
|
720
|
+
return;
|
721
|
+
|
722
|
+
NSString *objectName = [self nameForObject:object];
|
723
|
+
[self.objectNames removeObjectForKey:objectName];
|
724
|
+
}
|
725
|
+
|
726
|
+
#pragma mark Layout
|
727
|
+
|
728
|
+
- (void)updateFrameView
|
729
|
+
{
|
730
|
+
UIWindow *mainWindow = [self mainWindow];
|
731
|
+
if (!self.frameView)
|
732
|
+
{
|
733
|
+
self.frameView = [[[DCFrameView alloc] initWithFrame:(CGRect){ CGPointZero, mainWindow.frame.size } delegate:self] autorelease];
|
734
|
+
[mainWindow addSubview:self.frameView];
|
735
|
+
self.frameView.alpha = 0.0f;
|
736
|
+
[self updateViews];
|
737
|
+
}
|
738
|
+
|
739
|
+
[mainWindow bringSubviewToFront:self.frameView];
|
740
|
+
|
741
|
+
if (self.on)
|
742
|
+
{
|
743
|
+
if (self.currentView)
|
744
|
+
{
|
745
|
+
self.frameView.mainRect = [self.currentView.superview convertRect:self.currentView.frame toView:self.frameView];
|
746
|
+
if (self.currentView.superview == mainWindow)
|
747
|
+
self.frameView.superRect = CGRectZero;
|
748
|
+
else if (self.currentView.superview.superview)
|
749
|
+
self.frameView.superRect = [self.currentView.superview.superview convertRect:self.currentView.superview.frame toView:self.frameView];
|
750
|
+
else
|
751
|
+
self.frameView.superRect = CGRectZero;
|
752
|
+
}
|
753
|
+
else
|
754
|
+
{
|
755
|
+
self.frameView.mainRect = CGRectZero;
|
756
|
+
}
|
757
|
+
|
758
|
+
[self fadeView:self.frameView toAlpha:1.0f];
|
759
|
+
}
|
760
|
+
else
|
761
|
+
{
|
762
|
+
[self fadeView:self.frameView toAlpha:0.0f];
|
763
|
+
}
|
764
|
+
}
|
765
|
+
|
766
|
+
- (void)updateStatusBar
|
767
|
+
{
|
768
|
+
if (self.currentView)
|
769
|
+
{
|
770
|
+
NSString *nameForObject = [self nameForObject:self.currentView];
|
771
|
+
|
772
|
+
// remove the 'self.' if it's there to save space
|
773
|
+
if ([nameForObject hasPrefix:@"self."])
|
774
|
+
nameForObject = [nameForObject substringFromIndex:@"self.".length];
|
775
|
+
|
776
|
+
if (self.currentView.tag != 0)
|
777
|
+
self.statusBarOverlay.leftLabel.text = [NSString stringWithFormat:@"%@ (tag: %i)", nameForObject, self.currentView.tag];
|
778
|
+
else
|
779
|
+
self.statusBarOverlay.leftLabel.text = [NSString stringWithFormat:@"%@", nameForObject];
|
780
|
+
|
781
|
+
self.statusBarOverlay.rightLabel.text = NSStringFromCGRect(self.currentView.frame);
|
782
|
+
}
|
783
|
+
else
|
784
|
+
{
|
785
|
+
self.statusBarOverlay.leftLabel.text = @"DCIntrospect";
|
786
|
+
self.statusBarOverlay.rightLabel.text = [NSString stringWithFormat:@"'%@' for help", kDCIntrospectKeysToggleHelp];
|
787
|
+
}
|
788
|
+
|
789
|
+
if (self.showStatusBarOverlay)
|
790
|
+
self.statusBarOverlay.hidden = NO;
|
791
|
+
else
|
792
|
+
self.statusBarOverlay.hidden = YES;
|
793
|
+
}
|
794
|
+
|
795
|
+
- (void)updateViews
|
796
|
+
{
|
797
|
+
// current interface orientation
|
798
|
+
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
|
799
|
+
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
|
800
|
+
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
|
801
|
+
|
802
|
+
CGFloat pi = (CGFloat)M_PI;
|
803
|
+
if (orientation == UIDeviceOrientationPortrait)
|
804
|
+
{
|
805
|
+
self.frameView.transform = CGAffineTransformIdentity;
|
806
|
+
self.frameView.frame = CGRectMake(0, 0, screenWidth, screenHeight);
|
807
|
+
}
|
808
|
+
else if (orientation == UIDeviceOrientationLandscapeLeft)
|
809
|
+
{
|
810
|
+
self.frameView.transform = CGAffineTransformMakeRotation(pi * (90) / 180.0f);
|
811
|
+
self.frameView.frame = CGRectMake(screenWidth - screenHeight, 0, screenHeight, screenHeight);
|
812
|
+
}
|
813
|
+
else if (orientation == UIDeviceOrientationLandscapeRight)
|
814
|
+
{
|
815
|
+
self.frameView.transform = CGAffineTransformMakeRotation(pi * (-90) / 180.0f);
|
816
|
+
self.frameView.frame = CGRectMake(0, 0, screenWidth, screenHeight);
|
817
|
+
}
|
818
|
+
else if (orientation == UIDeviceOrientationPortraitUpsideDown)
|
819
|
+
{
|
820
|
+
self.frameView.transform = CGAffineTransformMakeRotation(pi);
|
821
|
+
self.frameView.frame = CGRectMake(0, 0, screenWidth, screenHeight);
|
822
|
+
}
|
823
|
+
|
824
|
+
self.currentView = nil;
|
825
|
+
[self updateFrameView];
|
826
|
+
}
|
827
|
+
|
828
|
+
- (void)showTemporaryStringInStatusBar:(NSString *)string
|
829
|
+
{
|
830
|
+
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(updateStatusBar) object:nil];
|
831
|
+
|
832
|
+
self.statusBarOverlay.leftLabel.text = string;
|
833
|
+
self.statusBarOverlay.rightLabel.text = nil;
|
834
|
+
[self performSelector:@selector(updateStatusBar) withObject:nil afterDelay:0.75];
|
835
|
+
}
|
836
|
+
|
837
|
+
#pragma mark Actions
|
838
|
+
|
839
|
+
- (void)logRecursiveDescriptionForCurrentView
|
840
|
+
{
|
841
|
+
[self logRecursiveDescriptionForView:self.currentView];
|
842
|
+
}
|
843
|
+
|
844
|
+
- (void)logRecursiveDescriptionForView:(UIView *)view
|
845
|
+
{
|
846
|
+
#ifdef DEBUG
|
847
|
+
// [UIView recursiveDescription] is a private method. This should probably be re-written to avoid any potential problems.
|
848
|
+
NSLog(@"DCIntrospect: %@", [view recursiveDescription]);
|
849
|
+
#endif
|
850
|
+
}
|
851
|
+
|
852
|
+
- (void)forceSetNeedsDisplay
|
853
|
+
{
|
854
|
+
[self.currentView setNeedsDisplay];
|
855
|
+
}
|
856
|
+
|
857
|
+
- (void)forceSetNeedsLayout
|
858
|
+
{
|
859
|
+
[self.currentView setNeedsLayout];
|
860
|
+
}
|
861
|
+
|
862
|
+
- (void)forceReloadOfView
|
863
|
+
{
|
864
|
+
if ([self.currentView class] == [UITableView class])
|
865
|
+
[(UITableView *)self.currentView reloadData];
|
866
|
+
}
|
867
|
+
|
868
|
+
- (void)toggleOutlines
|
869
|
+
{
|
870
|
+
UIWindow *mainWindow = [self mainWindow];
|
871
|
+
self.viewOutlines = !self.viewOutlines;
|
872
|
+
|
873
|
+
if (self.viewOutlines)
|
874
|
+
[self addOutlinesToFrameViewFromSubview:mainWindow];
|
875
|
+
else
|
876
|
+
[self.frameView.rectsToOutline removeAllObjects];
|
877
|
+
|
878
|
+
[self.frameView setNeedsDisplay];
|
879
|
+
|
880
|
+
NSString *string = [NSString stringWithFormat:@"Showing view outlines is %@", (self.viewOutlines) ? @"on" : @"off"];
|
881
|
+
if (self.showStatusBarOverlay)
|
882
|
+
[self showTemporaryStringInStatusBar:string];
|
883
|
+
else
|
884
|
+
NSLog(@"DCIntrospect: %@", string);
|
885
|
+
}
|
886
|
+
|
887
|
+
- (void)addOutlinesToFrameViewFromSubview:(UIView *)view
|
888
|
+
{
|
889
|
+
for (UIView *subview in view.subviews)
|
890
|
+
{
|
891
|
+
if ([self shouldIgnoreView:subview])
|
892
|
+
continue;
|
893
|
+
|
894
|
+
CGRect rect = [subview.superview convertRect:subview.frame toView:frameView];
|
895
|
+
|
896
|
+
NSValue *rectValue = [NSValue valueWithCGRect:rect];
|
897
|
+
[self.frameView.rectsToOutline addObject:rectValue];
|
898
|
+
[self addOutlinesToFrameViewFromSubview:subview];
|
899
|
+
}
|
900
|
+
}
|
901
|
+
|
902
|
+
- (void)toggleNonOpaqueViews
|
903
|
+
{
|
904
|
+
self.highlightNonOpaqueViews = !self.highlightNonOpaqueViews;
|
905
|
+
|
906
|
+
UIWindow *mainWindow = [self mainWindow];
|
907
|
+
[self setBackgroundColor:(self.highlightNonOpaqueViews) ? kDCIntrospectOpaqueColor : [UIColor clearColor]
|
908
|
+
ofNonOpaqueViewsInSubview:mainWindow];
|
909
|
+
|
910
|
+
NSString *string = [NSString stringWithFormat:@"Highlighting non-opaque views is %@", (self.highlightNonOpaqueViews) ? @"on" : @"off"];
|
911
|
+
if (self.showStatusBarOverlay)
|
912
|
+
[self showTemporaryStringInStatusBar:string];
|
913
|
+
else
|
914
|
+
NSLog(@"DCIntrospect: %@", string);
|
915
|
+
}
|
916
|
+
|
917
|
+
- (void)setBackgroundColor:(UIColor *)color ofNonOpaqueViewsInSubview:(UIView *)view
|
918
|
+
{
|
919
|
+
for (UIView *subview in view.subviews)
|
920
|
+
{
|
921
|
+
if ([self shouldIgnoreView:subview])
|
922
|
+
continue;
|
923
|
+
|
924
|
+
if (!subview.opaque)
|
925
|
+
subview.backgroundColor = color;
|
926
|
+
|
927
|
+
[self setBackgroundColor:color ofNonOpaqueViewsInSubview:subview];
|
928
|
+
}
|
929
|
+
}
|
930
|
+
|
931
|
+
- (void)toggleRedrawFlashing
|
932
|
+
{
|
933
|
+
self.flashOnRedraw = !self.flashOnRedraw;
|
934
|
+
NSString *string = [NSString stringWithFormat:@"Flashing on redraw is %@", (self.flashOnRedraw) ? @"on" : @"off"];
|
935
|
+
if (self.showStatusBarOverlay)
|
936
|
+
[self showTemporaryStringInStatusBar:string];
|
937
|
+
else
|
938
|
+
NSLog(@"DCIntrospect: %@", string);
|
939
|
+
|
940
|
+
// flash all views to show what is working
|
941
|
+
[self callDrawRectOnViewsInSubview:[self mainWindow]];
|
942
|
+
}
|
943
|
+
|
944
|
+
- (void)callDrawRectOnViewsInSubview:(UIView *)subview
|
945
|
+
{
|
946
|
+
for (UIView *view in subview.subviews)
|
947
|
+
{
|
948
|
+
if (![self shouldIgnoreView:view])
|
949
|
+
{
|
950
|
+
[view setNeedsDisplay];
|
951
|
+
[self callDrawRectOnViewsInSubview:view];
|
952
|
+
}
|
953
|
+
}
|
954
|
+
}
|
955
|
+
|
956
|
+
- (void)flashRect:(CGRect)rect inView:(UIView *)view
|
957
|
+
{
|
958
|
+
if (self.flashOnRedraw)
|
959
|
+
{
|
960
|
+
CALayer *layer = [CALayer layer];
|
961
|
+
layer.frame = rect;
|
962
|
+
layer.backgroundColor = kDCIntrospectFlashOnRedrawColor.CGColor;
|
963
|
+
[view.layer addSublayer:layer];
|
964
|
+
[layer performSelector:@selector(removeFromSuperlayer) withObject:nil afterDelay:kDCIntrospectFlashOnRedrawFlashLength];
|
965
|
+
}
|
966
|
+
}
|
967
|
+
|
968
|
+
#pragma mark Description Methods
|
969
|
+
|
970
|
+
- (NSString *)describeProperty:(NSString *)propertyName value:(id)value
|
971
|
+
{
|
972
|
+
if ([propertyName isEqualToString:@"contentMode"])
|
973
|
+
{
|
974
|
+
switch ([value intValue])
|
975
|
+
{
|
976
|
+
case 0: return @"UIViewContentModeScaleToFill";
|
977
|
+
case 1: return @"UIViewContentModeScaleAspectFit";
|
978
|
+
case 2: return @"UIViewContentModeScaleAspectFill";
|
979
|
+
case 3: return @"UIViewContentModeRedraw";
|
980
|
+
case 4: return @"UIViewContentModeCenter";
|
981
|
+
case 5: return @"UIViewContentModeTop";
|
982
|
+
case 6: return @"UIViewContentModeBottom";
|
983
|
+
case 7: return @"UIViewContentModeLeft";
|
984
|
+
case 8: return @"UIViewContentModeRight";
|
985
|
+
case 9: return @"UIViewContentModeTopLeft";
|
986
|
+
case 10: return @"UIViewContentModeTopRight";
|
987
|
+
case 11: return @"UIViewContentModeBottomLeft";
|
988
|
+
case 12: return @"UIViewContentModeBottomRight";
|
989
|
+
default: return nil;
|
990
|
+
}
|
991
|
+
}
|
992
|
+
else if ([propertyName isEqualToString:@"textAlignment"])
|
993
|
+
{
|
994
|
+
switch ([value intValue])
|
995
|
+
{
|
996
|
+
case 0: return @"UITextAlignmentLeft";
|
997
|
+
case 1: return @"UITextAlignmentCenter";
|
998
|
+
case 2: return @"UITextAlignmentRight";
|
999
|
+
default: return nil;
|
1000
|
+
}
|
1001
|
+
}
|
1002
|
+
else if ([propertyName isEqualToString:@"lineBreakMode"])
|
1003
|
+
{
|
1004
|
+
switch ([value intValue])
|
1005
|
+
{
|
1006
|
+
case 0: return @"UILineBreakModeWordWrap";
|
1007
|
+
case 1: return @"UILineBreakModeCharacterWrap";
|
1008
|
+
case 2: return @"UILineBreakModeClip";
|
1009
|
+
case 3: return @"UILineBreakModeHeadTruncation";
|
1010
|
+
case 4: return @"UILineBreakModeTailTruncation";
|
1011
|
+
case 5: return @"UILineBreakModeMiddleTruncation";
|
1012
|
+
default: return nil;
|
1013
|
+
}
|
1014
|
+
}
|
1015
|
+
else if ([propertyName isEqualToString:@"activityIndicatorViewStyle"])
|
1016
|
+
{
|
1017
|
+
switch ([value intValue])
|
1018
|
+
{
|
1019
|
+
case 0: return @"UIActivityIndicatorViewStyleWhiteLarge";
|
1020
|
+
case 1: return @"UIActivityIndicatorViewStyleWhite";
|
1021
|
+
case 2: return @"UIActivityIndicatorViewStyleGray";
|
1022
|
+
default: return nil;
|
1023
|
+
}
|
1024
|
+
}
|
1025
|
+
else if ([propertyName isEqualToString:@"returnKeyType"])
|
1026
|
+
{
|
1027
|
+
switch ([value intValue])
|
1028
|
+
{
|
1029
|
+
case 0: return @"UIReturnKeyDefault";
|
1030
|
+
case 1: return @"UIReturnKeyGo";
|
1031
|
+
case 2: return @"UIReturnKeyGoogle";
|
1032
|
+
case 3: return @"UIReturnKeyJoin";
|
1033
|
+
case 4: return @"UIReturnKeyNext";
|
1034
|
+
case 5: return @"UIReturnKeyRoute";
|
1035
|
+
case 6: return @"UIReturnKeySearch";
|
1036
|
+
case 7: return @"UIReturnKeySend";
|
1037
|
+
case 8: return @"UIReturnKeyYahoo";
|
1038
|
+
case 9: return @"UIReturnKeyDone";
|
1039
|
+
case 10: return @"UIReturnKeyEmergencyCall";
|
1040
|
+
default: return nil;
|
1041
|
+
}
|
1042
|
+
}
|
1043
|
+
else if ([propertyName isEqualToString:@"keyboardAppearance"])
|
1044
|
+
{
|
1045
|
+
switch ([value intValue])
|
1046
|
+
{
|
1047
|
+
case 0: return @"UIKeyboardAppearanceDefault";
|
1048
|
+
case 1: return @"UIKeyboardAppearanceAlert";
|
1049
|
+
default: return nil;
|
1050
|
+
}
|
1051
|
+
}
|
1052
|
+
else if ([propertyName isEqualToString:@"keyboardType"])
|
1053
|
+
{
|
1054
|
+
switch ([value intValue])
|
1055
|
+
{
|
1056
|
+
case 0: return @"UIKeyboardTypeDefault";
|
1057
|
+
case 1: return @"UIKeyboardTypeASCIICapable";
|
1058
|
+
case 2: return @"UIKeyboardTypeNumbersAndPunctuation";
|
1059
|
+
case 3: return @"UIKeyboardTypeURL";
|
1060
|
+
case 4: return @"UIKeyboardTypeNumberPad";
|
1061
|
+
case 5: return @"UIKeyboardTypePhonePad";
|
1062
|
+
case 6: return @"UIKeyboardTypeNamePhonePad";
|
1063
|
+
case 7: return @"UIKeyboardTypeEmailAddress";
|
1064
|
+
case 8: return @"UIKeyboardTypeDecimalPad";
|
1065
|
+
default: return nil;
|
1066
|
+
}
|
1067
|
+
}
|
1068
|
+
else if ([propertyName isEqualToString:@"autocorrectionType"])
|
1069
|
+
{
|
1070
|
+
switch ([value intValue])
|
1071
|
+
{
|
1072
|
+
case 0: return @"UIKeyboardTypeDefault";
|
1073
|
+
case 1: return @"UITextAutocorrectionTypeDefault";
|
1074
|
+
case 2: return @"UITextAutocorrectionTypeNo";
|
1075
|
+
default: return nil;
|
1076
|
+
}
|
1077
|
+
}
|
1078
|
+
else if ([propertyName isEqualToString:@"autocapitalizationType"])
|
1079
|
+
{
|
1080
|
+
switch ([value intValue])
|
1081
|
+
{
|
1082
|
+
case 0: return @"UITextAutocapitalizationTypeNone";
|
1083
|
+
case 1: return @"UITextAutocapitalizationTypeWords";
|
1084
|
+
case 2: return @"UITextAutocapitalizationTypeSentences";
|
1085
|
+
case 3: return @"UITextAutocapitalizationTypeAllCharacters";
|
1086
|
+
default: return nil;
|
1087
|
+
}
|
1088
|
+
}
|
1089
|
+
else if ([propertyName isEqualToString:@"clearButtonMode"] ||
|
1090
|
+
[propertyName isEqualToString:@"leftViewMode"] ||
|
1091
|
+
[propertyName isEqualToString:@"rightViewMode"])
|
1092
|
+
{
|
1093
|
+
switch ([value intValue])
|
1094
|
+
{
|
1095
|
+
case 0: return @"UITextFieldViewModeNever";
|
1096
|
+
case 1: return @"UITextFieldViewModeWhileEditing";
|
1097
|
+
case 2: return @"UITextFieldViewModeUnlessEditing";
|
1098
|
+
case 3: return @"UITextFieldViewModeAlways";
|
1099
|
+
default: return nil;
|
1100
|
+
}
|
1101
|
+
}
|
1102
|
+
else if ([propertyName isEqualToString:@"borderStyle"])
|
1103
|
+
{
|
1104
|
+
switch ([value intValue])
|
1105
|
+
{
|
1106
|
+
case 0: return @"UITextBorderStyleNone";
|
1107
|
+
case 1: return @"UITextBorderStyleLine";
|
1108
|
+
case 2: return @"UITextBorderStyleBezel";
|
1109
|
+
case 3: return @"UITextBorderStyleRoundedRect";
|
1110
|
+
default: return nil;
|
1111
|
+
}
|
1112
|
+
}
|
1113
|
+
else if ([propertyName isEqualToString:@"progressViewStyle"])
|
1114
|
+
{
|
1115
|
+
switch ([value intValue])
|
1116
|
+
{
|
1117
|
+
case 0: return @"UIProgressViewStyleBar";
|
1118
|
+
case 1: return @"UIProgressViewStyleDefault";
|
1119
|
+
default: return nil;
|
1120
|
+
}
|
1121
|
+
}
|
1122
|
+
else if ([propertyName isEqualToString:@"separatorStyle"])
|
1123
|
+
{
|
1124
|
+
switch ([value intValue])
|
1125
|
+
{
|
1126
|
+
case 0: return @"UITableViewCellSeparatorStyleNone";
|
1127
|
+
case 1: return @"UITableViewCellSeparatorStyleSingleLine";
|
1128
|
+
case 2: return @"UITableViewCellSeparatorStyleSingleLineEtched";
|
1129
|
+
default: return nil;
|
1130
|
+
}
|
1131
|
+
}
|
1132
|
+
else if ([propertyName isEqualToString:@"selectionStyle"])
|
1133
|
+
{
|
1134
|
+
switch ([value intValue])
|
1135
|
+
{
|
1136
|
+
case 0: return @"UITableViewCellSelectionStyleNone";
|
1137
|
+
case 1: return @"UITableViewCellSelectionStyleBlue";
|
1138
|
+
case 2: return @"UITableViewCellSelectionStyleGray";
|
1139
|
+
default: return nil;
|
1140
|
+
}
|
1141
|
+
}
|
1142
|
+
else if ([propertyName isEqualToString:@"editingStyle"])
|
1143
|
+
{
|
1144
|
+
switch ([value intValue])
|
1145
|
+
{
|
1146
|
+
case 0: return @"UITableViewCellEditingStyleNone";
|
1147
|
+
case 1: return @"UITableViewCellEditingStyleDelete";
|
1148
|
+
case 2: return @"UITableViewCellEditingStyleInsert";
|
1149
|
+
default: return nil;
|
1150
|
+
}
|
1151
|
+
}
|
1152
|
+
else if ([propertyName isEqualToString:@"accessoryType"] || [propertyName isEqualToString:@"editingAccessoryType"])
|
1153
|
+
{
|
1154
|
+
switch ([value intValue])
|
1155
|
+
{
|
1156
|
+
case 0: return @"UITableViewCellAccessoryNone";
|
1157
|
+
case 1: return @"UITableViewCellAccessoryDisclosureIndicator";
|
1158
|
+
case 2: return @"UITableViewCellAccessoryDetailDisclosureButton";
|
1159
|
+
case 3: return @"UITableViewCellAccessoryCheckmark";
|
1160
|
+
default: return nil;
|
1161
|
+
}
|
1162
|
+
}
|
1163
|
+
else if ([propertyName isEqualToString:@"style"])
|
1164
|
+
{
|
1165
|
+
switch ([value intValue])
|
1166
|
+
{
|
1167
|
+
case 0: return @"UITableViewStylePlain";
|
1168
|
+
case 1: return @"UITableViewStyleGrouped";
|
1169
|
+
default: return nil;
|
1170
|
+
}
|
1171
|
+
|
1172
|
+
}
|
1173
|
+
else if ([propertyName isEqualToString:@"autoresizingMask"])
|
1174
|
+
{
|
1175
|
+
UIViewAutoresizing mask = [value intValue];
|
1176
|
+
NSMutableString *string = [NSMutableString string];
|
1177
|
+
if (mask & UIViewAutoresizingFlexibleLeftMargin)
|
1178
|
+
[string appendString:@"UIViewAutoresizingFlexibleLeftMargin"];
|
1179
|
+
if (mask & UIViewAutoresizingFlexibleRightMargin)
|
1180
|
+
[string appendString:@" | UIViewAutoresizingFlexibleRightMargin"];
|
1181
|
+
if (mask & UIViewAutoresizingFlexibleTopMargin)
|
1182
|
+
[string appendString:@" | UIViewAutoresizingFlexibleTopMargin"];
|
1183
|
+
if (mask & UIViewAutoresizingFlexibleBottomMargin)
|
1184
|
+
[string appendString:@" | UIViewAutoresizingFlexibleBottomMargin"];
|
1185
|
+
if (mask & UIViewAutoresizingFlexibleWidth)
|
1186
|
+
[string appendString:@" | UIViewAutoresizingFlexibleWidthMargin"];
|
1187
|
+
if (mask & UIViewAutoresizingFlexibleHeight)
|
1188
|
+
[string appendString:@" | UIViewAutoresizingFlexibleHeightMargin"];
|
1189
|
+
|
1190
|
+
if ([string hasPrefix:@" | "])
|
1191
|
+
[string replaceCharactersInRange:NSMakeRange(0, 3) withString:@""];
|
1192
|
+
|
1193
|
+
return ([string length] > 0) ? string : @"UIViewAutoresizingNone";
|
1194
|
+
}
|
1195
|
+
else if ([propertyName isEqualToString:@"accessibilityTraits"])
|
1196
|
+
{
|
1197
|
+
UIAccessibilityTraits traits = [value intValue];
|
1198
|
+
NSMutableString *string = [NSMutableString string];
|
1199
|
+
if (traits & UIAccessibilityTraitButton)
|
1200
|
+
[string appendString:@"UIAccessibilityTraitButton"];
|
1201
|
+
if (traits & UIAccessibilityTraitLink)
|
1202
|
+
[string appendString:@" | UIAccessibilityTraitLink"];
|
1203
|
+
if (traits & UIAccessibilityTraitSearchField)
|
1204
|
+
[string appendString:@" | UIAccessibilityTraitSearchField"];
|
1205
|
+
if (traits & UIAccessibilityTraitImage)
|
1206
|
+
[string appendString:@" | UIAccessibilityTraitImage"];
|
1207
|
+
if (traits & UIAccessibilityTraitSelected)
|
1208
|
+
[string appendString:@" | UIAccessibilityTraitSelected"];
|
1209
|
+
if (traits & UIAccessibilityTraitPlaysSound)
|
1210
|
+
[string appendString:@" | UIAccessibilityTraitPlaysSound"];
|
1211
|
+
if (traits & UIAccessibilityTraitKeyboardKey)
|
1212
|
+
[string appendString:@" | UIAccessibilityTraitKeyboardKey"];
|
1213
|
+
if (traits & UIAccessibilityTraitStaticText)
|
1214
|
+
[string appendString:@" | UIAccessibilityTraitStaticText"];
|
1215
|
+
if (traits & UIAccessibilityTraitSummaryElement)
|
1216
|
+
[string appendString:@" | UIAccessibilityTraitSummaryElement"];
|
1217
|
+
if (traits & UIAccessibilityTraitNotEnabled)
|
1218
|
+
[string appendString:@" | UIAccessibilityTraitNotEnabled"];
|
1219
|
+
if (traits & UIAccessibilityTraitUpdatesFrequently)
|
1220
|
+
[string appendString:@" | UIAccessibilityTraitUpdatesFrequently"];
|
1221
|
+
if (traits & UIAccessibilityTraitStartsMediaSession)
|
1222
|
+
[string appendString:@" | UIAccessibilityTraitStartsMediaSession"];
|
1223
|
+
if (traits & UIAccessibilityTraitAdjustable)
|
1224
|
+
[string appendFormat:@" | UIAccessibilityTraitAdjustable"];
|
1225
|
+
if ([string hasPrefix:@" | "])
|
1226
|
+
[string replaceCharactersInRange:NSMakeRange(0, 3) withString:@""];
|
1227
|
+
|
1228
|
+
return ([string length] > 0) ? string : @"UIAccessibilityTraitNone";
|
1229
|
+
}
|
1230
|
+
|
1231
|
+
if ([value isKindOfClass:[NSValue class]])
|
1232
|
+
{
|
1233
|
+
// print out the return for each value depending on type
|
1234
|
+
NSString *type = [NSString stringWithUTF8String:[value objCType]];
|
1235
|
+
if ([type isEqualToString:@"c"])
|
1236
|
+
{
|
1237
|
+
return ([value boolValue]) ? @"YES" : @"NO";
|
1238
|
+
}
|
1239
|
+
else if ([type isEqualToString:@"{CGSize=ff}"])
|
1240
|
+
{
|
1241
|
+
CGSize size = [value CGSizeValue];
|
1242
|
+
return CGSizeEqualToSize(size, CGSizeZero) ? @"CGSizeZero" : NSStringFromCGSize(size);
|
1243
|
+
}
|
1244
|
+
else if ([type isEqualToString:@"{UIEdgeInsets=ffff}"])
|
1245
|
+
{
|
1246
|
+
UIEdgeInsets edgeInsets = [value UIEdgeInsetsValue];
|
1247
|
+
return UIEdgeInsetsEqualToEdgeInsets(edgeInsets, UIEdgeInsetsZero) ? @"UIEdgeInsetsZero" : NSStringFromUIEdgeInsets(edgeInsets);
|
1248
|
+
}
|
1249
|
+
}
|
1250
|
+
else if ([value isKindOfClass:[UIColor class]])
|
1251
|
+
{
|
1252
|
+
UIColor *color = (UIColor *)value;
|
1253
|
+
return [self describeColor:color];
|
1254
|
+
}
|
1255
|
+
else if ([value isKindOfClass:[UIFont class]])
|
1256
|
+
{
|
1257
|
+
UIFont *font = (UIFont *)value;
|
1258
|
+
return [NSString stringWithFormat:@"%.0fpx %@", font.pointSize, font.fontName];
|
1259
|
+
}
|
1260
|
+
|
1261
|
+
return value ? [value description] : @"nil";
|
1262
|
+
}
|
1263
|
+
|
1264
|
+
- (NSString *)describeColor:(UIColor *)color
|
1265
|
+
{
|
1266
|
+
if (!color)
|
1267
|
+
return @"nil";
|
1268
|
+
|
1269
|
+
NSString *returnString = nil;
|
1270
|
+
if (CGColorSpaceGetModel(CGColorGetColorSpace(color.CGColor)) == kCGColorSpaceModelRGB)
|
1271
|
+
{
|
1272
|
+
const CGFloat *components = CGColorGetComponents(color.CGColor);
|
1273
|
+
returnString = [NSString stringWithFormat:@"R: %.0f G: %.0f B: %.0f A: %.2f",
|
1274
|
+
components[0] * 256,
|
1275
|
+
components[1] * 256,
|
1276
|
+
components[2] * 256,
|
1277
|
+
components[3]];
|
1278
|
+
}
|
1279
|
+
else
|
1280
|
+
{
|
1281
|
+
returnString = [NSString stringWithFormat:@"%@ (incompatible color space)", color];
|
1282
|
+
}
|
1283
|
+
return returnString;
|
1284
|
+
}
|
1285
|
+
|
1286
|
+
#pragma mark DCIntrospector Help
|
1287
|
+
|
1288
|
+
- (void)toggleHelp
|
1289
|
+
{
|
1290
|
+
UIWindow *mainWindow = [self mainWindow];
|
1291
|
+
self.showingHelp = !self.showingHelp;
|
1292
|
+
|
1293
|
+
if (self.showingHelp)
|
1294
|
+
{
|
1295
|
+
self.statusBarOverlay.leftLabel.text = @"Help";
|
1296
|
+
self.statusBarOverlay.rightLabel.text = @"Any key to close";
|
1297
|
+
UIView *backingView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, mainWindow.frame.size.width, mainWindow.frame.size.height)] autorelease];
|
1298
|
+
backingView.tag = 1548;
|
1299
|
+
backingView.alpha = 0;
|
1300
|
+
backingView.backgroundColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.85f];
|
1301
|
+
[mainWindow addSubview:backingView];
|
1302
|
+
|
1303
|
+
UIWebView *webView = [[[UIWebView alloc] initWithFrame:backingView.frame] autorelease];
|
1304
|
+
webView.opaque = NO;
|
1305
|
+
webView.backgroundColor = [UIColor clearColor];
|
1306
|
+
webView.delegate = self;
|
1307
|
+
[backingView addSubview:webView];
|
1308
|
+
|
1309
|
+
NSMutableString *helpString = [NSMutableString stringWithString:@"<html>"];
|
1310
|
+
[helpString appendString:@"<head><style>"];
|
1311
|
+
[helpString appendString:@"body { background-color:rgba(0, 0, 0, 0.0); font:10pt helvetica; line-height: 15px margin-left:5px; margin-right:5px; margin-top:20px; color:rgb(240, 240, 240); } a { color:#45e0fe; font-weight:bold; } h1 { width:100%; font-size:14pt; border-bottom: 1px solid white; margin-top:22px; } h2 { font-size:11pt; margin-left:3px; margin-bottom:2px; } .name { margin-left:7px; } .key { float:right; margin-right:7px; } .key, .code { font-family:Courier; font-weight:bold; color:#CE8B39; } .spacer { height:10px; } p { margin-left: 7px; margin-right: 7px; }"];
|
1312
|
+
|
1313
|
+
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
|
1314
|
+
[helpString appendString:@"body { font-size:11pt; width:500px; margin:0 auto; }"];
|
1315
|
+
|
1316
|
+
[helpString appendString:@"</style></head><body><h1>DCIntrospect</h1>"];
|
1317
|
+
[helpString appendString:@"<p>Created by <a href='http://domesticcat.com.au'>Domestic Cat Software</a> 2011.</p>"];
|
1318
|
+
[helpString appendString:@"<p>Twitter: <a href='http://twitter.com/patr'>@patr</a></p>"];
|
1319
|
+
[helpString appendString:@"<p>More info and full documentation: <a href='http://domesticcat.com.au/projects/introspect'>domesticcat.com.au/projects/introspect</a></p>"];
|
1320
|
+
[helpString appendString:@"<p>GitHub project: <a href='https://github.com/domesticcatsoftware/dcintrospect'>github.com/domesticcatsoftware/dcintrospect/</a></p>"];
|
1321
|
+
|
1322
|
+
[helpString appendString:@"<div class='bindings'><h1>Key Bindings</h1>"];
|
1323
|
+
[helpString appendString:@"<p>Edit DCIntrospectSettings.h to change key bindings.</p>"];
|
1324
|
+
|
1325
|
+
[helpString appendString:@"<h2>General</h2>"];
|
1326
|
+
|
1327
|
+
[helpString appendFormat:@"<div><span class='name'>Invoke Introspector</span><div class='key'>%@</div></div>", ([kDCIntrospectKeysInvoke isEqualToString:@" "]) ? @"spacebar" : kDCIntrospectKeysInvoke];
|
1328
|
+
[helpString appendFormat:@"<div><span class='name'>Toggle View Outlines</span><div class='key'>%@</div></div>", kDCIntrospectKeysToggleViewOutlines];
|
1329
|
+
[helpString appendFormat:@"<div><span class='name'>Toggle Highlighting Non-Opaque Views</span><div class='key'>%@</div></div>", kDCIntrospectKeysToggleNonOpaqueViews];
|
1330
|
+
[helpString appendFormat:@"<div><span class='name'>Toggle Help</span><div class='key'>%@</div></div>", kDCIntrospectKeysToggleHelp];
|
1331
|
+
[helpString appendFormat:@"<div><span class='name'>Toggle flash on <span class='code'>drawRect:</span> (see below)</span><div class='key'>%@</div></div>", kDCIntrospectKeysToggleFlashViewRedraws];
|
1332
|
+
[helpString appendFormat:@"<div><span class='name'>Toggle coordinates</span><div class='key'>%@</div></div>", kDCIntrospectKeysToggleShowCoordinates];
|
1333
|
+
[helpString appendString:@"<div class='spacer'></div>"];
|
1334
|
+
|
1335
|
+
[helpString appendString:@"<h2>When a view is selected</h2>"];
|
1336
|
+
[helpString appendFormat:@"<div><span class='name'>Log Properties</span><div class='key'>%@</div></div>", kDCIntrospectKeysLogProperties];
|
1337
|
+
[helpString appendFormat:@"<div><span class='name'>Log Accessibility Properties</span><div class='key'>%@</div></div>", kDCIntrospectKeysLogAccessibilityProperties];
|
1338
|
+
[helpString appendFormat:@"<div><span class='name'>Log Recursive Description for View</span><div class='key'>%@</div></div>", kDCIntrospectKeysLogViewRecursive];
|
1339
|
+
[helpString appendFormat:@"<div><span class='name'>Enter GDB</span><div class='key'>%@</div></div>", kDCIntrospectKeysEnterGDB];
|
1340
|
+
[helpString appendFormat:@"<div><span class='name'>Move up in view hierarchy</span><div class='key'>%@</div></div>", ([kDCIntrospectKeysMoveUpInViewHierarchy isEqualToString:@""]) ? @"page up" : kDCIntrospectKeysMoveUpInViewHierarchy];
|
1341
|
+
[helpString appendFormat:@"<div><span class='name'>Move back down in view hierarchy</span><div class='key'>%@</div></div>", ([kDCIntrospectKeysMoveBackInViewHierarchy isEqualToString:@""]) ? @"page down" : kDCIntrospectKeysMoveBackInViewHierarchy];
|
1342
|
+
[helpString appendString:@"<div class='spacer'></div>"];
|
1343
|
+
|
1344
|
+
[helpString appendFormat:@"<div><span class='name'>Nudge Left</span><div class='key'>\uE235 / %@</div></div>", kDCIntrospectKeysNudgeViewLeft];
|
1345
|
+
[helpString appendFormat:@"<div><span class='name'>Nudge Right</span><div class='key'>\uE234 / %@</div></div>", kDCIntrospectKeysNudgeViewRight];
|
1346
|
+
[helpString appendFormat:@"<div><span class='name'>Nudge Up</span><div class='key'>\uE232 / %@</div></div>", kDCIntrospectKeysNudgeViewUp];
|
1347
|
+
[helpString appendFormat:@"<div><span class='name'>Nudge Down</span><div class='key'>\uE233 / %@</div></div>", kDCIntrospectKeysNudgeViewDown];
|
1348
|
+
[helpString appendFormat:@"<div><span class='name'>Center in Superview</span><div class='key'>%@</div></div>", kDCIntrospectKeysCenterInSuperview];
|
1349
|
+
[helpString appendFormat:@"<div><span class='name'>Increase Width</span><div class='key'>alt + \uE234 / %@</div></div>", kDCIntrospectKeysIncreaseWidth];
|
1350
|
+
[helpString appendFormat:@"<div><span class='name'>Decrease Width</span><div class='key'>alt + \uE235 / %@</div></div>", kDCIntrospectKeysDecreaseWidth];
|
1351
|
+
[helpString appendFormat:@"<div><span class='name'>Increase Height</span><div class='key'>alt + \uE233 / %@</div></div>", kDCIntrospectKeysIncreaseHeight];
|
1352
|
+
[helpString appendFormat:@"<div><span class='name'>Decrease Height</span><div class='key'>alt + \uE232 / %@</div></div>", kDCIntrospectKeysDecreaseHeight];
|
1353
|
+
[helpString appendFormat:@"<div><span class='name'>Increase Alpha</span><div class='key'>%@</div></div>", kDCIntrospectKeysIncreaseViewAlpha];
|
1354
|
+
[helpString appendFormat:@"<div><span class='name'>Decrease Alpha</span><div class='key'>%@</div></div>", kDCIntrospectKeysDecreaseViewAlpha];
|
1355
|
+
[helpString appendFormat:@"<div><span class='name'>Log view code</span><div class='key'>%@</div></div>", kDCIntrospectKeysLogCodeForCurrentViewChanges];
|
1356
|
+
[helpString appendString:@"<div class='spacer'></div>"];
|
1357
|
+
|
1358
|
+
[helpString appendFormat:@"<div><span class='name'>Call setNeedsDisplay</span><div class='key'>%@</div></div>", kDCIntrospectKeysSetNeedsDisplay];
|
1359
|
+
[helpString appendFormat:@"<div><span class='name'>Call setNeedsLayout</span><div class='key'>%@</div></div>", kDCIntrospectKeysSetNeedsLayout];
|
1360
|
+
[helpString appendFormat:@"<div><span class='name'>Call reloadData (UITableView only)</span><div class='key'>%@</div></div>", kDCIntrospectKeysReloadData];
|
1361
|
+
[helpString appendString:@"</div>"];
|
1362
|
+
|
1363
|
+
[helpString appendFormat:@"<h1>GDB</h1><p>Push <span class='code'>%@</span> (backtick) to jump into GDB. The currently selected view will be available as a variable named 'view'.</p>", kDCIntrospectKeysEnterGDB];
|
1364
|
+
|
1365
|
+
[helpString appendFormat:@"<h1>Flash on <span class='code'>drawRect:</span> calls</h1><p>To implement, call <span class='code'>[[DCIntrospect sharedIntrospector] flashRect:inView:]</span> inside the <span class='code'>drawRect:</span> method of any view you want to track.</p><p>When Flash on <span class='code'>drawRect:</span> is toggled on (binding: <span class='code'>%@</span>) the view will flash whenever <span class='code'>drawRect:</span> is called.</p>", kDCIntrospectKeysToggleFlashViewRedraws];
|
1366
|
+
|
1367
|
+
[helpString appendFormat:@"<h1>Naming objects & logging code</h1><p>By providing names for objects using <span class='code'>setName:forObject:accessedWithSelf:</span>, that name will be shown in the status bar instead of the class of the view.</p><p>This is also used when logging view code (binding: <span class='code'>%@</span>). Logging view code prints formatted code to the console for properties that have been changed.</p><p>For example, if you resize/move a view using the nudge keys, logging the view code will print <span class='code'>view.frame = CGRectMake(50.0 ..etc);</span> to the console. If a name is provided then <span class='code'>view</span> is replaced by the name.</p>", kDCIntrospectKeysLogCodeForCurrentViewChanges];
|
1368
|
+
|
1369
|
+
[helpString appendString:@"<h1>License</h1><p>DCIntrospect is made available under the <a href='http://en.wikipedia.org/wiki/MIT_License'>MIT license</a>.</p>"];
|
1370
|
+
|
1371
|
+
[helpString appendString:@"<h2 style='text-align:center;'><a href='http://close'>Close Help</h2>"];
|
1372
|
+
[helpString appendString:@"<div class='spacer'></div>"];
|
1373
|
+
|
1374
|
+
[UIView animateWithDuration:0.1
|
1375
|
+
animations:^{
|
1376
|
+
backingView.alpha = 1.0f;
|
1377
|
+
} completion:^(BOOL finished) {
|
1378
|
+
[webView loadHTMLString:helpString baseURL:nil];
|
1379
|
+
}];
|
1380
|
+
}
|
1381
|
+
else
|
1382
|
+
{
|
1383
|
+
UIView *backingView = (UIView *)[mainWindow viewWithTag:1548];
|
1384
|
+
[UIView animateWithDuration:0.1
|
1385
|
+
animations:^{
|
1386
|
+
backingView.alpha = 0;
|
1387
|
+
} completion:^(BOOL finished) {
|
1388
|
+
[backingView removeFromSuperview];
|
1389
|
+
}];
|
1390
|
+
[self updateStatusBar];
|
1391
|
+
}
|
1392
|
+
}
|
1393
|
+
|
1394
|
+
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
|
1395
|
+
{
|
1396
|
+
NSString *requestString = [[request URL] absoluteString];
|
1397
|
+
if ([requestString isEqualToString:@"about:blank"])
|
1398
|
+
return YES;
|
1399
|
+
else if ([requestString isEqualToString:@"http://close/"])
|
1400
|
+
[self toggleHelp];
|
1401
|
+
else
|
1402
|
+
[[UIApplication sharedApplication] openURL:[request URL]];
|
1403
|
+
|
1404
|
+
return NO;
|
1405
|
+
}
|
1406
|
+
|
1407
|
+
#pragma mark Experimental
|
1408
|
+
|
1409
|
+
- (void)logPropertiesForCurrentView
|
1410
|
+
{
|
1411
|
+
[self logPropertiesForObject:self.currentView];
|
1412
|
+
}
|
1413
|
+
|
1414
|
+
- (void)logPropertiesForObject:(id)object
|
1415
|
+
{
|
1416
|
+
Class objectClass = [object class];
|
1417
|
+
NSString *className = [NSString stringWithFormat:@"%@", objectClass];
|
1418
|
+
|
1419
|
+
NSMutableString *outputString = [NSMutableString stringWithFormat:@"\n\n** %@", className];
|
1420
|
+
|
1421
|
+
// list the class heirachy
|
1422
|
+
Class superClass = [objectClass superclass];
|
1423
|
+
while (superClass)
|
1424
|
+
{
|
1425
|
+
[outputString appendFormat:@" : %@", superClass];
|
1426
|
+
superClass = [superClass superclass];
|
1427
|
+
}
|
1428
|
+
[outputString appendString:@" ** \n"];
|
1429
|
+
|
1430
|
+
// dump properties of class and super classes, up to UIView
|
1431
|
+
NSMutableString *propertyString = [NSMutableString string];
|
1432
|
+
|
1433
|
+
Class inspectClass = objectClass;
|
1434
|
+
while (inspectClass)
|
1435
|
+
{
|
1436
|
+
NSMutableString *objectString = [NSMutableString string];
|
1437
|
+
[objectString appendFormat:@"\n ** %@ properties **\n", inspectClass];
|
1438
|
+
|
1439
|
+
if (inspectClass == UIView.class)
|
1440
|
+
{
|
1441
|
+
UIView *view = (UIView *)object;
|
1442
|
+
// print out generic uiview properties
|
1443
|
+
[objectString appendFormat:@" tag: %i\n", view.tag];
|
1444
|
+
[objectString appendFormat:@" frame: %@ | ", NSStringFromCGRect(view.frame)];
|
1445
|
+
[objectString appendFormat:@"bounds: %@ | ", NSStringFromCGRect(view.bounds)];
|
1446
|
+
[objectString appendFormat:@"center: %@\n", NSStringFromCGPoint(view.center)];
|
1447
|
+
[objectString appendFormat:@" transform: %@\n", NSStringFromCGAffineTransform(view.transform)];
|
1448
|
+
[objectString appendFormat:@" autoresizingMask: %@\n", [self describeProperty:@"autoresizingMask" value:[NSNumber numberWithInt:view.autoresizingMask]]];
|
1449
|
+
[objectString appendFormat:@" autoresizesSubviews: %@\n", (view.autoresizesSubviews) ? @"YES" : @"NO"];
|
1450
|
+
[objectString appendFormat:@" contentMode: %@ | ", [self describeProperty:@"contentMode" value:[NSNumber numberWithInt:view.contentMode]]];
|
1451
|
+
[objectString appendFormat:@"contentStretch: %@\n", NSStringFromCGRect(view.contentStretch)];
|
1452
|
+
[objectString appendFormat:@" backgroundColor: %@\n", [self describeColor:view.backgroundColor]];
|
1453
|
+
[objectString appendFormat:@" alpha: %.2f | ", view.alpha];
|
1454
|
+
[objectString appendFormat:@"opaque: %@ | ", (view.opaque) ? @"YES" : @"NO"];
|
1455
|
+
[objectString appendFormat:@"hidden: %@ | ", (view.hidden) ? @"YES" : @"NO"];
|
1456
|
+
[objectString appendFormat:@"clips to bounds: %@ | ", (view.clipsToBounds) ? @"YES" : @"NO"];
|
1457
|
+
[objectString appendFormat:@"clearsContextBeforeDrawing: %@\n", (view.clearsContextBeforeDrawing) ? @"YES" : @"NO"];
|
1458
|
+
[objectString appendFormat:@" userInteractionEnabled: %@ | ", (view.userInteractionEnabled) ? @"YES" : @"NO"];
|
1459
|
+
[objectString appendFormat:@"multipleTouchEnabled: %@\n", (view.multipleTouchEnabled) ? @"YES" : @"NO"];
|
1460
|
+
[objectString appendFormat:@" gestureRecognizers: %@\n", (view.gestureRecognizers) ? [view.gestureRecognizers description] : @"nil"];
|
1461
|
+
}
|
1462
|
+
else
|
1463
|
+
{
|
1464
|
+
// Dump all properties of the class
|
1465
|
+
unsigned int count;
|
1466
|
+
objc_property_t *properties = class_copyPropertyList(inspectClass, &count);
|
1467
|
+
size_t buf_size = 1024;
|
1468
|
+
char *buffer = malloc(buf_size);
|
1469
|
+
|
1470
|
+
for (unsigned int i = 0; i < count; ++i)
|
1471
|
+
{
|
1472
|
+
// get the property name and selector name
|
1473
|
+
NSString *propertyName = [NSString stringWithCString:property_getName(properties[i]) encoding:NSUTF8StringEncoding];
|
1474
|
+
|
1475
|
+
SEL sel = NSSelectorFromString(propertyName);
|
1476
|
+
if ([object respondsToSelector:sel])
|
1477
|
+
{
|
1478
|
+
NSString *propertyDescription;
|
1479
|
+
@try
|
1480
|
+
{
|
1481
|
+
// get the return object and type for the selector
|
1482
|
+
NSString *returnType = [NSString stringWithUTF8String:[[object methodSignatureForSelector:sel] methodReturnType]];
|
1483
|
+
id returnObject = [object valueForKey:propertyName];
|
1484
|
+
if ([returnType isEqualToString:@"c"])
|
1485
|
+
returnObject = [NSNumber numberWithBool:[returnObject intValue] != 0];
|
1486
|
+
|
1487
|
+
propertyDescription = [self describeProperty:propertyName value:returnObject];
|
1488
|
+
}
|
1489
|
+
@catch (NSException *exception)
|
1490
|
+
{
|
1491
|
+
// Non KVC compliant properties, see also +workaroundUITextInputTraitsPropertiesBug
|
1492
|
+
propertyDescription = @"N/A";
|
1493
|
+
}
|
1494
|
+
[objectString appendFormat:@" %@: %@\n", propertyName, propertyDescription];
|
1495
|
+
}
|
1496
|
+
}
|
1497
|
+
|
1498
|
+
free(properties);
|
1499
|
+
free(buffer);
|
1500
|
+
}
|
1501
|
+
|
1502
|
+
[propertyString insertString:objectString atIndex:0];
|
1503
|
+
|
1504
|
+
if (inspectClass == UIView.class)
|
1505
|
+
{
|
1506
|
+
break;
|
1507
|
+
}
|
1508
|
+
|
1509
|
+
inspectClass = [inspectClass superclass];
|
1510
|
+
}
|
1511
|
+
|
1512
|
+
[outputString appendString:propertyString];
|
1513
|
+
|
1514
|
+
// list targets if there are any
|
1515
|
+
if ([object respondsToSelector:@selector(allTargets)])
|
1516
|
+
{
|
1517
|
+
[outputString appendString:@"\n ** Targets & Actions **\n"];
|
1518
|
+
UIControl *control = (UIControl *)object;
|
1519
|
+
UIControlEvents controlEvents = [control allControlEvents];
|
1520
|
+
NSSet *allTargets = [control allTargets];
|
1521
|
+
[allTargets enumerateObjectsUsingBlock:^(id target, BOOL *stop)
|
1522
|
+
{
|
1523
|
+
NSArray *actions = [control actionsForTarget:target forControlEvent:controlEvents];
|
1524
|
+
[actions enumerateObjectsUsingBlock:^(id action, NSUInteger idx, BOOL *stop2)
|
1525
|
+
{
|
1526
|
+
[outputString appendFormat:@" target: %@ action: %@\n", target, action];
|
1527
|
+
}];
|
1528
|
+
}];
|
1529
|
+
}
|
1530
|
+
|
1531
|
+
[outputString appendString:@"\n"];
|
1532
|
+
NSLog(@"DCIntrospect: %@", outputString);
|
1533
|
+
}
|
1534
|
+
|
1535
|
+
- (void)logAccessabilityPropertiesForObject:(id)object
|
1536
|
+
{
|
1537
|
+
Class objectClass = [object class];
|
1538
|
+
NSString *className = [NSString stringWithFormat:@"%@", objectClass];
|
1539
|
+
NSMutableString *outputString = [NSMutableString string];
|
1540
|
+
|
1541
|
+
// warn about accessibility inspector if the element count is zero
|
1542
|
+
NSUInteger count = [object accessibilityElementCount];
|
1543
|
+
if (count == 0)
|
1544
|
+
[outputString appendString:@"\n\n** Warning: Logging accessibility properties requires Accessibility Inspector: Settings.app -> General -> Accessibility\n"];
|
1545
|
+
|
1546
|
+
[outputString appendFormat:@"** %@ Accessibility Properties **\n", className];
|
1547
|
+
[outputString appendFormat:@" label: %@\n", [object accessibilityLabel]];
|
1548
|
+
[outputString appendFormat:@" hint: %@\n", [object accessibilityHint]];
|
1549
|
+
[outputString appendFormat:@" traits: %@\n", [self describeProperty:@"accessibilityTraits" value:[NSNumber numberWithUnsignedLongLong:[object accessibilityTraits]]]];
|
1550
|
+
[outputString appendFormat:@" value: %@\n", [object accessibilityValue]];
|
1551
|
+
[outputString appendFormat:@" frame: %@\n", NSStringFromCGRect([object accessibilityFrame])];
|
1552
|
+
[outputString appendString:@"\n"];
|
1553
|
+
|
1554
|
+
NSLog(@"DCIntrospect: %@", outputString);
|
1555
|
+
}
|
1556
|
+
|
1557
|
+
- (NSArray *)subclassesOfClass:(Class)parentClass
|
1558
|
+
{
|
1559
|
+
// thanks to Matt Gallagher:
|
1560
|
+
int numClasses = objc_getClassList(NULL, 0);
|
1561
|
+
Class *classes = NULL;
|
1562
|
+
|
1563
|
+
classes = malloc(sizeof(Class) * numClasses);
|
1564
|
+
numClasses = objc_getClassList(classes, numClasses);
|
1565
|
+
|
1566
|
+
NSMutableArray *result = [NSMutableArray array];
|
1567
|
+
for (NSInteger i = 0; i < numClasses; i++)
|
1568
|
+
{
|
1569
|
+
Class superClass = classes[i];
|
1570
|
+
do
|
1571
|
+
{
|
1572
|
+
superClass = class_getSuperclass(superClass);
|
1573
|
+
} while(superClass && superClass != parentClass);
|
1574
|
+
|
1575
|
+
if (superClass == nil)
|
1576
|
+
{
|
1577
|
+
continue;
|
1578
|
+
}
|
1579
|
+
|
1580
|
+
[result addObject:classes[i]];
|
1581
|
+
}
|
1582
|
+
|
1583
|
+
free(classes);
|
1584
|
+
|
1585
|
+
return result;
|
1586
|
+
}
|
1587
|
+
|
1588
|
+
#pragma mark Helper Methods
|
1589
|
+
|
1590
|
+
- (UIWindow *)mainWindow
|
1591
|
+
{
|
1592
|
+
NSArray *windows = [[UIApplication sharedApplication] windows];
|
1593
|
+
if (windows.count == 0)
|
1594
|
+
return nil;
|
1595
|
+
|
1596
|
+
return [windows objectAtIndex:0];
|
1597
|
+
}
|
1598
|
+
|
1599
|
+
- (NSMutableArray *)viewsAtPoint:(CGPoint)touchPoint inView:(UIView *)view
|
1600
|
+
{
|
1601
|
+
NSMutableArray *views = [[NSMutableArray alloc] init];
|
1602
|
+
for (UIView *subview in view.subviews)
|
1603
|
+
{
|
1604
|
+
CGRect rect = subview.frame;
|
1605
|
+
if ([self shouldIgnoreView:subview])
|
1606
|
+
continue;
|
1607
|
+
|
1608
|
+
if (CGRectContainsPoint(rect, touchPoint))
|
1609
|
+
{
|
1610
|
+
[views addObject:subview];
|
1611
|
+
|
1612
|
+
// convert the point to it's superview
|
1613
|
+
CGPoint newTouchPoint = touchPoint;
|
1614
|
+
newTouchPoint = [view convertPoint:newTouchPoint toView:subview];
|
1615
|
+
[views addObjectsFromArray:[self viewsAtPoint:newTouchPoint inView:subview]];
|
1616
|
+
}
|
1617
|
+
}
|
1618
|
+
|
1619
|
+
return [views autorelease];
|
1620
|
+
}
|
1621
|
+
|
1622
|
+
- (void)fadeView:(UIView *)view toAlpha:(CGFloat)alpha
|
1623
|
+
{
|
1624
|
+
[UIView animateWithDuration:0.1
|
1625
|
+
delay:0.0
|
1626
|
+
options:UIViewAnimationOptionAllowUserInteraction
|
1627
|
+
animations:^{
|
1628
|
+
view.alpha = alpha;
|
1629
|
+
}
|
1630
|
+
completion:nil];
|
1631
|
+
}
|
1632
|
+
|
1633
|
+
- (BOOL)view:(UIView *)view containsSubview:(UIView *)subview
|
1634
|
+
{
|
1635
|
+
for (UIView *aView in view.subviews)
|
1636
|
+
{
|
1637
|
+
if (aView == subview)
|
1638
|
+
return YES;
|
1639
|
+
|
1640
|
+
if ([self view:aView containsSubview:subview])
|
1641
|
+
return YES;
|
1642
|
+
}
|
1643
|
+
|
1644
|
+
return NO;
|
1645
|
+
}
|
1646
|
+
|
1647
|
+
- (BOOL)shouldIgnoreView:(UIView *)view
|
1648
|
+
{
|
1649
|
+
if (view == self.frameView || view == self.inputTextView)
|
1650
|
+
return YES;
|
1651
|
+
return NO;
|
1652
|
+
}
|
1653
|
+
|
1654
|
+
@end
|