拦截/编程设置IBOutlet属性

时间:2014-05-28 20:19:13

标签: objective-c constraints nib objective-c-runtime

问题:

有没有办法以编程方式和自动方式设置IBOutlet属性(即没有对要设置的属性进行硬编码)?也许有一些" IBOutlet设置"我可以用我自己的专门代码拦截的例程吗?

背景

导致上述问题的问题源于以下事实:" IBOutleted"运行以下方法时,未设置大小约束(宽度和高度)(它是用于替换"占位符"使用实际视图从IB查看的方法):

+ (UIView*) replaceWithNibViewIfPlaceholder:(UIView*)view {

    BOOL isPlaceholder = ([[view subviews] count] == 0);

    // Special treatment for buttons (which contain their title label, and thus
    // always have one subview):
    if ([view isKindOfClass:[UIButton class]] && [[view subviews] count] == 1) {

        isPlaceholder = [[[view subviews] firstObject] isKindOfClass:[UILabel class]];
    }

    if (isPlaceholder) {

        // We're assuming that there only is one root view, and that it is of the correct type:
        UIView* replacer = [[view class] loadFromNib];

        // We don't need to set the frame nor autoresizingMask as we're utilizing auto layout.

        replacer.tag = view.tag;
        replacer.alpha = view.alpha;
        replacer.hidden = view.hidden;

        // Copy intrinsic constraints (i.e. size constraints, which are only associated with the view itself):
        [[view constraints] enumerateObjectsUsingBlock:^(NSLayoutConstraint* constraint, NSUInteger idx, BOOL *stop) {

            // If the constraint is not a size constraint, continue loop:
            if ((constraint.firstAttribute != NSLayoutAttributeWidth &&
                 constraint.firstAttribute != NSLayoutAttributeHeight &&
                 constraint.firstAttribute != NSLayoutAttributeNotAnAttribute) ||
                (constraint.secondAttribute != NSLayoutAttributeWidth &&
                 constraint.secondAttribute != NSLayoutAttributeHeight &&
                 constraint.secondAttribute != NSLayoutAttributeNotAnAttribute))
                return;

            NSLayoutConstraint* constraintClone = [NSLayoutConstraint constraintWithItem:replacer attribute:constraint.firstAttribute relatedBy:constraint.relation toItem:nil attribute:constraint.secondAttribute multiplier:constraint.multiplier constant:constraint.constant];

            // Now add the width or height constraint:
            [replacer addConstraint:constraintClone];
        }];

        return replacer;
    }

    return view;
}

从UIView :: awakeAfterUsingCoder:(NSCoder *)编码器调用此方法。它已经过很多不同的笔尖测试,到目前为止工作得很好。然而,现在的问题是我必须重新创建与被替换的视图严格相关的那些约束,即宽度和高度(与superview相关的约束被无缝转移)。我有一个这样的约束的IBOutlet,并且在通过这个方法时它仍然是零。

澄清一下,代码:

[replacer addConstraint:constraintClone];

工作正常,添加并应用约束。但是,相应的IBOutlet未设置(保持为零)。

更新

Sashas的回答是正确的,但拦截IBOutlet分配的方法并没有为我解决问题。

正如Sasha指出的那样,我的背景部分还不清楚。因此,我将以不同的方式快速尝试解释它。

我用来在Nib文件中存储更多或更少的复杂视图。为了在storyboard或其他nib文件中无缝插入它们,我实现了一个" NibLoadedView" class,它基本上替换了复杂视图中来自initWithCoder的任何实例。换句话说,我可以在storyboard / IB中设置简单占位符UIView的自定义类型,并在应用程序运行时将真实/复杂视图加载到其位置。应用于该占位符视图的所有约束都应移至实际视图。他们做了,至少是表达占位符与其周围环境之间关系的所有约束(其他观点)。另一方面,大小约束存储在占位符视图中,如果未转移到实际视图,则会丢失。而且这个转移是我遇到的问题,因为一旦我复制了约束,它们就按预期应用了,但如果我将其中一个作为IBOutlet引用,则IBOutlet将变为nil(它指向与之相关的约束)占位符视图,一旦该视图及其所有约束被删除,弱IBOutlet变为零;强大的IBOutlet也不会改变任何东西,它只会保持错误的约束而不是nil。)

解决方案是取代:

[replacer addConstraint:constraintClone];

使用:

memcpy((__bridge void*)constraint, (__bridge const void*)constraintClone, malloc_size((__bridge const void *)constraint));

[replacer addConstraint:constraint];

这会使用constraintClone覆盖内存中的约束,这样就可以隐式更新IBOutlet,无论它在何处设置。

2 个答案:

答案 0 :(得分:3)

IBOutlet通过KVC设置:当故事板或笔尖被解码时,它会为所有出口(动作,出口集合)调用setValue:forKey:。如果出于某种原因,您希望干扰此过程,请覆盖它并在key正确时使用自定义逻辑。

也许您想查看awakeFromNib - 因为这是nib完全解码并且所有插座都已设置的第一种方法。说实话,我并不真正理解目标,也许你可以解释一下。

答案 1 :(得分:1)

你想要太聪明了。用memcpy覆盖对象的内存充其量是有风险的,覆盖不透明对象更糟糕。你这样做的方式可能会导致泄密,弱引用出错等等,而这只是在美好的一天。

说真的,不要这样做。

如果我正确理解您的问题,您可以(a)占位符视图和(b)IBOutlet引用该占位符视图的约束。您希望(1)替换占位符视图和(2)更新IBOutlet以引用替换视图上的约束。并且您正在放置进一步的限制,您不希望知道哪个IBOutlet引用您的约束。

在一般意义上思考间接。

您可以按以下方式构建模型:

  • 创建一个控制器对象,用于管理对约束的引用。
  • 在占位符视图中添加一个属性,该视图引用其控制器对象。
  • 将控制器管理的初始约束设置为占位符
  • 将所有IBOutlet指向约束到控制器而不是对象。
  • 使用其指向控制器对象的链接将占位符替换为另一个视图时,请更新引用的约束。

一切正常,没什么可疑的。

这是NSObjectController支持的模型。

请勿阅读

如果你真的想拍自己并且脚下的用户不认为会覆盖,想想交流,当然还是很危险。够了。