LoginSignup
2
1

More than 5 years have passed since last update.

[iOS][Swift] XIBのカスタムビューを生成するときのextention

Last updated at Posted at 2018-08-20

XIBファイルでカスタムビューを作りコードから呼び出すときに、型を解決した状態でextensionのコードとして一つ用意するいい方法ないかと思って試したら、以下のようなヘ関数が楽でした。

注意
.xibファイルと.swiftファイル名が一致している前提

import UIKit

/// xibで作成するViewに準拠させる
protocol NibView {

    /// Viewのクラス
    associatedtype NibClass
}

extension NibView where NibClass: UIView {

    /// NibからViewを生成する
    /// xibファイルのCustom Classに同名のUIViewのクラスが指定されている前提
    ///
    /// - Returns: Viewのインスタンス
    static func generateViewFromNib() -> NibClass? {
        let nib = UINib(nibName: String(describing: self), bundle: nil)
        return nib.instantiate(withOwner: self, options: nil).first as? Self.NibClass
    }

}

UIViewのクラス側でプロトコルに準拠

class FooView: UIView {
  ...
}

// MARK: - NibView

extension FooView: NibView {
  typealias NibClass = FooView
}

使い方

if let fooView = FooView.generateViewFromNib() {
  ...
}

良くなかった例1

BundleのloadNibNamedはキャッシュされないためパフォーマンスが悪いので、UINibから生成すべきでした。

UIView+NibExtension.swift
import UIKit

protocol UIViewNibHelper {
}

extension UIViewNibHelper {

    /// NibからViewを生成する
    ///
    /// - Returns: 自身のView
    static func generateViewFromNib() -> Self? {
        let className = String(describing: self)
        return Bundle.main.loadNibNamed(className, owner: nil, options: nil)?.first as? Self
    }
}

extension UIView: UIViewNibHelper {}
// 戻り値はFooView?になる
if let fooView = FooView.generateViewFromNib() {
}

良くなかった例2

以下の方法ではxibのCusstom Classに設定するのではなく、File's OwnerのClassに同名のClassを指定した場合を前提とします。

extension UIView {

    /// Nibから生成したViewを追加する
    func addContentViewFromNib() {
        let nib = UINib(nibName: String(describing: type(of: self)), bundle: nil)
        let view = nib.instantiate(withOwner: self, options: nil).first as? UIView
        guard let contentView = view else {
            return
        }
        self.addSubview(contentView)
        contentView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            topAnchor.constraint(equalTo: contentView.topAnchor),
            leftAnchor.constraint(equalTo: contentView.leftAnchor),
            rightAnchor.constraint(equalTo: contentView.rightAnchor),
            bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
        ])
    }

}

一度initするしかなく冗長でした。

let fooView = FooView(frame: ...)
fooView.addContentViewFromNib()
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1