Можно использовать раскадровку для создания экземпляров различных подклассов настраиваемого контроллера представления, хотя для этого используется несколько неортодоксальный прием: переопределение alloc
метода для контроллера представления. Когда создается настраиваемый контроллер представления, переопределенный метод выделения фактически возвращает результат работы alloc
в подклассе.
Я должен предварять ответ с оговоркой, что, хотя я тестировал его в различных сценариях и не получил ошибок, я не могу гарантировать, что он справится с более сложными настройками (но я не вижу причин, по которым это не должно работать) , Кроме того, я не отправлял никаких приложений, использующих этот метод, поэтому существует вероятность того, что он может быть отклонен процессом проверки Apple (хотя опять же, я не вижу причин, по которым это должно быть).
В демонстрационных целях у меня есть подкласс UIViewController
called TestViewController
, в котором есть UILabel IBOutlet и IBAction. В моей раскадровке я добавил контроллер представления, изменил его класс TestViewController
и подключил IBOutlet к UILabel, а IBAction - к UIButton. Я представляю TestViewController в виде модального перехода, запускаемого UIButton на предыдущем viewController.
Чтобы контролировать, какой класс создается, я добавил статическую переменную и связанные методы класса, чтобы получить / установить подкласс, который будет использоваться (я думаю, можно было бы использовать другие способы определения, какой подкласс должен быть создан):
TestViewController.m:
#import "TestViewController.h"
@interface TestViewController ()
@end
@implementation TestViewController
static NSString *_classForStoryboard;
+(NSString *)classForStoryboard {
return [_classForStoryboard copy];
}
+(void)setClassForStoryBoard:(NSString *)classString {
if ([NSClassFromString(classString) isSubclassOfClass:[self class]]) {
_classForStoryboard = [classString copy];
} else {
NSLog(@"Warning: %@ is not a subclass of %@, reverting to base class", classString, NSStringFromClass([self class]));
_classForStoryboard = nil;
}
}
+(instancetype)alloc {
if (_classForStoryboard == nil) {
return [super alloc];
} else {
if (NSClassFromString(_classForStoryboard) != [self class]) {
TestViewController *subclassedVC = [NSClassFromString(_classForStoryboard) alloc];
return subclassedVC;
} else {
return [super alloc];
}
}
}
Для моего теста у меня есть два подкласса TestViewController
: RedTestViewController
и GreenTestViewController
. Каждый подкласс имеет дополнительные свойства и каждое переопределение viewDidLoad
для изменения цвета фона представления и обновления текста UILabel IBOutlet:
RedTestViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor redColor];
self.testLabel.text = @"Set by RedTestVC";
}
GreenTestViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor greenColor];
self.testLabel.text = @"Set by GreenTestVC";
}
В некоторых случаях я мог бы захотеть создать TestViewController
сам, в других случаях RedTestViewController
или GreenTestViewController
. В предыдущем контроллере представления я делаю это случайным образом следующим образом:
NSInteger vcIndex = arc4random_uniform(4);
if (vcIndex == 0) {
NSLog(@"Chose TestVC");
[TestViewController setClassForStoryBoard:@"TestViewController"];
} else if (vcIndex == 1) {
NSLog(@"Chose RedVC");
[TestViewController setClassForStoryBoard:@"RedTestViewController"];
} else if (vcIndex == 2) {
NSLog(@"Chose BlueVC");
[TestViewController setClassForStoryBoard:@"BlueTestViewController"];
} else {
NSLog(@"Chose GreenVC");
[TestViewController setClassForStoryBoard:@"GreenTestViewController"];
}
Обратите внимание, что setClassForStoryBoard
метод проверяет, действительно ли запрошенное имя класса является подклассом TestViewController, чтобы избежать путаницы. Ссылка выше BlueTestViewController
предназначена для тестирования этой функциональности.