Подклассификация подкласса 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];
}