【Swift3.0】xibを使ったカスタムビューの作り方


はじめに

Swift初心者の自分用の備忘録的な記事となります。
一度ネットで調べながら作成し、疑問点が出てきたためteratailで質問し、改良したものです。

要件

・UIViewをカスタムしてダイアログのように表示したい
・デザインはxibを使いたい

最初に作ったもの

CustomDialog.swift
required init?(coder aDecoder: NSCoder) {
    commonInitialize()
    super.init(coder: aDecoder)
}

override init(frame: CGRect) {
    commonInitialize()
    super.init(frame: frame)
}

func setUp(frame: CGRect, name: String) {

    let customDialog: UIView = Bundle.main.loadNibNamed("CustomDialog", owner: self, options: nil)?.first as! UIView

    self.frame = frame
    customDialog.frame = frame
    dialogName = name

    addSubview(customDialog)
}

func commmonInitialize() {
    dialogName = ""
}
ViewController.swift
var customDialog: CustomDialog = CustomDialog()
override func viewDidLoad() {
    super.viewDidLoad()

    customDialog.setUp(frame: self.view.frame, name: "hoge")
}

func showDialog() {
    self.view.addSubview(customDialog)
}

途中まで作ってみて

疑問点

・イニシャライザ(requiredとdesignered)が2度呼ばれてしまう
・ViewControllerの上に「CustomDialogそのもの」と「xibから呼び出したCustomDialog」が乗っていることになってしまう

聞いてみた

【Swift3.0】カスタムビューの作成の際のベストプラクティスについて

とりあえずの結論

CustomDialog.swiftをxibのFile's ownerとすることでUIViewそのものではなく、CustomDialogを呼び出すクラスとして使う

改良したもの

CustomDialog.swift
import UIKit

protocol CustomDialogDelegate: class {
    func shouldDismissCustomDialog -> Void
}

class CustomDialog: NSObject {

    private static let className = "CustomDialog"

    var name: String

    weak var customDialogDelegate: CustomDialogDelegate?

    @IBOutlet var dialog: UIView!

    // CustomDialogのパーツ
    @IBOutlet var nameLabel: UILabel!

    // MARK: IBActions

    @IBAction func closeButtonTapped(sender: UIButton) {
        customDialogDelegate?.shouldDismissCustomDialog()
    }

    // MARK: Initialize

    override init() {
        name = ""
        super.init()
        Bundle.main.loadNibNamed(CustomDialog.className, owner: self, options: nil)
    }

    // MARK: Public Methods

    func show(presentedView: UIView?, requestName: String) {
        if let unwrappedPresentedView = presentedView {
            customDialog.frame = unwrappedPresentedView.frame
            name = requestName

            unwrappedPresentedView.addSubview(customDialog)
        } else {
            fatalError("View is Nil!")
        }
    }

    func hide() {
        customDialog.removeFromSuperview()
    }

}
ViewController.swift
import UIKit

class ViewController: UIViewController, CustomDialogDelegate {

    @IBOutlet var tableView: UITableView!

    var customDialog: CustomDialog = CustomDialog()

    override func viewDidLoad() {
        super.viewDidLoad()

        customDialog.customDialogDelegate = self
    }

    func showCustomDialog() {
        customDialog.show(presentedView: self.navigationController?.view, requestName: "hoge")
    }

    func shouldDismissCustomDialog() {
        customDialog.hide()
    }

}

改善案等ございましたらコメントいただけると嬉しいです。