I am looking to add a new IBInspectable
attribute (computed property) to UILabel
via a category method. Ideally I want this attribute to be set after the labels text is set (via setValue:forKey), as this IBInspectable attribute may result in the the UILabels text being updated and we don't want the text in the UILabel
to later replace it. Looking at the documentation there is no mention if pre-defined attributes are always set set before user defined attributes during the nib/storyboard
load for attributes configured in Interface Builder.
Are custom attributes added to an object in Interface Builder using IBInspectable
or User defined runtime attributes guaranteed to be set after the standard pre-defined objects properties/attributes?
The following experiment concludes that the native text property is set prior to the category property, and so the value can be safely overwritten by the category setter.
A label category:
// UILabel+Thingy.h
#import <UIKit/UIKit.h>
@interface UILabel (Thingy)
@property (nonatomic, strong) IBInspectable NSString *thingy;
@end
// UILabel+UILabel_Thingy.m
#import "UILabel+Thingy.h"
#import <objc/runtime.h>
@implementation UILabel (Thingy)
- (NSString *)thingy {
return objc_getAssociatedObject(self, @selector(thingy));
}
- (void)setThingy:(NSString *)thingy {
NSLog(@"setting thingy to '%@', my text is currently '%@'", thingy, self.text);
objc_setAssociatedObject(self, @selector(thingy), thingy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
In IB, set the inspectable category property and the text property....
A little instrumentation in the containing view controller:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"didLoad text is '%@' and thingy is '%@'", self.label.text, self.label.thingy);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"willAppear text is '%@' and thingy is '%@'", self.label.text, self.label.thingy);
}
Run it, and the NSLog output indicates that during the awake from nib, the native property is set by the time the category property setter is called...
...[794:41622] setting thingy to 'thingy value', my text is currently 'text value'
...[794:41622] didload text is 'text value' and thingy is 'thingy value'
...[794:41622] willappear text is 'text value' and thingy is 'thingy value'
Setting the label's text property in the category property setter will (and does, I tested it) result in the text property being overwritten to the thingy property, since the text property is initialized first.
Further evidence can be seen in the XIB file when rendered as XML...
<label opaque="NO" (... all the native properties) text="text value" (...) id="XAM-6h-4fn">
<rect key="frame" x="274" y="147" width="278" height="34"/>
(... and so on)
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="thingy" value="thingy value"/>
</userDefinedRuntimeAttributes>
</label>
... which is consistent with the view being instantiated and initialized via a pre-order traversal, thereby setting the (parent tag) label properties before the (child tag) userDefinedRuntimeAttributes.
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