![](/img/trans.png)
[英]Loading UIViewController from storyboard through its custom init method
[英]Custom init of UIViewController from storyboard
我試圖找到一些相關問題,但一無所獲,希望有人能提供幫助。
我在 storyboard 上設置了一些 UIViewController。然后我想在代碼中加載其中一個視圖控制器並將其推送到導航堆棧。 我想出正確的方法是使用
instantiateViewControllerWithIdentifier
這會調用init(coder: NSCoder)
並且一切正常,我的程序可以運行,但我希望能夠有一個自定義初始化程序來為我的視圖 controller 設置一些變量。請考慮以下事項:
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
需要在創建 object 時指定。 有什么解決辦法嗎? 我對將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)
}
}
我似乎無法做到這一點並通過使用let
而不是var
來保持i
不變。
簡化我之前的答案,這是快速的,並避免了其他hacky修復:
這是一個詳細視圖控制器,您可能希望從設置了objectID的storyboard實例化:
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)")
}
}
}
以下是如何使用它來推送一個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
下面是兩個幫助程序,一個是Storyboard枚舉,在項目中添加項目中的每個故事板作為一個案例。 該名稱必須與{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()
}
}
}
(架構影響/復制Kickstarter的開源iOS項目)
解決此問題的一種(丑陋)方法:
您可以在代碼中的外部緩沖區中設置let i
(本例中為AppDelegate變量)
required init?(coder aDecoder: NSCoder) {
self.i = UIApplication.shared().delegate.bufferForI
super.init(coder: aDecoder)
}
當你通過Storyboard啟動你的UIViewController時:
UIApplication.shared().delegate.bufferForI = myIValue
self.navigationController!.pushViewControllerFading(self.storyboard!.instantiateViewController(withIdentifier: "myViewControllerID") as UIViewController)
編輯:您不必通過AppDelegate傳遞值。 這里更好的答案。
從iOS 13
開始,可以使用最新更新的 API,同時從Storyboard
UIViewController
首先,讓我們為UIViewController
創建自定義初始化程序,它使用NSCoder
:
final class ViewController: UIViewController {
var someProperty: Int
init(coder: NSCoder, someProperty: Int) {
self.someProperty = someProperty
super.init(coder: coder)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
稍后,您可以使用更新的 API 創建ViewController
:
let viewController = UIStoryboard.yourStoryboard.instantiateViewController(identifier: String(describing: ViewController.self)) { creator in
let viewController = UIViewController(coder: creator, someProperty: someValue)
return UIViewController()
}
如您所見,在閉包中,我們傳遞了新的初始化程序。 這種創建 viewController 的方法非常強大,因為當viewController
使用viewController
創建時,我們(最終)可以使用初始化器,這取決於required init(coder: NSCoder)
UIStoryboard
有關詳細信息, 請參閱此處。
從 iOS 13 開始,故事板中有一項新功能。 如果您使用 segue 操作,則在實例化時將數據傳遞給故事板視圖控制器非常簡單。 在界面構建器中,從 segue 控制拖動到將執行 segue 的視圖控制器。 然后實現類似於以下內容的附加參數:
在源 VC 中:
@IBSegueAction func showChooser(_ coder: NSCoder, sender: Any?) -> ChooserTableViewController? {
return ChooserTableViewController(coder: coder, useMap: true)
}
在目標 VC 中:
let useMap: Bool
init?(coder: NSCoder, useMap: Bool) {
self.user = useMap
super.init(coder: coder)
}
如果您需要在不使用 segue 的情況下實例化 VC,故事板有新的實例化函數,允許您在視圖控制器創建期間調用自定義編碼器 init。 有關詳細信息,請參閱 UIStoryboardViewControllerCreator 類型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.