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
下面是我在最近的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()