Как и в Используя «self» в функциях расширения класса в Swift , вы можете определить общий вспомогательный метод, который определяет тип «я» из вызывающего контекста:
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)
}
}
Self
определяется во время компиляции, а не во время выполнения. В вашем коде Self
в точности эквивалентен UIViewController
, а не «подкласс, который вызывает это». Это вернет UIViewController
, и вызывающий должен будет as
перевести его в правый подкласс. Я предполагаю, что это то, чего вы пытались избежать (хотя это обычный способ какао), поэтому просто возвращение UIViewController
, вероятно, является лучшим решением).
Примечание: вы должны не следует называть функцию initialize
в любом случае. Это существующая функция класса NSObject
и в лучшем случае приведет к путанице. Ошибки в худшем случае.
Но если вы хотите избежать as
вызывающего абонента, подклассом обычно не является инструмент для добавьте функциональность в Swift. Вместо этого вам обычно нужны дженерики и протоколы. В этом случае вам нужны только дженерики.
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
}
Это не метод класса. Это просто функция. Здесь нет необходимости в классе.
let tvc: UITableViewController = instantiateViewController(name: name, storyboardId: storyboardId)
Другой способ - использовать протокол, который также позволяет вам возвращать 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
}
}