2016-02-10 13 views
33

Sto avendo problema per la scrittura di init personalizzato per sottoclasse di UIViewController, fondamentalmente voglio passare la dipendenza attraverso il metodo init per viewController piuttosto che impostare immobile direttamente come viewControllerB.property = valueinit personalizzato per UIViewController a Swift con l'installazione interfaccia storyboard

Così ho fatto un'init personalizzato per il mio viewController e chiamare super-designato init

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

l'interfaccia controller della vista risiede nella storyboard, ho anche rendere l'interfaccia per la classe personalizzata per essere il mio controller della vista. E Swift richiede di chiamare questo metodo init anche se non stai facendo nulla all'interno di questo metodo. In caso contrario, il compilatore si lamenterà ...

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

Il problema è quando provo a chiamare mia abitudine init con MyViewController(meme: meme) non init immobili a mia viewController a tutti ...

stavo cercando di debug, ho trovato nel mio viewController, init(coder aDecoder: NSCoder) viene chiamato prima, poi il mio init personalizzato viene chiamato più tardi. Tuttavia questi due metodi init restituiscono diversi indirizzi di memoria self.

Sto sospettando qualcosa di sbagliato con init per my viewController, e restituirà sempre self con il init?(coder aDecoder: NSCoder), che non ha implementazione.

Qualcuno sa come eseguire correttamente init personalizzati per viewController? Nota: l'interfaccia di mio viewController è impostato in storyboard

qui è il mio codice 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) 
    } 

} 
+0

sei arrivato una soluzione per questo? – user481610

risposta

5

in origine vi erano un paio di risposte, che erano mucca votato e cancellato anche se erano fondamentalmente corretto. La risposta è, non puoi.

Quando si lavora da una definizione di storyboard, le istanze del controller di visualizzazione sono tutte archiviate. Quindi, per iniziarli è necessario utilizzare init?(coder.... Il coder è da dove provengono tutte le impostazioni/informazioni sulla vista.

Quindi, in questo caso, non è possibile chiamare anche altre funzioni di init con un parametro personalizzato. Dovrebbe essere impostato come proprietà durante la preparazione del seguito, oppure puoi abbandonare segues e caricare le istanze direttamente dallo storyboard e configurarle (fondamentalmente un pattern factory usando uno storyboard).

In tutti i casi si utilizza la funzione di inizializzazione richiesta SDK e successivamente si passano parametri aggiuntivi.

1

UIViewController classe conforme alle NSCoding protocollo che è definito come:

public protocol NSCoding { 

    public func encode(with aCoder: NSCoder) 

    public init?(coder aDecoder: NSCoder) // NS_DESIGNATED_INITIALIZER 
}  

Così UIViewController ha due designato inizializzatore init?(coder aDecoder: NSCoder) e init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?).

Storyborad chiama init?(coder aDecoder: NSCoder) direttamente a init UIViewController e UIView, Non c'è spazio per passare i parametri.

Una soluzione ingombrante è quello di utilizzare una cache temporanea:

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

Non è possibile utilizzare un inizializzatore personalizzato quando si inizializza da uno storyboard, utilizzando init?(coder aDecoder: NSCoder) è come Apple ha progettato lo storyboard per inizializzare un controller. Tuttavia, ci sono modi per inviare dati a un UIViewController.

Il nome del controller di visualizzazione ha detail al suo interno, quindi suppongo che ci arrivi da un altro controller. In questo caso è possibile utilizzare il metodo prepareForSegue per inviare i dati al dettaglio (Questo è Swift 3):

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

ho appena usato una proprietà di tipo String invece di Meme a scopo di test. Inoltre, assicurati di inserire l'identificatore dei passaggi corretto ("identifier" era solo un segnaposto).

8

Un modo per farlo è con un inizializzatore di convenienza.

class MemeDetailVC : UIViewController { 

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

Poi inizializzare il MemeDetailVC con let memeDetailVC = MemeDetailVC(theMeme)

Apple's documentation on initializers è abbastanza buono, ma il mio preferito è la serie Ray Wenderlich: Initialization in Depth tutorial che dovrebbe dare un sacco di spiegazione/esempi sulle varie opzioni init e la "corretta "modo di fare le cose.


EDIT: Sebbene sia possibile utilizzare un inizializzatore convenienza sui controller di visualizzazione personalizzata, ognuno è ragione nell'affermare che non è possibile utilizzare inizializzatori personalizzati durante l'inizializzazione dallo storyboard o tramite un segue storyboard.

Se la tua interfaccia è impostata nello storyboard e stai creando il controller completamente a livello di programmazione, allora un inizializzatore di convenienza è probabilmente il modo più semplice per fare ciò che stai cercando di fare poiché non devi occuparti di l'init richiesto con NSCoder (che ancora non capisco).

Se si ottiene il controller di visualizzazione tramite lo storyboard, sarà necessario seguire @Caleb Kleveter's answer e convertire il controller di visualizzazione nella sottoclasse desiderata, quindi impostare manualmente la proprietà.

+4

stay golden ponyboy – thexande

11

Come è stato specificato in una delle risposte sopra, non è possibile utilizzare sia il metodo init sia lo storyboard personalizzato. Tuttavia, è ancora possibile utilizzare un metodo statico per istanziare ViewController da uno storyboard ed eseguire impostazioni aggiuntive su di esso. Essa sarà simile a questa:

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

Non dimenticate di specificare IdentifierOfYouViewController come identificatore di vista della periferica nella storyboard. Inoltre non utilizzare meme var nel viewDidLoad poiché non è ancora inizializzato in quel punto. Potrebbe anche essere necessario modificare il nome dello storyboard nel codice sopra.

+0

Dopo l'inizializzazione di MemeDetailVC, la proprietà "meme" è esistente. Quindi arriva l'iniezione di dipendenza per assegnare il valore a "meme". In questo momento, loadView() e viewDidLoad non sono stati richiamati. Questi due metodi verranno chiamati dopo MemeDetailVC add/push per visualizzare la gerarchia. –

2

Come ha sottolineato @Caleb Kleveter, non è possibile utilizzare l'inizializzatore personalizzato durante l'inizializzazione da uno Storyboard.

Tuttavia, possiamo risolvere il problema utilizzando il metodo factory/class che istanzia l'oggetto del controller di visualizzazione dallo storyboard e restituisce l'oggetto del controller di visualizzazione. Penso che questo sia un bel modo.

Nota: Questa non è una risposta esatta alla domanda, piuttosto una soluzione alternativa per risolvere il problema.

metodo di classe marca, in classe MemeDetailVC, come segue:

// Considering your view controller resides in Main.storyboard and it's identifier is set to "MemeDetailVC" 
class func `init`(meme: Meme) -> MemeDetailVC { 
    let storyboard = UIStoryboard(name: "Main", bundle: nil) 
    let vc = storyboard.instantiateViewController(withIdentifier: "MemeDetailVC") as? MemeDetailVC 
    vc.meme = meme 
    return vc 
} 

Usage:

let memeDetailVC = MemeDetailVC.init(meme: Meme()) 
Problemi correlati