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




swift segue (9)

essayez ceci, après instantiateViewControllerWithIdentifier.

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

comme :

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

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.


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é.


Comme la réponse acceptée l'indique, il ne semble pas possible de faire avec des storyboards.

Ma solution est d'utiliser Nib's - tout comme les développeurs les utilisaient avant les storyboards. Si vous voulez avoir un contrôleur de vue réutilisable, sous-classifiable (ou même une vue), ma recommandation est d'utiliser des Nibs.

SubclassMyViewController *myViewController = [[SubclassMyViewController alloc] initWithNibName:@"MyViewController" bundle:nil]; 

Lorsque vous connectez toutes vos prises au "propriétaire du fichier" dans MyViewController.xib vous ne spécifiez PAS la classe dans laquelle la Nib doit être chargée, vous spécifiez simplement des paires clé-valeur: " cette vue doit être connectée à ce nom de variable d'instance " Lors de l'appel de [SubclassMyViewController alloc] initWithNibName: le processus d'initialisation spécifie quel contrôleur de vue sera utilisé pour " contrôler " la vue que vous avez créée dans la plume.


La méthode Objc_setclass ne crée pas d'instance de childvc. Mais tout en sortant de childvc, deinit de childvc est appel. Comme il n'y a pas de mémoire allouée séparément pour childvc, l'application se bloque. Basecontroller a une instance, alors que child vc n'en a pas.


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)


bonne question - mais malheureusement seulement une réponse boiteuse. Je ne crois pas qu'il soit actuellement possible de faire ce que vous proposez car il n'y a pas d'initialiseurs dans UIStoryboard qui permettent de surcharger le contrôleur de vue associé au storyboard tel que défini dans les détails de l'objet dans le storyboard à l'initialisation. C'est à l'initialisation que tous les éléments de l'interface utilisateur du stoaryboard sont liés à leurs propriétés dans le contrôleur de vue.

Il s'initialisera par défaut avec le contrôleur de vue spécifié dans la définition du storyboard.

Si vous essayez de réutiliser les éléments d'interface utilisateur que vous avez créés dans le storyboard, ils doivent toujours être liés ou associés à des propriétés dans lesquelles le contrôleur de vue les utilise pour pouvoir "informer" le contrôleur de vue des événements.

Ce n'est pas vraiment un gros problème de copier une mise en page de storyboard, surtout si vous avez seulement besoin d'un design similaire pour 3 vues, mais si vous le faites, vous devez vous assurer que toutes les associations précédentes sont effacées. pour communiquer avec le contrôleur de vue précédent. Vous serez en mesure de les reconnaître comme messages d'erreur KVO dans la sortie du journal.

Quelques approches que vous pourriez prendre:

  • stockez les éléments de l'interface utilisateur dans un UIView - dans un fichier xib et instanciez-le depuis votre classe de base et ajoutez-le en tant que sous-vue dans la vue principale, généralement self.view. Ensuite, vous utiliserez simplement la mise en page du storyboard avec des contrôleurs de vue essentiellement vierges qui conservent leur place dans le storyboard mais avec la sous-classe de contrôleur de vue correcte qui leur est assignée. Puisqu'ils hériteraient de la base, ils obtiendraient cette vue.

  • créez la disposition dans le code et installez-la à partir de votre contrôleur de base. Évidemment, cette approche va à l'encontre du but de l'utilisation du storyboard, mais peut être le moyen d'aller dans votre cas. Si vous avez d'autres parties de l'application qui bénéficieraient de l'approche du storyboard, vous pouvez dévier ici et là, le cas échéant. Dans ce cas, comme ci-dessus, vous utiliseriez simplement les contrôleurs de vue de banque avec votre sous-classe affectée et laissez le contrôleur de vue de base installer l'interface utilisateur.

Ce serait bien si Apple avait trouvé un moyen de faire ce que vous proposez, mais la question d'avoir les éléments graphiques pré-liés avec la sous-classe du contrôleur serait toujours un problème.

Excellente nouvelle année!! être bien


Bien que ce ne soit pas strictement une sous-classe, vous pouvez:

  1. option -drag le contrôleur de vue de classe de base dans le plan du document pour faire une copie
  2. Déplacez la nouvelle copie du contrôleur de vue vers un emplacement distinct sur le storyboard
  3. Modifier la classe pour le contrôleur de vue de sous-classe dans l'inspecteur d'identité

Voici un exemple d'un tutoriel de Bloc j'ai écrit, sous- ViewController avec WhiskeyViewController :

Cela vous permet de créer des sous-classes de sous-classes de contrôleur de vue dans le storyboard. Vous pouvez ensuite utiliser instantiateViewControllerWithIdentifier: pour créer des sous-classes spécifiques.

Cette approche est un peu inflexible: les modifications ultérieures dans le storyboard au contrôleur de classe de base ne se propagent pas à la sous-classe. Si vous avez beaucoup de sous-classes, vous pouvez être mieux avec l'une des autres solutions, mais cela va faire une pincée.


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

Lorsque votre ViewController principal est de type UITableViewController , il possède une propriété clearsSelectionOnViewWillAppear , qui est clearsSelectionOnViewWillAppear par défaut sur YES , ce qui clearsSelectionOnViewWillAppear automatiquement la sélection.

Cette propriété n'est pas disponible pour un UITableView , je suppose que c'est parce qu'il n'a pas non ViewWillAppear méthode ViewWillAppear .

Un UIViewController n'a pas besoin de cette propriété car il n'a pas de UITableView origine.

conclusion: vous devrez l'implémenter vous-même lorsque vous n'utilisez pas un UITableViewController .





objective-c cocoa-touch ios5 uiviewcontroller storyboard