我正在尝试创建一个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编写的旧项目,所以会很感激支持的答案。
答案 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()