È possibile fare in modo che uno storyboard crei un'istanza di sottoclassi diverse di un controller di visualizzazione personalizzato, sebbene implichi una tecnica leggermente non ortodossa: sovrascrivere il alloc
metodo per il controller di visualizzazione. Quando viene creato il controller di visualizzazione personalizzato, il metodo di allocazione sovrascritto restituisce infatti il risultato dell'esecuzione alloc
nella sottoclasse.
Dovrei anteporre la risposta con la condizione che, anche se l'ho testato in vari scenari e non ho ricevuto errori, non posso garantire che possa far fronte a configurazioni più complesse (ma non vedo motivo per cui non dovrebbe funzionare) . Inoltre, non ho inviato alcuna app utilizzando questo metodo, quindi c'è la possibilità che possa essere rifiutata dal processo di revisione di Apple (anche se ancora una volta non vedo motivo per cui dovrebbe).
A scopo dimostrativo, ho una sottoclasse di UIViewController
chiamata TestViewController
, che ha un UILabel IBOutlet, ed un IBAction. Nel mio storyboard, ho aggiunto un controller di visualizzazione e modificato la sua classe in TestViewController
e ho collegato l'IBOutlet a una UILabel e l'IBAction a un UIButton. Presento il TestViewController tramite un segue modale attivato da un UIButton sul viewController precedente.
Per controllare quale classe viene istanziata, ho aggiunto una variabile statica e metodi di classe associati in modo da ottenere / impostare la sottoclasse da utilizzare (immagino che si potrebbero adottare altri modi per determinare quale sottoclasse deve essere istanziata):
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];
}
}
}
Per il mio test ho due sottoclassi di TestViewController
: RedTestViewController
e GreenTestViewController
. Le sottoclassi hanno ciascuna proprietà aggiuntive e ciascuna sostituisce viewDidLoad
per modificare il colore di sfondo della vista e aggiornare il testo di 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";
}
In alcune occasioni potrei voler creare un'istanza TestViewController
, in altre occasioni RedTestViewController
o GreenTestViewController
. Nel controller di visualizzazione precedente, lo faccio a caso come segue:
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"];
}
Si noti che il setClassForStoryBoard
metodo verifica che il nome della classe richiesta sia effettivamente una sottoclasse di TestViewController, per evitare confusioni. Il riferimento sopra a BlueTestViewController
è lì per testare questa funzionalità.