LoginSignup
7

More than 3 years have passed since last update.

SwiftUIでNibでデザインしたCellやViewを使う

Last updated at Posted at 2019-06-08

SwiftUIがUIKitと併用できると聞いて、コード記述のUIクラスだけでなく、Nibもできそうだったので調べて記事を書いてみました!
サンプルコード: https://github.com/kntkymt/SwiftUIWithNib

※本記事のスクリーンショットはMacOS Mojave 10.14.5, Xcode10.2上で撮影されたものであり、 beta版のスクリーンショットではありません。
SwiftUIの使用にはこちら記事を参考にさせていただきました。
macOSの複数バージョンを同一パーティションにインストール

前提

このようなUITableViewCell, Nib(.xib)があったとします。

import UIKit

final class HogeTableCell: UITableViewCell {

    // MARK: - Outlet

    @IBOutlet private weak var image: UIImageView! 
    @IBOutlet private weak var categoryLabel: UILabel!
    @IBOutlet private weak var titleLabel: UILabel!
    @IBOutlet private weak var descriptionLabel: UILabel!
}

スクリーンショット 2019-06-09 0.36.02.png

本題

Nibを元にHogeTableCellクラスのインスタンスを生成し、返すビルダーメソッドを追加します。これをインスタンスを生成する側でinitの代わりに用います。

class func build() -> UIView {
    return UINib("HogeTableViewCell", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UIView
}

SwiftUI上で使用する分には問題ないと考え、UIViewにダウンキャストしています。

Nib使用時に特別なのは以上です。

あとは、SwiftUItutorial Creating and Combining ViewsのSession5にもあるように、UIViewRepresentableでラップしてあげれば、SwiftUI上で通常通り利用できるようになります。.xibでいじった部分もちゃんと反映されてます。

import SwiftUI

struct SwiftUIHogeTableView: UIViewRepresentable {

    func updateUIView(_ uiView: UIView, context: Context) {
        // サイズの調整など
    }

    func makeUIView(context: Context) -> UIView {
        // インスタンスの生成
        TopImageCollectionCell.build()
    }
}

#if DEBUG
struct SwiftUIHogeTableView_Previews : PreviewProvider {
    static var previews: some View {
        // ここでxibのViewのサイズとプレビューのサイズを同じにする
        SwiftUIHogeTableView()
            .frame(width: 355, height: 420)
            .previewLayout(.sizeThatFits)
    }
}
#endif

import SwiftUI

struct ContentView : View {
    var body: some View {
        SwiftUIHogeTableView()
            .frame(width: 355, height: 420) 
    }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

beta版のスクリーンショットはNDAに引っかかるかが怪しいラインらしいので、念の為上げない事にします。すいません!
色々適当ですが、githubにサンプルコードを上げているので、よければそちらをご自分で確認してみてください!
ご指摘があればいただきたいです!

SwiftUI、簡単にiOSっぽいデザインが出来ていいですね。どうしてもSwiftUIでデザインが難しいところはNibを使ってできそうでよかったです。

おまけ

Nibからクラスのインスタンスを生成する機構を抽象化してprotocol化するのがオススメです。
※クラス名と.xib名が同一であることを前提に抽象化を行っているので、違う名前にすると使えません。

import UIKit

protocol NibInstantiatable: class {
    associatedtype Instance
    static var NibName: String { get }
    static var nib: UINib { get }
    static func instantiateView() -> Instance
}

extension NibInstantiatable where Self: UIView {

    static var NibName: String {
        return String(describing: self)
    }

    static var nib: UINib {
        return UINib(nibName: NibName, bundle: nil)
    }

    static func instantiateView() -> Self {
        return nib.instantiate(withOwner: nil, options: nil)[0] as! Self
    }
}

クラス側では以下の様に記述できます。

import UIKit

final class HogeTableCell: UITableViewCell, NibInstantiatable {

    // MARK: - Outlet

    @IBOutlet private weak var image: UIImageView! 
    @IBOutlet private weak var categoryLabel: UILabel!
    @IBOutlet private weak var titleLabel: UILabel!
    @IBOutlet private weak var descriptionLabel: UILabel!

    // MARK: - Builder

    class func build() -> UIView {
        let view = instantiateView()
        return view
    }
}

build() の引数にModelを入れるようにすればOutletへのデータの注入もできそうですね。

class func build(model: HogeModel) -> UIView {
        let view = instantiateView()
        view.image = model.image
        view.categoryLabel.text = model.category
        view.titleLabel.text = model.title
        view.descriptionLabel = model.description

        return view
}

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
7