[英]How to initialize ViewController from storyboard using it's Coder
I realize that this might be difficult to achieve, but I would like to be able to initialize ViewControllers
from storyboard
using init(coder: NSCoder)
function directly.我意识到这可能很难实现,但我希望能够直接使用
init(coder: NSCoder)
function 从storyboard
初始化ViewControllers
。 I mean not using storyboard.instantiateViewController(identifier: coder: )
- I know I could use this, but this is what I would like to skip.我的意思是不使用
storyboard.instantiateViewController(identifier: coder: )
- 我知道我可以使用它,但这是我想跳过的。 Instead I would prefer to have a init in ViewController
like this: init(viewModel: ViewModel)
and use this init to initialize this ViewController
from the storyboard
.相反,我更愿意在
ViewController
中有一个像这样的 init: init(viewModel: ViewModel)
并使用这个 init 从storyboard
初始化这个ViewController
。 Inside this init I imagine having some mechanism that would open my storyboard
, and extracted it's coder somehow, so that I could write:在这个 init 中,我想象有一些机制可以打开我的
storyboard
,并以某种方式提取它的编码器,这样我就可以写:
static let storyboard = UIStoryboard(named: "Game")
private let viewModel: ViewModel
init(viewModel: ViewModel) {
self.viewModel = viewModel
let identifier = String(describing: Self.self)
let coder: NSCoder = Self.storyboard.somehowGetTheCoderForViewController(withId: identifier)
super.init(coder: coder)
}
Problem is how to read storyboard in such a way that I was able to get the coder of particular ViewController from it.问题是如何以我能够从中获取特定 ViewController 的编码器的方式读取 storyboard。 Again - I know this can be solved by using something like this
再次 - 我知道这可以通过使用这样的东西来解决
storyboard.instantiateViewController(identifier: String(describing: Self.self)) { coder in
Self.init(coder: coder, viewModel: viewModel)
}
But Im looking for a way to not use instantiateViewController
, and just be able to get the coder
, so that later I could just initiate
VC like this:但是我正在寻找一种不使用
instantiateViewController
的方法,而只是能够获得coder
,这样以后我就可以像这样initiate
VC 了:
let viewController = ViewContorller(viewModel: viewModel)
So the question is how to unpack storyboard
, and retrieve coder
object for some ViewController
.所以问题是如何解压
storyboard
,并为一些ViewController
检索coder
object 。
You are not supposed to do this.你不应该这样做。
As the documentation of init(nibName:bundle:)
says:正如
init(nibName:bundle:)
的文档所说:
This is the designated initializer for this class. When using a storyboard to define your view controller and its associated views, you never initialize your view controller class directly.
这是这个 class 的指定初始化程序。当使用 storyboard 定义视图 controller 及其关联视图时,您永远不会直接初始化视图 controller class。 Instead, view controllers are instantiated by the storyboard either automatically when a segue is triggered or programmatically when your app calls the
instantiateViewController(withIdentifier:)
method of a storyboard object.相反,视图控制器由 storyboard 实例化,或者在触发 segue 时自动实例化,或者在您的应用程序调用 storyboard object 的
instantiateViewController(withIdentifier:)
方法时以编程方式实例化。
When initialising a VC using an initialiser, that initialiser is what you should use, not init(coder:)
.使用初始化程序初始化 VC 时,您应该使用该初始化程序,而不是
init(coder:)
。 If you use storyboards, then you should use instantiateViewController
, or use segues to get new VCs.如果你使用故事板,那么你应该使用
instantiateViewController
,或者使用 segues 来获取新的 VC。
So to achieve this syntax:所以要实现这个语法:
let viewController = ViewContorller(viewModel: viewModel)
You can put your VCs in xib files, and do:您可以将您的 VC 放入 xib 文件中,然后执行以下操作:
init(viewModel: ViewModel) {
self.viewModel = viewModel
let identifier = String(describing: Self.self)
let coder: NSCoder = Self.storyboard.somehowGetTheCoderForViewController(withId: identifier)
super.init(nibName: String(describing: Self.self), bundle: nil)
}
If you must use storyboards, then you can't use an initialiser syntax for initialising VCs.如果你必须使用故事板,那么你不能使用初始化语法来初始化 VC。 You can instead make a factory method, such as:
您可以改用工厂方法,例如:
let viewController = ViewContorller.from(viewModel: viewModel)
And implement it using UIStoryboard.instantiateViewController
.并使用
UIStoryboard.instantiateViewController
实现它。
This is why the initializer instantiateViewController(identifier:creator:)
exists.这就是初始化程序
instantiateViewController(identifier:creator:)
存在的原因。
https://developer.apple.com/documentation/uikit/uistoryboard/3213989-instantiateviewcontroller https://developer.apple.com/documentation/uikit/uistoryboard/3213989-instantiateviewcontroller
You receive the coder
and use it to call a custom initializer that itself calls the coder
initializer but also does other custom initialization.您收到
coder
并使用它来调用自定义初始化器,该初始化器本身调用coder
初始化器,但也执行其他自定义初始化。
To illustrate, suppose we have a ViewController class with a message
String property to be presented to the user in a UILabel when the view appears.为了说明这一点,假设我们有一个 ViewController class,它有一个
message
字符串属性,当视图出现时,它会在 UILabel 中呈现给用户。 So we've given ViewController an init(coder:message:)
initializer:所以我们给了 ViewController 一个
init(coder:message:)
初始化器:
class ViewController: UIViewController {
@IBOutlet var lab : UILabel!
var message: String = ""
convenience init(coder:NSCoder, message:String) {
self.init(coder:coder)!
self.message = message
}
override func viewDidLoad() {
super.viewDidLoad()
self.lab.text = self.message
}
}
Left to its own devices, however, the runtime will never call our init(coder:message:)
initializer;然而,任其自生自灭,运行时永远不会调用我们的
init(coder:message:)
初始化程序; it knows nothing of it!它对此一无所知! The only way the runtime knows to instantiate a view controller from a storyboard is by calling
init(coder:)
.运行时知道从 storyboard 实例化视图 controller 的唯一方法是调用
init(coder:)
。 But we can call init(coder:message:)
when we instantiate the view controller from the storyboard.但是当我们从 storyboard 实例化视图 controller 时,我们可以调用
init(coder:message:)
。
Suppose this is the storyboard's initial view controller, and we're calling it in the scene delegate at launch time:假设这是故事板的初始视图 controller,我们在启动时在场景委托中调用它:
func scene(_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
guard let scene = (scene as? UIWindowScene) else { return }
let message = "Howdy, world!" // or whatever
self.window = UIWindow(windowScene: scene)
let sb = UIStoryboard(name: "Main", bundle: nil)
self.window?.rootViewController = sb.instantiateInitialViewController {
return ViewController(coder: $0, message: message)
}
self.window?.makeKeyAndVisible()
}
We call instantiateViewController(identifier:creator:)
and the creator:
function calls init(coder:message:)
— which, in turn, calls init(coder:)
.我们调用
instantiateViewController(identifier:creator:)
和creator:
function 调用init(coder:message:)
—— 反过来调用init(coder:)
。 Thus our use of the creator:
function is legal, and the view appears correctly.因此我们对
creator:
function 的使用是合法的,并且视图正确显示。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.