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!
}
本題
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
}