Initialisation personnalisée pour UIViewController dans Swift avec configuration de l'interface dans le storyboard

J'ai un problème pour l'écriture d'init personnalisée pour la sous-class de UIViewController, fondamentalement je veux passer la dépendance à travers la méthode init pour viewController plutôt que de définir la propriété directement comme viewControllerB.property = value

J'ai donc fait un init personnalisé pour mon viewController et j'appelle super-désigné init

 init(meme: Meme?) { self.meme = meme super.init(nibName: nil, bundle: nil) } 

L'interface du controller de vue réside dans le storyboard, j'ai également fait l'interface pour que la class personnalisée soit mon controller de vue. Et Swift nécessite d'appeler cette méthode init même si vous ne faites rien dans cette méthode. Sinon, le compilateur va se plaindre …

 required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } 

Le problème est quand j'essaie d'appeler mon init personnalisé avec MyViewController(meme: meme) il ne possède pas les propriétés d'init dans mon viewController du tout …

J'essayais de déboguer, j'ai trouvé dans mon viewController, init(coder aDecoder: NSCoder) est appelé en premier, puis mon init personnalisé est appelé plus tard. Cependant, ces deux methods init renvoient différentes adresses d' self memory.

Je soupçonne quelque chose de mal avec l'init pour mon viewController, et il returnnera toujours le self avec l' init?(coder aDecoder: NSCoder) , qui, n'a pas d'implémentation.

Est-ce que quelqu'un sait comment faire un init personnalisé pour votre viewController correctement? Note: l'interface de mon viewController est configurée dans le storyboard

voici mon code ViewController:

 class MemeDetailVC : UIViewController { var meme : Meme! @IBOutlet weak var editedImage: UIImageView! // TODO: incorrect init init(meme: Meme?) { self.meme = meme super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override func viewDidLoad() { /// setup nav title title = "Detail Meme" super.viewDidLoad() } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) editedImage = UIImageView(image: meme.editedImage) } } 

    Comme cela a été spécifié dans l'une des réponses ci-dessus, vous ne pouvez pas utiliser ni la méthode d'initialisation personnalisée, ni le storyboard. Mais vous pouvez toujours utiliser une méthode statique pour instancier ViewController à partir d'un storyboard et y effectuer des configurations supplémentaires. Il ressemblera à ceci:

     class MemeDetailVC : UIViewController { var meme : Meme! static func makeMemeDetailVC(meme: Meme) -> MemeDetailVC { let newViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("IdentifierOfYouViewController") as! MemeDetailVC newViewController.meme = meme return newViewController } } 

    N'oubliez pas de spécifier IdentifierOfYouViewController comme identificateur de controller de vue dans votre storyboard. N'utilisez pas mème var dans votre viewDidLoad car il n'est toujours pas initialisé à ce moment là. Vous devrez peut-être également changer le nom du storyboard dans le code ci-dessus.

    Vous ne pouvez pas utiliser un initialiseur personnalisé lorsque vous initialisez à partir d'un Storyboard, en utilisant init?(coder aDecoder: NSCoder) est la façon dont Apple a conçu le storyboard pour initialiser un controller. Cependant, il existe des moyens d'envoyer des données à un UIViewController .

    Le nom de votre controller de vue contient des detail , donc je suppose que vous y arrivez depuis un autre controller. Dans ce cas, vous pouvez utiliser la méthode prepareForSegue pour envoyer des données au détail (Ceci est Swift 3):

     override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) { if segue.identifier == "identifier" { if let controller = segue.destinationViewController as? MemeDetailVC { controller.meme = "Meme" } } } 

    Je viens d'utiliser une propriété de type Ssortingng au lieu de Meme à des fins de test. En outre, assurez-vous que vous passez dans l'identificateur de segue correct ( "identifier" était juste un espace réservé).

    Une façon que j'ai fait est avec un initialiseur de commodité.

     class MemeDetailVC : UIViewController { convenience init(meme: Meme) { self.init() self.meme = meme } } 

    Ensuite, vous initialisez votre MemeDetailVC avec let memeDetailVC = MemeDetailVC(theMeme)

    La documentation d'Apple sur les initialiseurs est plutôt bonne, mais mon préféré est la série de didacticiels Ray Wenderlich: Initialization in Depth qui devrait vous donner beaucoup d'explications / exemples sur vos différentes options d'initialisation et la façon «correcte» de faire les choses.


    EDIT : Bien que vous puissiez utiliser un initialiseur de commodité sur les controllers de vue personnalisés, tout le monde a raison de dire que vous ne pouvez pas utiliser d'initialiseurs personnalisés lors de l'initialisation à partir du storyboard ou d'une séquence de storyboard.

    Si votre interface est configurée dans le storyboard et que vous créez le controller complètement par programme, un initialiseur de commodité est probablement le moyen le plus facile de faire ce que vous essayez de faire, car vous n'avez pas besoin de gérer l'init nécessaire avec le NSCoder (que je ne comprends toujours pas).

    Si vous obtenez votre controller de vue via le storyboard, vous devrez suivre la réponse de @Caleb Kleveter et placer le controller de vue dans la sous-class désirée, puis définir manuellement la propriété.

    Il y avait à l'origine quelques réponses, qui ont été votées et supprimées, même si elles étaient essentiellement correctes. La réponse est, vous ne pouvez pas.

    Lorsque vous travaillez à partir d'une définition de storyboard, vos instances de controller de vue sont toutes archivées. Donc, pour les initialiser, il est nécessaire que init?(coder... soit utilisé.) Le coder est d'où proviennent tous les parameters / informations de vue.

    Donc, dans ce cas, il n'est pas possible d'appeler une autre fonction init avec un paramètre personnalisé. Il doit soit être défini comme une propriété lors de la préparation de la section, soit vous pouvez trancher des segues et charger les instances directement depuis le storyboard et les configurer (essentiellement un model d'usine utilisant un storyboard).

    Dans tous les cas, vous utilisez la fonction init requirejse par le SDK et transmettez ensuite des parameters supplémentaires.

    UIViewController class UIViewController conforme au protocole NSCoding qui est défini comme suit:

     public protocol NSCoding { public func encode(with aCoder: NSCoder) public init?(coder aDecoder: NSCoder) // NS_DESIGNATED_INITIALIZER } 

    UIViewController possède donc deux initialiseurs UIViewController désignés init?(coder aDecoder: NSCoder) et init(nibName nibNameOrNil: Ssortingng?, bundle nibBundleOrNil: Bundle?) .

    Storyborad appelle init?(coder aDecoder: NSCoder) directement à init UIViewController et UIView , Il n'y a pas de place pour que vous passiez des parameters.

    Une solution de contournement fastidieuse consiste à utiliser un cache temporaire:

     class TempCache{ static let sharedInstance = TempCache() var meme: Meme? } TempCache.sharedInstance.meme = meme // call this before init your ViewController required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder); self.meme = TempCache.sharedInstance.meme }