Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User defined runtime attributes (IBInspectable) set after pre-defined attributes?

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?

like image 252
bencallis Avatar asked Sep 05 '16 09:09

bencallis


1 Answers

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....

enter image description here

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.

like image 143
danh Avatar answered Oct 06 '22 11:10

danh