Skip to content

Instantly share code, notes, and snippets.

@michaeleisel
Last active November 5, 2017 04:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michaeleisel/056f553ad738a612dfbff2906db73194 to your computer and use it in GitHub Desktop.
Save michaeleisel/056f553ad738a612dfbff2906db73194 to your computer and use it in GitHub Desktop.
// HOW TO USE:
// This makes debugging auto-layout easier by telling you the variable names of the views in your constraints
// Search for "hasPrefix:" in this file and fill in the prefix for your app
// FYI: in order to do this, it swizzles -description and -debugDescription of NSLayoutConstraint in a +load method when you start the app
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
@interface UIView (SCIvars)
- (NSString *)aaa_ivarName;
@end
@implementation UIView (SCIvars)
- (NSString *)aaa_ivarName
{
if ([[self nextResponder] isKindOfClass:[UIViewController class]]) {
return [NSString stringWithFormat:@"%@.view", [[self nextResponder] class]];
}
for (UIResponder *responder = [self nextResponder]; responder != nil; responder = [responder nextResponder]) {
for (Class aClass = [responder class]; aClass != [UIResponder class]; aClass = [aClass superclass]) {
// E.g., UIViewController may store a view to a "_view" ivar, but that's not what we're interested in
if (![NSStringFromClass(aClass) hasPrefix:]) { // NOTE: You have to fill in the prefix that you use in your project here, e.g. "NS" for foundation. Or, you can simply filter out any classes that start with "UI"
continue;
}
unsigned int outCount;
Ivar *ivars = class_copyIvarList([responder class], &outCount);
for (NSInteger i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
if (ivar_getTypeEncoding(ivar)[0] == '@') {
id object = object_getIvar(responder, ivar);
if (object == self) {
return [NSString stringWithUTF8String:ivar_getName(ivar)];
}
}
}
}
}
return nil;
}
@end
@interface NSLayoutConstraint (SCDebugging)
@end
@implementation NSLayoutConstraint (SCDebugging)
- (NSString *)aaa_debugDescription
{
return [self description]; // This would actually be calling aaa_description's implementation, because of the swizzling
}
- (NSString *)_aaa_stringForAttribute:(NSLayoutAttribute)attribute
{
switch (attribute) {
case NSLayoutAttributeLeft:
return @"left";
case NSLayoutAttributeRight:
return @"right";
case NSLayoutAttributeTop:
return @"top";
case NSLayoutAttributeBottom:
return @"bottom";
case NSLayoutAttributeLeading:
return @"leading";
case NSLayoutAttributeTrailing:
return @"trailing";
case NSLayoutAttributeWidth:
return @"width";
case NSLayoutAttributeHeight:
return @"height";
case NSLayoutAttributeCenterX:
return @"centerX";
case NSLayoutAttributeCenterY:
return @"centerY";
// baseline and last baseline are the same enum value
// case NSLayoutAttributeLastBaseline:
case NSLayoutAttributeBaseline:
return @"baseline";
case NSLayoutAttributeFirstBaseline:
return @"firstBaseline";
case NSLayoutAttributeLeftMargin:
return @"leftMargin";
case NSLayoutAttributeRightMargin:
return @"rightMargin";
case NSLayoutAttributeTopMargin:
return @"topMargin";
case NSLayoutAttributeBottomMargin:
return @"bottomMargin";
case NSLayoutAttributeLeadingMargin:
return @"leadingMargin";
case NSLayoutAttributeTrailingMargin:
return @"trailingMargin";
case NSLayoutAttributeCenterXWithinMargins:
return @"centerXWithinMargins";
case NSLayoutAttributeCenterYWithinMargins:
return @"centerYWithinMargins";
case NSLayoutAttributeNotAnAttribute:
break;
}
return @"(invalid attribute)";
}
- (NSString *)_aaa_descriptionForItem:(id)item attribute:(NSLayoutAttribute)attribute
{
NSString *itemString;
if ([item isKindOfClass:[UIView class]]) {
NSString *ivarName = [item aaa_ivarName];
if (ivarName) {
itemString = ivarName;
} else {
itemString = [NSString stringWithFormat:@"<%@: %p>", [item class], item];
}
} else {
itemString = [item description];
}
if (attribute == NSLayoutAttributeNotAnAttribute) {
return itemString;
}
NSString *attributeString = [self _aaa_stringForAttribute:attribute];
return [NSString stringWithFormat:@"%@.%@", itemString, attributeString];
}
- (NSString *)aaa_description
{
NSMutableString *description = [NSMutableString string];
[description appendFormat:@"%p ", self];
[description appendString:[self _aaa_descriptionForItem:self.firstItem attribute:self.firstAttribute]];
[description appendString:@" "];
NSString *relationString;
switch (self.relation) {
case NSLayoutRelationEqual:
relationString = @"==";
break;
case NSLayoutRelationLessThanOrEqual:
relationString = @"<=";
break;
case NSLayoutRelationGreaterThanOrEqual:
relationString = @">=";
break;
default:
break;
}
[description appendString:relationString];
[description appendString:@" "];
if (self.secondItem) {
[description appendString:[self _aaa_descriptionForItem:self.secondItem attribute:self.secondAttribute]];
if (self.constant < 0) {
[description appendFormat:@" - %@ ", @(-self.constant)];
[description appendFormat:@" "];
} else if (self.constant > 0) {
[description appendFormat:@" + %@ ", @(self.constant)];
}
} else {
[description appendFormat:@"%@", @(self.constant)];
}
if (self.priority != UILayoutPriorityRequired) {
[description appendFormat:@" (priority %@)", @(self.priority)];
}
return [description copy];
}
// Only works for instance methods
+ (void)aaa_replaceOriginalSelector:(SEL)originalSelector withSwizzledSelector:(SEL)swizzledSelector
{
Method originalMethod = class_getInstanceMethod([self class], originalSelector);
Method swizzledMethod = class_getInstanceMethod([self class], swizzledSelector);
BOOL didAddMethod =
class_addMethod([self class],
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod([self class],
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self aaa_replaceOriginalSelector:@selector(description) withSwizzledSelector:@selector(aaa_description)];
[self aaa_replaceOriginalSelector:@selector(debugDescription) withSwizzledSelector:@selector(aaa_debugDescription)];
});
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment