[objective-c] Comment utiliser uiviewcontroller de storyboard unique pour plusieurs sous-classes



Answers

Le code de ligne que nous recherchons est:

object_setClass(AnyObject!, AnyClass!)

Dans Storyboard -> ajouter UIViewController lui donner un nom de classe ParentVC.

class ParentVC: UIViewController {

    var type: Int?

    override func awakeFromNib() {

        if type = 0 {

            object_setClass(self, ChildVC1.self)
        }
        if type = 1 {

            object_setClass(self, ChildVC2.self)
        }  
    }

    override func viewDidLoad() {   }
}

class ChildVC1: ParentVC {

    override func viewDidLoad() {
        super.viewDidLoad()

        println(type)
        // Console prints out 0
    }
}

class ChildVC2: ParentVC {

    override func viewDidLoad() {
        super.viewDidLoad()

        println(type)
        // Console prints out 1
    }
}
Question

Disons que j'ai un storyboard qui contient UINavigationController tant que contrôleur de vue initial. Son contrôleur de vue racine est une sous-classe de UITableViewController , qui est BasicViewController . Il a IBAction qui est connecté au bouton de navigation droit de la barre de navigation

De là, je voudrais utiliser le storyboard comme modèle pour d'autres vues sans avoir à créer des storyboards supplémentaires. Disons que ces vues auront exactement la même interface mais avec le contrôleur de vue racine de la classe SpecificViewController1 et SpecificViewController2 qui sont des sous-classes de BasicViewController .
Ces 2 contrôleurs de vue auraient la même fonctionnalité et l'interface, sauf pour la méthode IBAction .
Ce serait comme suit:

@interface BasicViewController : UITableViewController

@interface SpecificViewController1 : BasicViewController

@interface SpecificViewController2 : BasicViewController

Puis-je faire quelque chose comme ça?
Puis-je simplement instancier le storyboard de BasicViewController mais avoir un contrôleur de vue racine pour sous- BasicViewController SpecificViewController1 et SpecificViewController2 ?

Merci.




essayez ceci, après instantiateViewControllerWithIdentifier.

- (void)setClass:(Class)c {
    object_setClass(self, c);
}

comme :

SubViewController *vc = [sb instantiateViewControllerWithIdentifier:@"MainViewController"];
[vc setClass:[SubViewController class]];



Il est possible d'avoir un storyboard instancié différentes sous-classes d'un contrôleur de vue personnalisé, bien qu'il implique une technique légèrement peu orthodoxe: substituer la méthode alloc pour le contrôleur de vue. Lorsque le contrôleur de vue personnalisé est créé, la méthode d'allocation substituée renvoie en fait le résultat de l'exécution de alloc sur la sous-classe.

Je devrais préface la réponse à condition que, bien que je l'ai testé dans divers scénarios et n'ai reçu aucune erreur, je ne peux pas m'assurer qu'il fera face à des configurations plus complexes (mais je ne vois pas pourquoi cela ne devrait pas fonctionner) . De plus, je n'ai soumis aucune application utilisant cette méthode, donc il y a des chances que cela puisse être rejeté par le processus de révision d'Apple (bien que je ne vois pas pourquoi cela devrait être le cas).

À des fins de démonstration, j'ai une sous-classe de UIViewController appelée TestViewController , qui a un IBOutlet UILabel, et un IBAction. Dans mon storyboard, j'ai ajouté un contrôleur de vue et modifié sa classe à TestViewController , et connecté l'IBOutlet à un UILabel et l'IBAction à un UIButton. Je présente le TestViewController au moyen d'une séquence modale déclenchée par un UIButton sur le viewController précédent.

Pour contrôler quelle classe est instanciée, j'ai ajouté une variable statique et des méthodes de classe associées afin d'obtenir / définir la sous-classe à utiliser (je suppose que l'on pourrait adopter d'autres façons de déterminer quelle sous-classe doit être instanciée):

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];
        }
    }
}

Pour mon test, j'ai deux sous-classes de TestViewController : RedTestViewController et GreenTestViewController . Les sous-classes ont chacune des propriétés supplémentaires et remplacent chacune viewDidLoad pour changer la couleur d'arrière-plan de la vue et mettre à jour le texte de l'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";
}

À certaines occasions je pourrais vouloir instancier TestViewController lui-même, dans d'autres occasions RedTestViewController ou GreenTestViewController . Dans le contrôleur de vue précédent, je le fais au hasard comme suit:

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"];
}

Notez que la méthode setClassForStoryBoard vérifie que le nom de classe demandé est bel et bien une sous-classe de TestViewController, afin d'éviter toute confusion. La référence ci-dessus à BlueTestViewController est là pour tester cette fonctionnalité.




Le moyen le plus flexible est probablement d'utiliser des vues réutilisables.

(Créez une vue dans un fichier XIB ou un Container view distinct et ajoutez-la à chaque scène de contrôleur de vue de sous-classe dans le storyboard)




Related