创建一个通用的UViewController初始化器

时间:2017-05-09 15:49:48

标签: ios swift generics uiviewcontroller swift-protocols

我正在尝试创建一个UIViewController扩展程序,我可以用它来初始化新实例。对于我项目中的每个视图控制器,我都有一个相应的故事板。

即。

EditSomethingViewController.swift
EditSomethingViewController.storyboard

这是我到目前为止所做的:

extension UIViewController {

    static func initalize() -> UIViewController? {
        let name = String(self)

        let storyboard = UIStoryboard(name: name, bundle: nil)

        return storyboard.instantiateInitialViewController()
    }

}

然而,这意味着当我使用它时,我仍然需要施放响应。

即。

if let viewController = EditSomethingViewController.initalize() as? EditSomethingViewController {
     // do something with view controller here  
}

是否可以以这样的方式创建扩展程序,这意味着我不必投出响应?

P.S。处理用Swift 2.3编写的旧项目,所以会很感激支持的答案。

5 个答案:

答案 0 :(得分:3)

我使用此扩展程序:

extension UIViewController
{
    class func instantiateFromStoryboard(_ name: String = "Main") -> Self
    {
        return instantiateFromStoryboardHelper(name)
    }

    fileprivate class func instantiateFromStoryboardHelper<T>(_ name: String) -> T
    {
        let storyboard = UIStoryboard(name: name, bundle: nil)
        let controller = storyboard.instantiateViewController(withIdentifier: String(describing: self)) as! T
        return controller
    }
}

用法:

let controller = MyViewController.instantiateFromStoryboard()

答案 1 :(得分:0)

您可以将返回类型更改为Self,该类型与您调用方法的类型相匹配。

这是我用来做这个的方法。它需要被放入协议扩展中。

static func loadFromStoryboard() -> Self? {
    let storyboard = UIStoryboard(name: NSStringFromClass(self),
                                  bundle: Bundle(for: self))
    return storyboard.instantiateInitialViewController() as? Self
}

答案 2 :(得分:0)

我认为您不希望手动使每个VC符合协议。这将是太多的工作:))

我没有测试过这个,但这应该有效:

protocol Initializable {
    static func initalize() -> Self?
}

extension UIViewController: Initializable {
    static func initalize() -> Self? {
        let name = NSStringFromClass(self as! AnyClass)

        let storyboard = UIStoryboard(name: name, bundle: nil)

        return storyboard.getInitialVC(type: self)
    }
}

extension UIStoryboard {
    func getInitialVC<T: UIViewController>(type: T.Type) -> T? {
        return instantiateInitialViewController() as? T
    }
}

答案 3 :(得分:0)

在@Chikabuz答案中,我可以将他的代码段添加到这样的内容中

extension UIViewController
{
    class func instantiateFromStoryboard(_ name: String = "Main", identifier: String) -> Self
    {
        return instantiateFromStoryboardHelper(name, identifier: identifier)
    }

    fileprivate class func instantiateFromStoryboardHelper<T>(_ name: String, identifier: String) -> T
    {
        let storyboard = UIStoryboard(name: name, bundle: nil)
        let controller = storyboard.instantiateViewController(withIdentifier: identifier) as! T
        return controller
    }
}

然后,在我的表视图控制器中,这样的事情(我正在尝试将每个单元格的行自动执行到自己的vc):

struct MenuItem {
    let title: String
    let subtitle: String
    let `class`: AnyClass
}
class MenuViewController: UITableViewController {

    private var menu: [MenuItem] = [
        MenuItem(title: "Some View",
                 subtitle: "The good old description",
                 class: FirstViewController.self),
        MenuItem(title: "Another View",
                 subtitle: "Demo of this view",
                 class: SecondViewController.self)
    ]
    ...
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: 
    IndexPath) {
        let item = menu[indexPath.row]

        let vcClass = item.class as! UIViewController.Type
        let vc = vcClass.instantiateFromStoryboard(identifier: String(describing: item.class))

        self.navigationController?.pushViewController(vc, animated: true)
        tableView.deselectRow(at: indexPath, animated: true)
    }
}

那里,魔术! 您的情节提要不必与vc的文件名完全相同,但默认为Main

PS:如果您崩溃了,可能是您忘记添加必须与您的vc名称完全相同的identifier,或者... vc不在Main.storyboard

答案 4 :(得分:0)

这是一种重型解决方案,但是如果您决定投资,那将是非常好的。

SwiftGen可以生成代码,以便从情节提要中以编程方式初始化视图控制器。 Docs

// You can instantiate scenes using the `instantiate` method:
let vc = StoryboardScene.Dependency.dependent.instantiate()