36

我正在尝试进行此扩展:

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”类型的返回表达式转换为“Self”类型

可能吗?我也想让它成为init(storyboardName: String, storyboardId: String)

4

4 回答 4

67

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)
    }
}
于 2015-10-18T16:37:49.457 回答
15

Self是在编译时确定的,而不是在运行时确定的。在您的代码中,Self完全等同于UIViewController,而不是“碰巧调用它的子类”。这将返回UIViewController,调用者必须将as其放入正确的子类中。我认为这就是您要避免的(尽管这是“正常的 Cocoa”方式,所以仅仅返回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)
于 2015-10-18T16:25:27.150 回答
1

更清洁的解决方案(至少在视觉上更整洁):

斯威夫特 5.1

class func initialize(storyboardName: String, storyboardId: String) -> Self {
    return UIStoryboard(name: storyboardName, bundle: nil)
        .instantiateViewController(withIdentifier: storyboardId).view as! Self
}
于 2019-06-08T11:19:48.227 回答
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
    }
}
于 2018-08-12T16:24:33.577 回答