我试图进行此扩展:
extension UIViewController
{
class func initialize(storyboardName: String, storyboardId: String) -> Self
{
let storyboad = UIStoryboard(name: storyboardName, bundle: nil)
let controller = storyboad.instantiateViewControllerWithIdentifier(storyboardId) as! Self
return controller
}
}
但我收到编译错误:
错误:无法转换类型' UIViewController'的返回表达式至 返回类型'自我'
有可能吗?我也希望将其设为init(storyboardName: String, storyboardId: String)
答案 0 :(得分:65)
与Using 'self' in class extension functions in Swift类似,您可以定义一个通用的辅助方法,从调用上下文中推断出self的类型:
extension UIViewController
{
class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self
{
return instantiateFromStoryboardHelper(storyboardName, storyboardId: storyboardId)
}
private class func instantiateFromStoryboardHelper<T>(storyboardName: String, storyboardId: String) -> T
{
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier(storyboardId) as! T
return controller
}
}
然后
let vc = MyViewController.instantiateFromStoryboard("name", storyboardId: "id")
编译,类型推断为MyViewController
。
更新 Swift 3:
extension UIViewController
{
class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self
{
return instantiateFromStoryboardHelper(storyboardName: storyboardName, storyboardId: storyboardId)
}
private class func instantiateFromStoryboardHelper<T>(storyboardName: String, storyboardId: String) -> T
{
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: storyboardId) as! T
return controller
}
}
另一种可能的解决方案,使用unsafeDowncast
:
extension UIViewController
{
class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self
{
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: storyboardId)
return unsafeDowncast(controller, to: self)
}
}
答案 1 :(得分:15)
Self
在编译时确定,而不是运行时。在您的代码中,UIViewController
完全等同于UIViewController
,而不是&#34;正好调用此类的子类。&#34;这将返回as
,调用者必须UIViewController
进入正确的子类。我认为这是你想要避免的(尽管它是#34;正常的Cocoa&#34;这样做的方式,所以只返回initialize
可能是最好的解决方案。)
注意:在任何情况下都不应将函数命名为NSObject
。这是as
的现有类函数,最多会造成混淆,最坏的情况是错误。
但是如果你想避免调用者的func instantiateViewController<VC: UIViewController>(storyboardName: String, storyboardId: String) -> VC {
let storyboad = UIStoryboard(name name: storyboardName, bundle: nil)
let controller = storyboad.instantiateViewControllerWithIdentifier(storyboardId) as! VC
return controller
}
,子类化通常不是在Swift中添加功能的工具。相反,您通常需要泛型和协议。在这种情况下,只需要泛型。
let tvc: UITableViewController = instantiateViewController(name: name, storyboardId: storyboardId)
这不是一种类方法。它只是一个功能。这里没有必要上课。
numberOfSectionsInTableView
答案 2 :(得分:0)
另一种方法是使用协议,该协议还允许您返回Self
。
protocol StoryboardGeneratable {
}
extension UIViewController: StoryboardGeneratable {
}
extension StoryboardGeneratable where Self: UIViewController
{
static func initialize(storyboardName: String, storyboardId: String) -> Self
{
let storyboad = UIStoryboard(name: storyboardName, bundle: nil)
let controller = storyboad.instantiateViewController(withIdentifier: storyboardId) as! Self
return controller
}
}
答案 3 :(得分:0)
更清洁的解决方案(至少在视觉上更整洁):
class func initialize(storyboardName: String, storyboardId: String) -> Self {
// The absurdity that's Swift's type system. If something is possible to do with two functions, why not let it be just one?
func loadFromImpl<T>() -> T {
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
return storyboard.instantiateViewController(withIdentifier: storyboardId).view as! T
}
return loadFromImpl()
}