12

我有一个类 ,FooView它是 的子类UIView,它的视图是从 nib 加载的,例如:

+ (instancetype)viewFromNib
{
    NSArray *xib = [[NSBundle mainBundle] loadNibNamed:@"FooView" owner:self options:nil];
    return [xib objectAtIndex:0];
}

笔尖本身FooView在 Identity Inspector 中将其自定义类设置为。

这被实例化为:

FooView *view = [FooView viewFromNib];

这表现如你所料。但是,当 FooView 本身被子类化为 FooSubclassView,并实例化为:

FooSubclassView *view = [FooSubclassView viewFromNib];

view仍然是类型FooView,不是FooSubclassView

Swizzling 类object_setClass并不能解决底层对象是 的实例这一事实,FooView因此在子类实例上调用的方法将是超类 ( FooView) 的方法,而不是FooSubclassView.

我该如何纠正这个问题,以便子类具有正确的类型,而不必为每个子类创建一个新的 nib,或者不必viewFromNib在每个子类中重新实现?

4

2 回答 2

8

Swizzling is not (ever) the answer.

The problem is in your NIB; it is archived with object[0] being an instance of FooView, not FooSubclassView. If you want to load the same NIB with a different view subclass as object[0], you need to move the instance out of the NIB's archive.

Probably the easiest thing to do, since your class is already loading the NIB, is make an instance of FooView or FooSubclassView the File's Owner.

This question has a decent explanation of the File's Owner pattern. Note that you are pretty much already there in that your class is what is loading the XIB/NIB anyway.

And here is the official docs on File's Owner.

于 2013-09-02T02:03:26.700 回答
1

I'm not sure you are onto the best solution, but I think this is what you are looking for.

+ (instancetype)viewFromNib
{
    NSString *className = NSStringFromClass([self class]);
    NSArray *xib = [[NSBundle mainBundle] loadNibNamed:className owner:self options:nil];
    return [xib objectAtIndex:0];
}

That is as long as you can be sure that the NIB has the same name as the class.


After realizing that I mistook one of the requirements, I say I'll have to agree with @bbum.

- (id)init
{
    // NOTE: If you don't know the size, you can work this out after you load the nib.
    self = [super initWithFrame:GCRectMake(0, 0, 320, 480)];
    if (self) {
        // Load the nib using the instance as the owner.
        [[NSBundle mainBundle] loadNibNamed:@"FooView" owner:self options:nil];
    }
    return self;
}

+ (instancetype)viewFromNib
{
    return [[self alloc] init];
}
于 2013-09-02T02:02:40.433 回答