UIViewController扩展从storyboard实例化

我想在Swift中编写一个扩展来处理故事板中的UIViewController实例化。

我的想法如下:由于UIStoryboard的方法instantiateViewControllerWithIdentifier需要一个标识符来实例化一个给定的故事板的视图控制器,为什么不分配每个视图控制器在我的故事板一个标识符等于其确切的类名(即UserDetailViewController将有一个标识符“UserDetailViewController”),并在UIViewController上创build一个类方法,该方法将:

  • 接受UIStoryboard实例作为唯一参数
  • 获取当前类名称作为string
  • 在类别名称作为参数在故事板实例上调用instantiateViewControllerWithIdentifier
  • 获取新创build的UIViewController实例,并返回它

所以,而不是(它重复类名string,不是很好)

 let vc = self.storyboard?.instantiateViewControllerWithIdentifier("UserDetailViewController") as UserDetailViewController 

这将是:

 let vc = UserDetailViewController.instantiateFromStoryboard(self.storyboard!) 

我曾经在Objective-C中使用以下类别:

 + (instancetype)instantiateFromStoryboard:(UIStoryboard *)storyboard { return [storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([self class])]; } 

但是我完全被Swift版困住了。 我希望有一种方法可以做到这一点。 我尝试了以下内容:

 extension UIViewController { class func instantiateFromStoryboard(storyboard: UIStoryboard) -> Self { return storyboard.instantiateViewControllerWithIdentifier(NSStringFromClass(Self)) } } 

返回Self而不是AnyObject允许types推断工作。 否则,我将不得不把这个方法的每一个返回,这是烦人的,但也许你有一个更好的解决scheme?

这给了我错误: Use of unresolved identifier 'Self' NSStringFromClass部分似乎是问题。

你怎么看?

  • 有什么办法从类函数返回Self

  • 你怎么得到这个工作,而不需要每次都投入返回值? (即保持-> Self作为返回值)

谢谢。

如何写一个扩展到UIStoryboard而不是UIViewController

 extension UIStoryboard { func instantiateVC<T: UIViewController>() -> T? { // get a class name and demangle for classes in Swift if let name = NSStringFromClass(T.self)?.componentsSeparatedByString(".").last { return instantiateViewControllerWithIdentifier(name) as? T } return nil } } 

即使采用这种方法,使用方面的成本也很低。

 let vc: UserDetailViewController? = aStoryboard.instantiateVC() 

我们正在把我们的目标C项目移植到迅速。 我们已经将项目分解成模块。 模块有自己的故事板。 我们已经通过避免显式的故事板名称将你的(甚至是我们的)问题的解决scheme扩展到一个更高的层次。

 // Add you modules here. Make sure rawValues refer to a stroyboard file name. enum StoryModule : String { case SomeModule case AnotherModule = "AnotherModulesStoryBoardName" // and so on... } extension UIStoryboard { class func instantiateController<T>(forModule module : StoryModule) -> T { let storyboard = UIStoryboard.init(name: module.rawValue, bundle: nil); let name = String(T).componentsSeparatedByString(".").last return storyboard.instantiateViewControllerWithIdentifier(name!) as! T } } // Some controller whose UI is in a stroyboard named "SomeModule.storyboard", // and whose storyboardID is the class name itself, ie "MyViewController" class MyViewController : UIViewController { // Controller Code } // Usage class AClass { // Here we must alwasy provide explicit type let viewController : MyViewController = UIStoryboard.instantiateController(forModule: StoryModule.SomeModule) } 

两件事情:

  • Objective-C中的类构造函数是Swift中的便捷初始值设定项。 使用convenience init而不是class func
  • NSStringFromClass(Self)NSStringFromClass(self.type)

感谢MartinR和他的回答 ,我知道答案:

 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 identifier = String(describing: self) let controller = storyboard.instantiateViewController(withIdentifier: identifier) as! T return controller } } 

用法:

 let viewController = MyController.instantiateFromStoryboard() let viewController2 = MyController.instantiateFromStoryboard("Main") 

或者,你可以这样做

  func instantiateViewControllerWithIdentifier<T>(_ identifier: T.Type) -> T { let identifier = String(describing: identifier) return instantiateViewController(withIdentifier: identifier) as! T } 

你可以添加这个扩展名:

 extension UIStoryboard{ func instantiateViewController<T:UIViewController>(type: T.Type) -> T? { var fullName: String = NSStringFromClass(T.self) if let range = fullName.range(of:".", options:.backwards, range:nil, locale: nil){ fullName = fullName.substring(from: range.upperBound) } return self.instantiateViewController(withIdentifier:fullName) as? T } } 

并可以像这样实例化视图控制器: –

 self.storyboard?.instantiateViewController(type: VC.self)! 

在UIViewController中使用协议来达到你的想法

let vc = YourViewController.instantiate(from: .StoryboardName)

你可以看到我的链接的使用:D

https://github.com/JavanC/StoryboardDesignable

下面是我在最近的Swift 4.0项目中做到的:

 protocol InstantiableFromStoryboard {} extension InstantiableFromStoryboard { static func fromStoryboard(name: String = "Main", bundle: Bundle? = nil) -> Self { let identifier = String(describing: self) guard let viewController = UIStoryboard(name: name, bundle: bundle).instantiateViewController(withIdentifier: identifier) as? Self else { fatalError("Cannot instantiate view controller of type " + identifier) } return viewController } } extension UIViewController: InstantiableFromStoryboard {} 

用法

 let viewController = ViewController.fromStoryboard()