簡體   English   中英

從 storyboard 自定義 UIViewController

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM