I have a class, FooView
, that is a subclass of UIView
, and whose view is loaded from a nib, something like:
+ (instancetype)viewFromNib
{
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:@"FooView" owner:self options:nil];
return [xib objectAtIndex:0];
}
The nib itself has its Custom Class set to FooView
in the Identity Inspector.
This is instantiated as:
FooView *view = [FooView viewFromNib];
This behaves as you'd expect. However, when FooView is itself subclassed as FooSubclassView, and instantiated as:
FooSubclassView *view = [FooSubclassView viewFromNib];
view
is still of type FooView
, not FooSubclassView
.
Swizzling the class with object_setClass
doesn't fix the fact that the underlying object is an instance of FooView
, and thus methods called on the subclass instance will be those of the superclass (FooView
), not FooSubclassView
.
How can I correct this so that subclasses are of the correct type, without having to create a new nib for every subclass, or having to reimplement viewFromNib
in every subclass?
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.
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];
}
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