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 に答える
0

もう 1 つの方法は、プロトコルを使用することです。これにより、 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 に答える