life example container ios swift uiviewcontroller initialization

ios - life - swift container view example



Init personalizado para UIViewController en Swift con configuraciĆ³n de interfaz en storyboard (6)

Tengo problemas para escribir init personalizado para la subclase de UIViewController, básicamente quiero pasar la dependencia a través del método init para viewController en lugar de establecer la propiedad directamente como viewControllerB.property = value

Así que hice un init personalizado para mi viewController y llamé a init super designado

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

La interfaz del controlador de vista reside en el guión gráfico, también hice que la interfaz para la clase personalizada sea mi controlador de vista. Y Swift requiere llamar a este método init incluso si no está haciendo nada dentro de este método. De lo contrario, el compilador se quejará ...

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

El problema es que cuando trato de llamar a mi init personalizado con MyViewController(meme: meme) no inicia las propiedades en mi viewController en absoluto ...

Estaba tratando de depurar, encontré en mi viewController, init(coder aDecoder: NSCoder) se llama primero, luego mi init personalizado se llama más tarde. Sin embargo, estos dos métodos init devuelven diferentes direcciones de memoria self .

Sospecho que algo está mal con el init para mi viewController, y siempre se devolverá con el init?(coder aDecoder: NSCoder) , que no tiene implementación.

¿Alguien sabe cómo hacer un init personalizado para su viewController correctamente? Nota: la interfaz de mi viewController está configurada en el guión gráfico

Aquí está mi código de 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) } }


Como ha señalado @Caleb Kleveter, no podemos usar un inicializador personalizado mientras se inicia desde un Storyboard.

Pero, podemos resolver el problema utilizando el método de fábrica / clase que instancia el objeto del controlador de vista desde Storyboard y devuelve el objeto del controlador de vista. Creo que esta es una forma genial.

Nota: Esta no es una respuesta exacta a la pregunta, sino una solución alternativa para resolver el problema.

Haga el método de clase, en la clase MemeDetailVC, de la siguiente manera:

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

Uso:

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


Como se especificó en una de las respuestas anteriores, no puede usar el método init y el guión gráfico personalizados. Pero aún puede usar un método estático para crear una instancia de ViewController desde un guión gráfico y realizar una configuración adicional en él. Se verá así:

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

No olvide especificar IdentifierOfYouViewController como identificador del controlador de vista en su guión gráfico. También es posible que deba cambiar el nombre del guión gráfico en el código anterior.


No puede usar un inicializador personalizado cuando inicializa desde un Storyboard, usando init?(coder aDecoder: NSCoder) es cómo Apple diseñó el storyboard para inicializar un controlador. Sin embargo, hay formas de enviar datos a un UIViewController .

El nombre de su controlador de vista tiene detail , así que supongo que llega desde un controlador diferente. En este caso, puede usar el método prepareForSegue para enviar datos al detalle (Esto es Swift 3):

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

Acabo de usar una propiedad de tipo String lugar de Meme para fines de prueba. Además, asegúrese de pasar el identificador de segue correcto ( "identifier" era solo un marcador de posición).


Originalmente hubo un par de respuestas, que fueron votadas y eliminadas por las vacas, aunque eran básicamente correctas. La respuesta es que no puedes.

Al trabajar desde una definición de guión gráfico, todas las instancias de su controlador de vista se archivan. Entonces, para iniciarlos se requiere que se use init?(coder... El coder es de donde proviene toda la información de configuración / vista.

Entonces, en este caso, no es posible llamar también a alguna otra función init con un parámetro personalizado. Debería establecerse como una propiedad al preparar el segue, o podría deshacerse de los segues y cargar las instancias directamente desde el guión gráfico y configurarlas (básicamente un patrón de fábrica usando un guión gráfico).

En todos los casos, utiliza la función init requerida del SDK y luego pasa parámetros adicionales.


Una forma de hacerlo es con un práctico inicializador.

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

Luego, inicialice su MemeDetailVC con let memeDetailVC = MemeDetailVC(theMeme)

La documentación de Apple sobre los inicializadores es bastante buena, pero mi favorito personal es la serie de tutoriales Ray Wenderlich: Initialization in Depth , que debería darle muchas explicaciones / ejemplos sobre sus diversas opciones de inicio y la forma "adecuada" de hacer las cosas.

EDITAR : Si bien puede usar un inicializador de conveniencia en los controladores de vista personalizados, todos tienen razón al afirmar que no puede usar inicializadores personalizados al inicializar desde el guión gráfico o a través de una secuencia de guiones gráficos.

Si su interfaz está configurada en el guión gráfico y está creando el controlador completamente mediante programación, entonces un inicializador de conveniencia es probablemente la forma más fácil de hacer lo que está tratando de hacer, ya que no tiene que lidiar con el inicio requerido con NSCoder (que todavía no entiendo).

Sin embargo, si obtiene su controlador de vista a través del guión gráfico, deberá seguir la respuesta de @Caleb Kleveter y lanzar el controlador de vista en su subclase deseada y luego configurar la propiedad manualmente.


UIViewController clase UIViewController ajusta al protocolo NSCoding que se define como:

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

Entonces, UIViewController tiene dos inicializadores designados init?(coder aDecoder: NSCoder) e init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) .

Storyborad llama a init?(coder aDecoder: NSCoder) directamente a init UIViewController y UIView . No hay espacio para pasar parámetros.

Una solución engorrosa es usar un caché temporal:

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 }