8492


UIViewController のサブクラスに関する質問です。

Objective-C では [[FooViewController alloc] init] で FooViewController.xib を読み込んで
ViewController を初期化できました。  
Swift でも同じことがしたいのですが、FooViewController() でオブジェクトを作っても FooViewController.xib
が読み込まれないようです。

最初に以下のようにやってみたのですが、super.init を呼ぶ前に self が使えないというエラーが出て無理でした。

[code]

    convinience override init() {
        let fullName = NSStringFromClass(self.dynamicType)
        let className = split(fullName){$0 == "."}.last
    
        super.init(nibName: className, bundle: nil)
    }
    
[/code]

以下のように init でloadNibNamedをすれば一応できるのですが、viewDidLoad を自分で呼び出すのは邪悪なので無理でした。

[code]

    override init() {
        super.init()
    
        let fullName = NSStringFromClass(self.dynamicType)
        let className = split(fullName){$0 == "."}.last
    
        NSBundle.mainBundle().loadNibNamed(className, owner: self, options: nil)
        viewDidLoad()
    }
    
[/code]

他には以下のように、xibを元に ViewController を作る関数も考えてみましたが、グローバル関数にするのもクラス関数にするのも微妙です。

[code]

    func viewController<T: UIViewController>(type: T.Type) -> T {
    
        let fullName = NSStringFromClass(T)
        let className = split(fullName){$0 == "."}.last
        let controller = T(nibName: className, bundle: nil)
    
        return controller as T
    }
    
    // viewController(FooViewController) でオブジェクト作成
    
[/code]

何かもっといい方法があれば教えて下さい。


CC BY-SA 3.0
2015-03-26T16:13:55.513
8448
2015-04-04T00:20:27.770
8148
question
swift
クラス名と同名のxibファイルでUIViewControllerを初期化したい


すでに、決定的な回答がついて、質問がクローズされましたが、最初の質問者さんの方針で行くなら、こんなこともできますよという内容を蛇足ながら説明したいと思います。

[code]

    import UIKit
    
    class SecondViewController: UIViewController {
    
        override init() {
            let className = SecondViewController.getMyName()
            super.init(nibName: className, bundle: nil)
        }
    
        required init(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        // クラス名を取得するクラスメソッド
        class func getMyName() {
            let className = split(self.description){$0 == "."}.last
            return className
        }
    }
    
[/code]

ポイント1：クラス名を取得するために`self.dynamicType`を使うには、SecondViewControllerのインスタンスができてなければいけない。そのためにまず`super.init()`しないといけない。ならば、別の手段でクラス名を取得しよう。  
ポイント2：クラスメソッドは、インスタンスを生成しなくても、実行できる。クラスメソッドで、クラス名を取得しよう。  
ポイント3：クラスメソッド内では、`self`はインスタンスでなく、クラスそのものを指す。`self.description`でクラス名を取得できる。


CC BY-SA 3.0
2015-03-26T18:15:33.033
8450
2015-03-28T11:01:46.450
2015-03-28T11:01:46.450
7362
7362
8448
answer


質問の読み間違えの場合申し訳ございません。

クラス名とnibの名前が同じ場合、何もしなくとも、つまり、init()をオーバーライドしなくとも

[code]

    let viewController = FooViewController();
    
[/code]

だけでnibが読み込まれるはずです。

[アップルのUIViewControllerのリファレンスのnibName](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/occ/instp/UIViewController/nibName)


CC BY-SA 3.0
2015-03-27T03:05:22.413
8463
2015-03-27T04:40:22.943
2015-03-27T04:40:22.943
2741
2741
8448
answer


> Swift でも同じことがしたいのですが、FooViewController() でオブジェクトを作っても FooViewController.xib
> が読み込まれないようです。

クラス名と同名の xib ファイルが読み込まれるというルールは、言語によって変わることはないと思います。ただしそのクラス名とは、Objective-C
ランタイムから見たクラス名です。

もうご存知なのではないかと思うのですが、`NSStringFromClass(self.dynamicType)` の実行結果がそれに当たります。

* * *

どうしても Swift のクラス名と xib ファイル名を一致させたいという場合は、その、Objective-C
ランタイムから見たクラス名の方を変更する、という方法があります。

具体的には、クラス定義の前に `@objc(#Objective-C ランタイム向けのクラス名#)` を追加します：

[code]

    @objc(FooViewController)
    class FooViewController: UIViewController {
        …
    
[/code]

この場合、このクラスは名前空間に属さない状態になるので注意してください。

参照：[Using Swift with Cocoa and Objective-C: Interacting with Objective-C
APIs](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-
CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_35)

* * *

どうしてもサブクラス化で解決されたい場合は、せめて `loadView` をオーバーライドし、その中で xib ファイルを読み込まれることをお勧めします。

`loadView` ではどこかで `self.view` にビューをアサインする必要があるのですが、xib ファイル内で正しく設定されていれば、xib
ファイルを読み込んだ時点でアサインされるはずです。

これは、`UIViewController` のビューをコードだけで作る場合など、`UIViewController` の nib
読み込み機能を使わない場合の標準的な方法なので、`viewDidLoad` なども正常に呼ばれるはずです。

逆に、イニシャライザで xib ファイルを読み込むという方法は、yamamoto さんがすでに経験されている通り、UIViewController が本来
xib ファイルを読み込んだときに行う処理が実行されない（`viewDidLoad`
だけとは限りません）ことになり、選択肢としてはちょっと考えにくいかな…と思います。


CC BY-SA 3.0
2015-03-27T12:57:27.773
8492
2015-03-27T12:57:27.773
2299
8448
answer


xibだと単なるViewとして配置されるので、LayoutGuideに対応できず不便ではないですか？自分はViewControllerごとにxibではなくstoryboardを作るようにしています。storyboard上でViewControllerを配置し、それをViewControllerクラスにひもづけています。

ロードする時は、以下のようにできると思います。  
※ StoryboardのViewControllerのIs Initial View Controllerにチェックしておく必要があります。

[code]

    // ViewControllerのSubclassか、extensionでクラスメソッドを追加
    class func loadViewContorller() -> UIViewController {
        let fullName = NSStringFromClass(self.dynamicType)
        let className = split(fullName){$0 == "."}.last    
        UIStoryboard *sb = 
            [UIStoryboard storyboardWithName:className bundle:nil];
        return [sb instantiateInitialViewController];
    }
    
[/code]

こちらも参考になれば。  
<http://blogios.stack3.net/archives/1619>


CC BY-SA 3.0
2015-04-04T00:20:27.770
8736
2015-04-04T00:20:27.770
8666
8448
answer