创建一个通用的 UViewController 初始化器
Creating a generic UViewController initialiser
我正在尝试创建可用于初始化新实例的 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 编写的旧项目,因此希望得到支持的答案。
您可以将 return 类型更改为 Self
,这将匹配您调用方法的类型。
这是我用过的方法。它将需要放入协议扩展中。
static func loadFromStoryboard() -> Self? {
let storyboard = UIStoryboard(name: NSStringFromClass(self),
bundle: Bundle(for: self))
return storyboard.instantiateInitialViewController() as? Self
}
我使用这个扩展:
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()
我假设您不想让您的每个 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
}
}
添加到@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
}
}
然后,在我的 table 视图控制器中,像这样(我试图将每个单元格的行自动化到它自己的 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
内
这是一个重型解决方案,但如果您决定投资它,那将是非常好的。
SwiftGen 可以生成代码,以编程方式从故事板中初始化视图控制器。 Docs
// You can instantiate scenes using the `instantiate` method:
let vc = StoryboardScene.Dependency.dependent.instantiate()
方法二
enum Storyboards: String {
case main = "Main"
case home = "Home"
func instantiateVC<T>(_ identifier: T.Type) -> T? {
let storyboard = UIStoryboard(name: rawValue, bundle: nil)
guard let viewcontroller = storyboard.instantiateViewController(withIdentifier: String(describing: identifier)) as? T else { return nil}
return viewcontroller
}
}
用法:(确保故事板 ID 和 class-名称相同。)
if let vc = Storyboards.main.instantiateVC(ViewController.self) {
self.present(vc, animated: true, completion: nil)
}
方法一
故事板扩展:
extension UIStoryboard {
enum Name: String {
case main = "Main"
case home = "Home"
}
static func instantiateViewController<T>(storyboard name: Name = .main, ofType type: T.Type) -> T? {
return UIStoryboard(name: name.rawValue, bundle: nil).instantiateViewController(withIdentifier: String(describing: type.self)) as? T
}
}
用法:(确保故事板 ID 和 class-名称相同。)
if let vc = UIStoryboard.instantiateViewController(storyboard: .home, ofType: HomeVC.self) {
self.present(vc, animated: true, completion: nil)
}
我正在尝试创建可用于初始化新实例的 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 编写的旧项目,因此希望得到支持的答案。
您可以将 return 类型更改为 Self
,这将匹配您调用方法的类型。
这是我用过的方法。它将需要放入协议扩展中。
static func loadFromStoryboard() -> Self? {
let storyboard = UIStoryboard(name: NSStringFromClass(self),
bundle: Bundle(for: self))
return storyboard.instantiateInitialViewController() as? Self
}
我使用这个扩展:
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()
我假设您不想让您的每个 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
}
}
添加到@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
}
}
然后,在我的 table 视图控制器中,像这样(我试图将每个单元格的行自动化到它自己的 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
这是一个重型解决方案,但如果您决定投资它,那将是非常好的。
SwiftGen 可以生成代码,以编程方式从故事板中初始化视图控制器。 Docs
// You can instantiate scenes using the `instantiate` method:
let vc = StoryboardScene.Dependency.dependent.instantiate()
方法二
enum Storyboards: String {
case main = "Main"
case home = "Home"
func instantiateVC<T>(_ identifier: T.Type) -> T? {
let storyboard = UIStoryboard(name: rawValue, bundle: nil)
guard let viewcontroller = storyboard.instantiateViewController(withIdentifier: String(describing: identifier)) as? T else { return nil}
return viewcontroller
}
}
用法:(确保故事板 ID 和 class-名称相同。)
if let vc = Storyboards.main.instantiateVC(ViewController.self) {
self.present(vc, animated: true, completion: nil)
}
方法一
故事板扩展:
extension UIStoryboard {
enum Name: String {
case main = "Main"
case home = "Home"
}
static func instantiateViewController<T>(storyboard name: Name = .main, ofType type: T.Type) -> T? {
return UIStoryboard(name: name.rawValue, bundle: nil).instantiateViewController(withIdentifier: String(describing: type.self)) as? T
}
}
用法:(确保故事板 ID 和 class-名称相同。)
if let vc = UIStoryboard.instantiateViewController(storyboard: .home, ofType: HomeVC.self) {
self.present(vc, animated: true, completion: nil)
}