Swift 4中如何将Type作为变量进行传递?
Great question! The key here is to use generics in Swift, which lets you pass a view controller type as a parameter while maintaining type safety—something your Any approach can't handle reliably. Here's how to refactor your code cleanly:
Basic Generic Function
First, create a generic function that accepts a view controller type, handles the storyboard instantiation, and returns a strongly typed instance:
func instantiateViewController<T: UIViewController>( ofType type: T.Type, withIdentifier identifier: String, fromStoryboard storyboardName: String = "Main" ) -> T { let storyboard = UIStoryboard(name: storyboardName, bundle: nil) // Use guard to safely cast and provide a meaningful error if something goes wrong guard let viewController = storyboard.instantiateViewController(withIdentifier: identifier) as? T else { fatalError("Failed to instantiate view controller with identifier \(identifier) as \(type) from \(storyboardName) storyboard") } return viewController }
How to Use It
Call the function by passing your view controller's type (using .self) and its storyboard identifier:
let mapVC = instantiateViewController(ofType: MapViewController.self, withIdentifier: "MapViewController")
Simplified Version (If Identifier Matches Class Name)
If your storyboard identifiers match your view controller class names (a common best practice), you can eliminate the identifier parameter entirely by deriving it from the type:
func instantiateViewController<T: UIViewController>( ofType type: T.Type, fromStoryboard storyboardName: String = "Main" ) -> T { let identifier = String(describing: type) let storyboard = UIStoryboard(name: storyboardName, bundle: nil) guard let viewController = storyboard.instantiateViewController(withIdentifier: identifier) as? T else { fatalError("Failed to instantiate \(identifier) from \(storyboardName) storyboard") } return viewController }
Even Cleaner Call
Now you don't need to repeat the identifier:
let mapVC = instantiateViewController(ofType: MapViewController.self)
Swifty Extension Approach
For even better integration with Swift's style, extend UIStoryboard to add this functionality directly:
extension UIStoryboard { static func instantiateViewController<T: UIViewController>( ofType type: T.Type, fromStoryboard storyboardName: String = "Main" ) -> T { let identifier = String(describing: type) let storyboard = UIStoryboard(name: storyboardName, bundle: nil) guard let viewController = storyboard.instantiateViewController(withIdentifier: identifier) as? T else { fatalError("Failed to instantiate \(identifier) from \(storyboardName) storyboard") } return viewController } }
Usage with Extension
let mapVC = UIStoryboard.instantiateViewController(ofType: MapViewController.self)
Why This Works
- Generics: The
T: UIViewControllerconstraint ensures we only accept valid view controller types, andT.Typelets us pass the class itself as a parameter. - Type Safety: Unlike using
Any, this approach gives you compile-time checks and avoids unsafeas!casts without meaningful error handling. - Reusability: You can now use this function/extension for any view controller in your app, eliminating repetitive storyboard instantiation code.
内容的提问来源于stack exchange,提问作者Scott Robinson

