在预定义属性之后设置用户定义的运行时属性(IBInspectable)?

11
我想通过分类方法向UILabel添加一个新的IBInspectable属性(计算属性)。理想情况下,我希望在设置标签文本后(通过setValue:forKey)设置此属性,因为这个IBInspectable属性可能会导致UILabel的文本被更新,我们不希望UILabel的文本之后再被替换。查看文档发现,在Interface Builder中配置的属性的预定义属性是否总是在用户定义属性之前设置,在nib/storyboard加载期间没有提到。
使用IBInspectable或用户定义的运行时属性在Interface Builder中添加到对象的自定义属性是否保证在标准预定义对象属性/属性之后设置?

你能否更清楚地表明你是在编程方式下设置标签的文本(例如在viewDidLoad中),还是指在IB中设置? - Zayne ZH
抱歉,这两个都是通过IB设置的。 - bencallis
2个回答

2
以下实验结论表明,本地文本属性在分类属性之前设置,因此该值可以安全地被分类设置器覆盖。
一个标签类别:
//  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

在IB中,设置可检查类别属性和文本属性...

enter image description here

包含视图控制器中有少量的仪表设置:
- (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);
}

运行它,NSLog输出表明,在从nib唤醒期间,本地属性在类别属性设置程序被调用之前已经被设置...
引用:
...[794:41622]将thingy设置为'thingy值',我的文本当前为'text value' ...[794:41622] didload文本为'text value'和thingy为'thingy值' ...[794:41622] willappear文本为'text value'和thingy为'thingy值'
在类别属性设置器中设置标签的文本属性会导致(我测试过了)文本属性被覆盖为thingy属性,因为文本属性首先被初始化。
在XML格式呈现的XIB文件中可以看到进一步的证据...
<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>

这句话的意思是:通过“先序遍历”来实例化和初始化视图,以此在设置(父标签)标签属性之前就设置(子标签) userDefinedRuntimeAttributes。

谢谢您的回复。在测试不同版本的iOS时,我注意到了相同的行为。然而,我希望它有文档记录,这样我就可以完全放心地在生产代码中使用它。 - bencallis
似乎在文档中预期出现的位置没有明确涵盖此内容。但是,演示的确定性行为比文档更好,对吧?只要使用100%的公共API,该行为就可以被视为SDK的任何部分一样永久地应用于您的应用程序。 - danh
我想一个担忧是在未来的iOS版本中,顺序会改变,而没有任何明显的API更改。 - bencallis
我认为我会选择使用objc_setAssociatedObject这条路。 - bencallis
你可以自行决定,但该应用程序是静态编译的。它所附带的框架可能会发生变化,但除非开发人员使用更新的内容重新构建,否则应用程序不会发生变化。 - danh

0
如果您使用IBInspectable计算机变量创建扩展,当在Interface Builder中设置了自定义字段和标签文本字段的值时,将覆盖标签文本字段中的值,即使在Interface Builder中无法预览该方式。
extension UILabel {

    @IBInspectable var value: String? {
        get {
            return text
        }

        set {
            text = newValue
            setNeedsDisplay()
        }
    }

}

Interface builder enter image description here


感谢您的回答。我已经实现了一个类似的扩展,并在不同版本的iOS上进行了测试,发现了相同的行为。然而,我希望它有文档记录,这样我就可以完全放心地在生产代码中使用它。 - bencallis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接