I am setting up unit tests on my project to make sure that all UIViewController IBOutlets are connected to their respective Xib objects (i.e., are not nil after viewDidLoad.) I was considering applying a protocol to these UIViewControllers with a required function "getAllOutletNames", like so:
-(NSArray*)getAllOutletNames
{
return @[ @"outletproperty1", @"outletProperty2", ...];
}
...and then using [viewController valueForKey:outletName] to make sure none of these are nil. The problem with this is that it's a little unwieldy; "getAllOutletNames" has to be updated for each outlet added to the xib, which could easily be overlooked. I would prefer to do this in programmatic fashion, so that all IBOutlet properties can be automatically detected and iterated over.
I read in this NSHipster article (cmd+f for "attribute-backed attributes") that an attribute is applied to IBOutlets (or, an "attribute-backed attribute", which I don't quite understand.)
It looks like I can get a list of all properties in a class using part of this answer, and I can get their attributes using part of this answer. But, printing out the attributes from an IBOutlet versus a non-IBOutlet property using the following code, I don't find any difference:
const char * type = property_getAttributes(class_getProperty([self class], [outletName UTF8String]));
NSString * typeString = [NSString stringWithUTF8String:type];
NSArray * attributes = [typeString componentsSeparatedByString:@","];
NSLog(@"%@",attributes);
IBOutlet
(
"T@\"UILabel\"",
"&",
N,
"V_titleLabel"
)
Non-IBOutlet
(
"T@\"UIView\"",
"&",
N,
"V_programmaticallySetupView"
)
Is there any way to access this "attribute-backed attribute" that the NSHipster article had mentioned or otherwise determine if a property is an IBOutlet programmatically, or am I barking up the wrong tree here?
Here's a trick I've used before to mark methods as special, it can be adopted to properties as well:
#import <objc/runtime.h>
#define synthesize_nooutlet(name) \
synthesize name=_ ## name; \
- (oneway id)name { \
return _ ## name; \
}
@interface ViewController ()
@property (nonatomic, strong) UIView *theView;
@end
@implementation ViewController
@synthesize_nooutlet(theView);
- (void) viewDidLoad {
[super viewDidLoad];
[self.theView setBackgroundColor:[UIColor clearColor]];
char returnType;
method_getReturnType(class_getInstanceMethod([self class], @selector(theView)), &returnType, 1);
if (returnType == 'V') {
// Do stuff
}
}
@end
This works because of the distributed objects type encodings, documented here:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-BABHAIFA
You can use any of these to denote methods however you'd like, and as distributed objects don't exist on iOS, the compiler & runtime just ignore them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With