【问题标题】:Custom init of UIViewController from storyboard来自故事板的 UIViewController 的自定义初始化
【发布时间】:2015-08-07 13:43:27
【问题描述】:

我试图找到一些相关的问题,但没有得到任何东西,希望有人能提供帮助。

我在故事板上设置了一些 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)
  }

}

我似乎无法做到这一点并通过使用 let 而不是 var 来保持 i 不可变。

【问题讨论】:

  • 你需要在调用super.init(coder: aDecoder)之前初始化i。这样可以修复编译器错误。
  • 我只是编辑我的帖子,我认为这是你需要的,看到它的结尾
  • 你有什么解决方案我面临同样的问题但找不到答案

标签: ios swift uiviewcontroller


【解决方案1】:

我之前的答案的简化,它快速且避免了替代的hacky修复:

这里是一个细节视图控制器,您可能希望从情节提要中实例化一个 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)")
        }
    }
}

以下是您将如何使用它推送到 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

【讨论】:

  • 没有办法在这里使用let 而不是var
  • 但是如果那是细节视图控制器,它由包含 UIsplitviewcontroller 的故事板初始化,你什么时候调用你的工厂方法?您需要在情节提要中删除与它的详细关系吗?
【解决方案2】:

下面是两个助手,一个是 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 项目)

【讨论】:

    【解决方案3】:

    解决这个问题的(丑陋的)方法:

    您可以从代码中的外部缓冲区设置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 传递值。更好的答案here

    【讨论】:

    • 查看我的回答stackoverflow.com/a/41926532/2722398。正如您承认的那样,您的方法很丑陋。在人们开始将其作为一种做法之前,应该将其删除。因为人们开始使用 AppDelegate 作为全局缓冲区,所以我不得不进行丑陋的清理工作。
    • @hahmed 对不起,如果我冒犯了你的 iOS 专业知识哈哈。我只是认为,如果人们可以在丑陋但简单的解决方案和复杂且较长的解决方案之间做出选择,那会更好。
    • 这里是一个简单而干净的解决方案,用于更简单的实施:stackoverflow.com/a/41926924/2722398
    • @hahmed 好多了,谢谢!我编辑我的答案以指向你的答案。
    • 不要使用故事板
    【解决方案4】:

    从 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 类型。

    【讨论】:

      猜你喜欢
      • 2013-08-22
      • 1970-01-01
      • 1970-01-01
      • 2018-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-04
      相关资源
      最近更新 更多