web-dev-qa-db-ja.com

Swiftでインスタンスタイプを返します

私はこの拡張機能を作成しようとしています:

_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)として作成したい

35
ChikabuZ

Swiftのクラス拡張関数で「self」を使用 と同様に、呼び出しコンテキストから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)
    }
}
65
Martin R

Selfは、ランタイムではなくコンパイル時に決定されます。コードでは、SelfUIViewControllerとまったく同じであり、「たまたまこれを呼び出しているサブクラス」ではありません。これは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)
15
Rob Napier

よりクリーンなソリューション(少なくとも視覚的にきれい):

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()
}
0
mojuba

もう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
    }
}
0
ukim