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()