Przepraszam, jeśli pytano o to wcześniej, dużo szukałem i wiele odpowiedzi pochodzi z wcześniejszych wersji beta Swift, kiedy sprawy wyglądały inaczej. Nie mogę znaleźć ostatecznej odpowiedzi.
Chcę UIViewController
utworzyć podklasę i mieć niestandardowy inicjator, który pozwoli mi łatwo skonfigurować go w kodzie. Mam problem z zrobieniem tego w Swift.
Potrzebuję init()
funkcji, której mogę użyć do przekazania określonej funkcji, NSURL
której będę używać z kontrolerem widoku. W mojej głowie wygląda to tak init(withImageURL: NSURL)
. Jeśli dodam tę funkcję, prosi mnie o dodanie init(coder: NSCoder)
funkcji.
Wydaje mi się, że dzieje się tak, ponieważ jest oznaczony w superklasie required
słowem kluczowym? Więc muszę to zrobić w podklasie? Dodaję to:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
Co teraz? Czy mój specjalny inicjator jest uważany za convenience
jeden? Wyznaczony? Czy nazywam super inicjalizator? Inicjator z tej samej klasy?
Jak dodać mój specjalny inicjator do UIViewController
podklasy?
źródło
Odpowiedzi:
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) } }
źródło
ViewController()
,ViewController(imageURL: url)
lub ładowanie go z ujęć.required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
przeciwnym raziesuper.init(coder: aDecoder)
otrzymywałem błąd Propertyself.someProperty
not initialized nasuper.init
wezwanieinit(coder: aDecoder)
.fatalError
WystarcząDla tych, którzy piszą UI w kodzie
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") } }
źródło
Jest to bardzo podobne do innych odpowiedzi, ale z pewnym wyjaśnieniem. Przyjęta odpowiedź jest myląca, ponieważ jej właściwość jest opcjonalna i nie ujawnia faktu, że
init?(coder: NSCoder)
MUSISZ zainicjować każdą właściwość, a jedynym rozwiązaniem jest posiadaniefatalError()
. Ostatecznie możesz uciec, tworząc opcje dla swoich właściwości, ale to tak naprawdę nie odpowiada na pytanie 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") } }
Zasadniczo musisz PRZERWAĆ ładowanie go ze scenorysu. Czemu?
Bo kiedy wezwać viewController
storyboard.instantiateViewController(withIdentifier: "viewController")
następnie UIKit zrobi swoje rzeczy i nazywająrequired init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
Nigdy nie możesz przekierować tego wywołania do innej metody init.
Jednak dla programowo utworzonych viewController lub nib utworzonych viewControllers możesz przekierować to wywołanie, jak pokazano powyżej.
źródło
Są udokumentowane tutaj .
źródło
Jeśli potrzebujesz niestandardowego inicjatora dla popover, na przykład, możesz użyć następującego podejścia:
Utwórz niestandardowy init, który używa superinit z nibName i bundle, a następnie uzyskaj dostęp do właściwości widoku, aby wymusić ładowanie hierarchii widoków.
Następnie w funkcji viewDidLoad możesz skonfigurować widoki za pomocą parametrów przekazanych podczas inicjalizacji.
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) }
źródło