くま's Tech系Blog

基本的には技術で学んだことを書き留めようと思います。雑談もやるかもね!

UIのインスタンス化について

Xcodeには、Interface Builderでコードを書かずにデザインを構築することが多いと思います

Auto Layoutも比較的付けやすいので、使ったことがない人はいないくらい使われると思っています

今回は、Interface Builderソースコードを紐付ける方法をまとめます。(主に初期化などです)

ViewController

まずはViewControllerについてです。

ViewControllerの作り方についてですが、 New File... から新しいViewControllerのクラスを作ります。

次に、利用するStoryboardに新しいViewControllerを置き、 Storyboard IDに適当な文字列を指定します。

また、ソースコードでViewControllerを作った際にはClassに対応するクラスを指定しましょう。

インスタンス化する場合には下記のように行います。

let vc = storyboard?.instantiateViewController(identifier: "( Storyboard IDで指定した名前)") as? ViewController(該当のViewController)

今まで説明したものは1つのStroryBoardで管理する場合の方法ですが、1つのViewControllerを1つのStroryBoardで管理する場合には下記のようの行います。

let sb = UIStoryboard(name: "( Storyboardの名前)", bundle: nil)
let vc = storyboard?.instantiateViewController() as? ViewController(該当のViewController)

UIStoryboardのイニシャライザの引数について、 name には.storyboardファイルの拡張子を除いた部分の文字列を、 bundle には指定した.storyboardファイルとそれに関連するリソースが含まれているbundleを渡します。

上のコードのようにbundleにnilを渡すと、このメソッドはmain bundleを見にいきます。

XibでのカスタムView

ViewControllerをXibで定義するパターンもありますが、ここではカスタムViewをxibで作成するパターンについて説明します

カスタムViewを作成(初期化)する方法は大まかに2種類あるので、それぞれ説明します

①File’s Ownerで設定する場合

下記のようにカスタムViewをXibで作成する場合にFile’s Ownerでクラス指定する場合です。

生成方法は、イニシャライザでxibファイルを読み込み、自身に addSubview() します。

CustomViewクラスを継承したRoot viewの上に通常のUIViewを一枚挟んでから、Interfce Builder上で配置した部品が載ることになるのでパフォーマンスに影響を与える可能性があります(微々たるものではないので影響はほぼなさそう)

class LoginForm: UIView {
    @IBOutlet private weak var loginButton: UIButton!
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        loadNib()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        loadNib()
    }
    
    private func loadNib() {
        let view = UINib(nibName: "LoginForm", bundle: nil).instantiate(withOwner: self, options: nil).first as! UIView
        view.frame = self.bounds
        addSubview(view)
    }
}

②Custom Classで設定する場合

File’s Ownerとは別の方法でCustom Classで設定する方法もあります。

Custom Classで設定する場合には、File’s Ownerで設定する場合とは違い、無駄なUIViewを挟む必要はありません。 しかし、コードでinit(frame:)が呼ばれてしまうと、その時点でCustomViewがインスタンス化されてしまうため、xibと結びつけることができません。 コードから呼び出す場合には別途 static関数などを用意し、そちらを利用する必要があります。 仮にload() の処理をrequired init?(coder: NSCoder)で実行した場合には初期化されているのにinstantiateでサイド初期化しようとして無限ループに陥ります。

class LoginForm: UIView {
    @IBOutlet private weak var loginButton: UIButton!
    
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

    // パラメータを渡す場合にもここで引数を設定してViewController側で呼び出す
    static func load() {
        let view = UINib(nibName: "LoginForm", bundle: nil).instantiate(withOwner: nil, options: nil).first as! Self
      
        addSubview(view)
    }
}

File’s OwnerとCustom Classで設定する方法がありますが、File’s Ownerで設定する方がUIViewを扱う時と同じ方法で使えるのでおすすめです。

まとめ

ここまでUIのインスタンス化について記載しました。 どのパターンを使うのかは実装方法やインスタンス化のコードを踏まえ、自分のプロジェクトにあったものを採用するのがいいでしょう。

参照

qiita.com

developer.apple.com

medium.com

shiba1014.medium.com