Es posible hacer que un guión gráfico cree una instancia de diferentes subclases de un controlador de vista personalizado, aunque implica una técnica ligeramente poco ortodoxa: anular el alloc
método para el controlador de vista. Cuando se crea el controlador de vista personalizado, el método alloc anulado de hecho devuelve el resultado de la ejecución alloc
en la subclase.
Debo comenzar la respuesta con la condición de que, aunque lo probé en varios escenarios y no recibí errores, no puedo asegurar que se adapte a configuraciones más complejas (pero no veo ninguna razón por la que no debería funcionar) . Además, no he enviado ninguna aplicación con este método, por lo que existe la posibilidad de que sea rechazada por el proceso de revisión de Apple (aunque, nuevamente, no veo ninguna razón por la que debería hacerlo).
Para fines de demostración, tengo una subclase de UIViewController
called TestViewController
, que tiene un UILabel IBOutlet y un IBAction. En mi guión gráfico, agregué un controlador de vista y modifiqué su clase TestViewController
y conecté IBOutlet a un UILabel y IBAction a un UIButton. Presento el TestViewController mediante un segue modal desencadenado por un UIButton en el viewController anterior.
Para controlar qué clase se instancia, he agregado una variable estática y métodos de clase asociados, así que obtenga / configure la subclase que se utilizará (supongo que se podrían adoptar otras formas de determinar qué subclase se instanciará):
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 mi prueba tengo dos subclases de TestViewController
: RedTestViewController
y GreenTestViewController
. Cada una de las subclases tiene propiedades adicionales y cada una se reemplaza viewDidLoad
para cambiar el color de fondo de la vista y actualizar el texto de 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";
}
En algunas ocasiones podría querer crear TestViewController
una instancia de sí mismo, en otras ocasiones RedTestViewController
o GreenTestViewController
. En el controlador de vista anterior, hago esto al azar de la siguiente manera:
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"];
}
Tenga en cuenta que el setClassForStoryBoard
método verifica para asegurarse de que el nombre de clase solicitado sea de hecho una subclase de TestViewController, para evitar confusiones. La referencia anterior a BlueTestViewController
está ahí para probar esta funcionalidad.