iOSのnibで作ったViewにCustom Classを対応させて描画する

(2020-06-05)

nibとUIViewを継承したCustomViewクラスを作成し、nibにLabelを配置して@IBOutletと繋げた。

import Foundation
import UIKit
import viewframework

class CustomView: UIView {
    @IBOutlet weak var labelview: UILabel?
}

このCustomViewをnibのViewと対応させるのに次の2通りの方法がある。

ルートのViewのCustom Classとして設定する

ルートのViewのCustom ClassをCustomViewにした

nibのルートのViewのCustom ClassをCustomViewにすると、UINib.instantiate()でCustomViewのrequired init?(coder: NSCoder)が呼ばれインスタンスが作られるので、 それをaddSubViewすることで描画できる。

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let customView = UINib(nibName: "CustomView", bundle: Bundle(for: type(of: self))).instantiate(withOwner: nil, options: nil).first as! CustomView
        customView.labelview?.text = "OK"
        view.addSubview(customView)
    }
}

File’s OwnerのCustom Classとして設定する

File’s OwnerのCustom ClassをCustomViewにした

nibのFile’s OwnerのCustom ClassをCustomViewにし、 CustomView内でwithOwner: selfでnibをロードすると@IBOutletにインスタンスが詰まるので、ルートのViewも繋げておいてそれをaddSubViewすればCustomView以下にnibのヒエラルキーそのまま描画される。 Custom ClassがUIViewである必要は必ずしもなくロードしたviewを親のviewに直接addSubViewしても良い。

import Foundation
import UIKit

class CustomView: UIView {
    @IBOutlet var view: UIView!
    @IBOutlet weak var labelview: UILabel!
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        load()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func load() {
        print("before loading nib: \(view)") // => nil
        UINib(nibName: "CustomView", bundle: Bundle(for: type(of: self))).instantiate(withOwner: self, options: nil)
        // = Bundle(for: type(of: self)).loadNibNamed("CustomView", owner: self, options: nil)
        print("after loading nib: \(view!.superview)") // => nil
        addSubview(view)
        print("after adding Subview: \(view?.superview?.classForCoder)") // => Optional(sampleapp.CustomView)
    }
}
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let customView = CustomView(frame: view.frame)
        customView.labelview?.text = "OK"
        view.addSubview(customView)
    }
}

iOS13では動くがiOS12でクラッシュする問題

クラスにObjC用の別名を設定したとき元々のクラス名をCustom Classに入れるとiOS13では動くが、iOS12では ロード時の Unknown class _TtC8xibtest210CustomView in Interface Builder file や、 @IBOutlet にアクセスしたときの EXC_BAD_ACCESS でクラッシュする問題が発生した。

@objc(ExCustomView) class CustomView: UIView {
}

ObjC用の方のクラス名を指定したらいずれの環境でも動いた。補完の上でもObjCの方のクラス名が出てくる。

ObjC用の方のクラス名を指定