Подклассификация подкласса UIView, загруженного из наконечника
У меня есть класс 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
в каждом подклассе?
Ответы
Ответ 1
Swizzling не является (когда-либо) ответом.
Проблема в вашем NIB; он архивируется с объектом [0], являющимся экземпляром FooView
, а не FooSubclassView
. Если вы хотите загрузить тот же NIB с другим подклассом вида в качестве объекта [0], вам нужно перенести экземпляр из архива NIB.
Вероятно, самое простое, что нужно сделать, поскольку ваш класс уже загружает NIB, делает экземпляр FooView
или FooSubclassView
владельца файла.
У этого вопроса есть достойное объяснение шаблона File Owner. Обратите внимание, что вы уже почти там, где ваш класс - это то, что загружает XIB/NIB в любом случае.
И вот официальные документы в File Owner.
Ответ 2
Я не уверен, что вы на лучшее решение, но я думаю, что это то, что вы ищете.
+ (instancetype)viewFromNib
{
NSString *className = NSStringFromClass([self class]);
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:className owner:self options:nil];
return [xib objectAtIndex:0];
}
До тех пор, пока вы можете быть уверены, что NIB имеет то же имя, что и класс.
Поняв, что я принял одно из требований, я говорю, что мне нужно будет согласиться с @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];
}