J'ai essayé de trouver des questions pertinentes mais je n'ai rien pu obtenir, j'espère que quelqu'un pourra vous aider.
J'ai installé certains UIViewController sur un storyboard. Je souhaite ensuite charger un des contrôleurs de vue dans le code et le placer dans la pile de navigation. Je trouve que la bonne façon de faire est d'utiliser
instantiateViewControllerWithIdentifier
Ceci appelle init(coder: NSCoder)
et tout va bien, mon programme fonctionne, mais je veux pouvoir utiliser un initialiseur personnalisé qui configure certaines variables pour mon contrôleur de vue. Considérer ce qui suit:
class A : UIViewController {
let i : Int
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// property self.i not initialized at super.init call
}
}
Je reçois évidemment une erreur, car i
doit être spécifié au moment de la création de l'objet. Des solutions à cela? Je ne suis pas intéressé à déclarer i
comme var
et à le configurer ultérieurement car cela annule le point et je n'ai plus de garantie du compilateur que i
est immuable.
Supposons que j'ai une ViewController
chargée qui a une variable i
. Cette valeur est variable et peut changer. Supposons maintenant de ce ViewController que je veuille en présenter un autre et l’initialiser avec i
.
class ViewController: UIViewController {
var i : Int
// ... other things
// in response to some button tap...
@IBAction func tappedButton(sender: AnyObject) {
let st = UIStoryboard(name: "Main", bundle: nil)
let vc = st.instantiateViewControllerWithIdentifier("AControllerID") as! A
// How do I initialize A with i ?
self.presentViewController(vc, animated: true, completion: nil)
}
}
Je ne semble pas pouvoir faire cela et garder i
immuable en utilisant let
au lieu de var
.
Une simplification de ma réponse précédente qui est rapide et évite les correctifs alternatifs:
Voici un contrôleur de vue de détail que vous voudrez peut-être instancier à partir du storyboard avec un ensemble objectID:
import UIKit
class DetailViewController: UIViewController {
var objectID : Int!
internal static func instantiate(with objectID: Int) -> DetailViewController {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DetailViewController") as DetailViewController
vc.objectID = objectID
return vc
}
override func viewDidLoad() {
super.viewDidLoad()
if((objectID) != nil){
print("Here is my objectID: \(objectID)")
}
}
}
Voici comment vous l'utiliseriez pour pousser sur un contrôleur de navigation avec objectID défini sur 1:
self.navigationController.pushViewController(DetailViewController.instantiate(1), animated: true)
Ajout d'un article de blog: https://theswiftcook.wordpress.com/2017/02/17/how-to-initialize-a-storyboard-viewcontroller-with-data- sans-segues-Swift-3-0git/ /
Lien vers l'exemple sur GitHub: https://github.com/hammadzz/Instantiate-ViewController-From-Storyboard-With-Data
Ci-dessous deux assistants, l'un est un enum Storyboard, ajoutez chaque storyboard de votre projet en tant que cas sous cet enum. Le nom doit correspondre au fichier {. Storyboard_name} .storyboard. Chaque contrôleur de vue de votre storyboard doit avoir son identifiant de storyboard défini sur le nom de la classe. C'est une pratique assez courante.
import UIKit
public enum Storyboard: String {
case Main
case AnotherStoryboard
//case {storyboard_name}
public func instantiate<VC: UIViewController>(_ viewController: VC.Type) -> VC {
guard
let vc = UIStoryboard(name: self.rawValue, bundle: nil)
.instantiateViewController(withIdentifier: VC.storyboardIdentifier) as? VC
else { fatalError("Couldn't instantiate \(VC.storyboardIdentifier) from \(self.rawValue)") }
return vc
}
public func instantiateInitialVC() -> UIViewController {
guard let vc = UIStoryboard(name: self.rawValue, bundle: nil).instantiateInitialViewController() else {
fatalError("Couldn't instantiate initial viewcontroller from \(self.rawValue)")
}
return vc
}
}
extension UIViewController {
public static var defaultNib: String {
return self.description().components(separatedBy: ".").dropFirst().joined(separator: ".")
}
public static var storyboardIdentifier: String {
return self.description().components(separatedBy: ".").dropFirst().joined(separator: ".")
}
}
Voici comment vous pouvez instancier à partir d'un storyboard avec une valeur définie dans votre contrôleur de vue. Voici la magie:
import UIKit
class DetailViewController: UIViewController {
var objectID : Int!
var objectDetails: ObjectDetails = ObjectDetails()
internal static func instantiate(with objectID: Int) -> DetailViewController {
let vc = Storyboard.Main.instantiate(DetailViewController.self)
vc.objectID = objectID
return vc
}
override func viewDidLoad() {
super.viewDidLoad()
if((objectID) != nil){
// In this method I use to make a web request to pull details from an API
loadObjectDetails()
}
}
}
(Architecture influencée/copie du projet iOS open source de Kickstarter)
Un moyen (laid) de résoudre ce problème:
Vous pouvez définir votre let i
à partir d'un tampon externe dans votre code (variable AppDelegate dans cet exemple)
required init?(coder aDecoder: NSCoder) {
self.i = UIApplication.shared().delegate.bufferForI
super.init(coder: aDecoder)
}
Et lorsque vous démarrez votre UIViewController via Storyboard:
UIApplication.shared().delegate.bufferForI = myIValue
self.navigationController!.pushViewControllerFading(self.storyboard!.instantiateViewController(withIdentifier: "myViewControllerID") as UIViewController)
EDIT: Il n'est pas nécessaire de transmettre la valeur via AppDelegate. Meilleure réponse ici .