É possível fazer com que um storyboard instancie diferentes subclasses de um controlador de visualização customizado, embora envolva uma técnica um pouco heterodoxa: substituir o alloc
método para o controlador de visualização. Quando o controlador de visualização customizado é criado, o método de alocação sobrescrito retorna de fato o resultado da execução alloc
na subclasse.
Devo começar a resposta com a ressalva de que, embora eu tenha testado em vários cenários e não tenha recebido erros, não posso garantir que ele irá lidar com configurações mais complexas (mas não vejo razão para que não funcione) . Além disso, não enviei nenhum aplicativo usando esse método, portanto, há uma chance remota de que ele seja rejeitado pelo processo de revisão da Apple (embora, novamente, não vejo razão para isso).
Para fins de demonstração, tenho uma subclasse de UIViewController
chamado TestViewController
, que tem um UILabel IBOutlet e um IBAction. Em meu storyboard, adicionei um controlador de visualização e TestViewController
alterei sua classe para , e conectei o IBOutlet a um UILabel e o IBAction a um UIButton. Apresento o TestViewController por meio de uma segue modal acionada por um UIButton no viewController anterior.
Para controlar qual classe é instanciada, adicionei uma variável estática e métodos de classe associados para obter / definir a subclasse a ser usada (eu acho que pode-se adotar outras maneiras de determinar qual subclasse deve ser instanciada):
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];
}
}
}
Para meu teste, tenho duas subclasses de TestViewController
: RedTestViewController
e GreenTestViewController
. Cada uma das subclasses tem propriedades adicionais e cada substituição viewDidLoad
para alterar a cor de fundo da visualização e atualizar o texto do IBOutlet UILabel:
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";
}
Em algumas ocasiões, posso querer instanciar- TestViewController
se, em outras ocasiões RedTestViewController
ou GreenTestViewController
. No controlador de visualização anterior, faço isso aleatoriamente da seguinte maneira:
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"];
}
Observe que o setClassForStoryBoard
método verifica se o nome da classe solicitado é de fato uma subclasse de TestViewController, para evitar confusões. A referência acima BlueTestViewController
para testar esta funcionalidade.