Пользовательский запуск UIViewController из раскадровки
Я попытался найти некоторые актуальные вопросы, но ничего не мог получить, надеюсь, что кто-то может помочь.
Я установил некоторый UIViewController на раскадровке. Затем я хочу загрузить один из контроллеров представления в код и вставить его в стек навигации. Я считаю, что правильный способ сделать это - использовать
instantiateViewControllerWithIdentifier
Это вызывает init(coder: NSCoder)
, и все хорошо, моя программа работает, но я хочу иметь собственный инициализатор, который устанавливает некоторые переменные для моего контроллера представления. Рассмотрим следующее:
class A : UIViewController {
let i : Int
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// property self.i not initialized at super.init call
}
}
Я, очевидно, получаю ошибку, так как i
необходимо указать во время создания объекта. Любые решения? Мне не интересно объявлять i
как var
и настраивать его позже, поскольку это побеждает точку, и у меня больше нет гарантии компилятора, что i
является неизменяемым.
Редактирование разъяснений
Предположим, что у меня есть загруженный в настоящее время ViewController
, который имеет некоторую переменную i
. Это значение является переменной и может меняться. Теперь предположим, что из этого ViewController я хочу представить еще один и инициализировать его с помощью 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)
}
}
Кажется, я не могу этого сделать и сохраняю i
неизменяемым, используя let
вместо var
.
Ответы
Ответ 1
Упрощение моего предыдущего ответа, которое быстро и позволяет избежать альтернативных хакерских исправлений:
Вот контроллер подробного представления, который вы, возможно, захотите создать из раскадровки с набором 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)")
}
}
}
Вот как вы могли бы использовать его, чтобы надавить на контроллер навигации с идентификатором объекта, установленным в 1:
self.navigationController.pushViewController(DetailViewController.instantiate(1), animated: true)
Добавлено сообщение в блоге:
https://theswiftcook.wordpress.com/2017/02/17/how-to-initialize-a-storyboard-viewcontroller-with-data-without-segues-swift-3-0git/
Ссылка на пример на GitHub:
https://github.com/hammadzz/Instantiate-ViewController-From-Storyboard-With-Data
Ответ 2
Просто запустите свою собственность, прежде чем вызвать super.init. Это то, что вы должны сделать в Swift
class A : UIViewController {
let i : Int
required init(coder aDecoder: NSCoder) {
i = 10;
//Property should be init before you call super
super.init(coder: aDecoder)
}
}
Насколько я знаю, свойство let можно инициализировать только из метода init.
Вы хотите, чтобы вы i
в качестве интерфейса, это означает, что другой класс может изменить этот объект. Итак, я думаю, что лучше использовать var
Ответ 3
(уродливый) способ решить эту проблему:
Вы можете установить let i
из внешнего буфера в своем коде (переменная AppDelegate в этом примере)
required init?(coder aDecoder: NSCoder) {
self.i = UIApplication.shared().delegate.bufferForI
super.init(coder: aDecoder)
}
И когда вы инициируете свой UIViewController с помощью раскадровки:
UIApplication.shared().delegate.bufferForI = myIValue
self.navigationController!.pushViewControllerFading(self.storyboard!.instantiateViewController(withIdentifier: "myViewControllerID") as UIViewController)
EDIT:
Вам не нужно передавать значение через AppDelegate. Лучший ответ здесь.
Ответ 4
Ниже приведены два помощника, один - перечисление раскадровки, добавьте каждую раскладку в свой проект в качестве примера в этом перечислении. Имя должно соответствовать файлу {storyboard_name}.storyboard. Каждый контроллер представления в вашем раскадровке должен иметь свой идентификатор раскадровки, установленный на имя класса. Это довольно стандартная практика.
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: ".")
}
}
Вот как вы можете создать экземпляр из раскадровки со значением, установленным в вашем контроллере представления. Вот волшебство:
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()
}
}
}
(Архитектура повлияла/копирует проект iOS с открытым исходным кодом Kickstarter)