Możliwe jest, aby scenorys tworzył wystąpienie różnych podklas niestandardowego kontrolera widoku, chociaż wymaga to nieco niekonwencjonalnej techniki: przesłaniania alloc
metody kontrolera widoku. Po utworzeniu kontrolera widoku niestandardowego, zastąpiona metoda alokacji w rzeczywistości zwraca wynik działania alloc
w podklasie.
Odpowiedź powinienem poprzedzić z zastrzeżeniem, że chociaż testowałem go w różnych scenariuszach i nie otrzymałem żadnych błędów, nie mogę zapewnić, że poradzi sobie z bardziej złożonymi konfiguracjami (ale nie widzę powodu, dla którego nie miałoby działać) . Ponadto nie przesłałem żadnych aplikacji przy użyciu tej metody, więc istnieje zewnętrzna szansa, że może ona zostać odrzucona w procesie recenzji Apple (chociaż znowu nie widzę powodu, dla którego miałoby to robić).
Dla celów demonstracyjnych mam podklasę UIViewController
nazwaną TestViewController
, która ma UILabel IBOutlet i IBAction. W moim storyboardzie dodałem kontroler widoku i zmieniłem jego klasę na TestViewController
oraz podłączyłem IBOutlet do UILabel, a IBAction do UIButton. Przedstawiam TestViewController za pomocą modalnego segue wyzwalanego przez UIButton na poprzednim viewController.
Aby kontrolować, która klasa jest tworzona, dodałem zmienną statyczną i powiązane metody klas, więc pobierz / ustaw podklasę, która ma być używana (myślę, że można przyjąć inne sposoby określania, która podklasa ma zostać utworzona):
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];
}
}
}
Do mojego testu mam dwie podklasy TestViewController
: RedTestViewController
i GreenTestViewController
. Każda z podklas ma dodatkowe właściwości i każda nadpisanie, viewDidLoad
aby zmienić kolor tła widoku i zaktualizować tekst 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";
}
Czasami mogę chcieć utworzyć instancję TestViewController
, w innych przypadkach RedTestViewController
lub GreenTestViewController
. W powyższym kontrolerze widoku robię to losowo w następujący sposób:
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"];
}
Zwróć uwagę, że setClassForStoryBoard
metoda sprawdza, czy żądana nazwa klasy jest rzeczywiście podklasą TestViewController, aby uniknąć wszelkich pomyłek. Powyższe odniesienie BlueTestViewController
służy do testowania tej funkcji.