Disculpas si esto se ha preguntado antes, he buscado mucho y muchas respuestas son de versiones beta de Swift anteriores cuando las cosas eran diferentes. Parece que no puedo encontrar una respuesta definitiva.
Quiero UIViewController
crear una subclase y tener un inicializador personalizado que me permita configurarlo en código fácilmente. Tengo problemas para hacer esto en Swift.
Quiero una init()
función que pueda usar para pasar un específico NSURL
que luego usaré con el controlador de vista. En mi mente se parece a algo así init(withImageURL: NSURL)
. Si agrego esa función, me pide que agregue la init(coder: NSCoder)
función.
Creo que esto se debe a que está marcado en la superclase con la required
palabra clave? ¿Entonces tengo que hacerlo en la subclase? Lo agrego:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
¿Ahora que? ¿Mi inicializador especial se considera convenience
uno? ¿Uno designado? ¿Llamo a un superinicializador? ¿Un inicializador de la misma clase?
¿Cómo agrego mi inicializador especial a una UIViewController
subclase?
fuente
Respuestas:
class ViewController: UIViewController { var imageURL: NSURL? // this is a convenient way to create this view controller without a imageURL convenience init() { self.init(imageURL: nil) } init(imageURL: NSURL?) { self.imageURL = imageURL super.init(nibName: nil, bundle: nil) } // if this view controller is loaded from a storyboard, imageURL will be nil /* Xcode 6 required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } */ // Xcode 7 & 8 required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } }
fuente
ViewController()
,ViewController(imageURL: url)
o cargarlo de un guión.required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
contrariosuper.init(coder: aDecoder)
, recibía el error de Propiedadself.someProperty
no inicializada en lasuper.init
llamadainit(coder: aDecoder)
. ElfatalError
testamento será suficientePara aquellos que escriben UI en código
class Your_ViewController : UIViewController { let your_property : String init(your_property: String) { self.your_property = your_property super.init(nibName: nil, bundle: nil) } override func viewDidLoad() { super.viewDidLoad() } required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") } }
fuente
Esto es muy similar a las otras respuestas, pero con alguna explicación. La respuesta aceptada es engañosa porque su propiedad es opcional y no expone el hecho de que
init?(coder: NSCoder)
DEBE inicializar todas y cada una de las propiedades y la única solución es tener unfatalError()
. En última instancia, podría salirse con la suya haciendo que sus propiedades sean opcionales, pero eso no responde realmente a la pregunta del OP.// Think more of a OnlyNibOrProgrammatic_NOTStoryboardViewController class ViewController: UIViewController { let name: String override func viewDidLoad() { super.viewDidLoad() } // I don't have a nib. It's all through my code. init(name: String) { self.name = name super.init(nibName: nil, bundle: nil) } // I have a nib. I'd like to use my nib and also initialze the `name` property init(name: String, nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle? ) { self.name = name super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } // when you do storyboard.instantiateViewController(withIdentifier: "ViewController") // The SYSTEM will never call this! // it wants to call the required initializer! init?(name: String, coder aDecoder: NSCoder) { self.name = "name" super.init(coder: aDecoder) } // when you do storyboard.instantiateViewController(withIdentifier: "ViewController") // The SYSTEM WILL call this! // because this is its required initializer! // but what are you going to do for your `name` property?! // are you just going to do `self.name = "default Name" just to make it compile?! // Since you can't do anything then it's just best to leave it as `fatalError()` required init?(coder aDecoder: NSCoder) { fatalError("I WILL NEVER instantiate through storyboard! It's impossible to initialize super.init?(coder aDecoder: NSCoder) with any other parameter") } }
Básicamente tienes que ABANDONAR cargarlo desde el guión gráfico. ¿Por qué?
Porque cuando llamas a un viewController
storyboard.instantiateViewController(withIdentifier: "viewController")
, UIKit hará lo suyo y llamarárequired init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
Nunca puede redirigir esa llamada a otro método de inicio.
Sin embargo, para viewController creado mediante programación o viewControllers creado con nib, puede redirigir esa llamada como se muestra arriba.
fuente
Están documentados aquí .
fuente
Si necesita un inicio personalizado para un popover, por ejemplo, puede usar el siguiente enfoque:
Cree un init personalizado que use el super init con nibName y bundle y luego acceda a la propiedad de la vista para forzar la carga de la jerarquía de vistas.
Luego, en la función viewDidLoad puede configurar las vistas con los parámetros pasados en la inicialización.
import UIKit struct Player { let name: String let age: Int } class VC: UIViewController { @IBOutlet weak var playerName: UILabel! let player: Player init(player: Player) { self.player = player super.init(nibName: "VC", bundle: Bundle.main) if let view = view, view.isHidden {} } override func viewDidLoad() { super.viewDidLoad() configure() } func configure() { playerName.text = player.name + "\(player.age)" } } func showPlayerVC() { let foo = Player(name: "bar", age: 666) let vc = VC(player: foo) present(vc, animated: true, completion: nil) }
fuente