UIKitでも使えるリアルタイムプレビュー
SwiftUIではXcode11から追加されたXcode Previewsという機能を使って、コードの変更結果をリアルタイムでプレビューすることができます。
実はこの機能は、UIKit(UIView/UIViewController)でも利用することができます。
この記事では、SwiftUIまだ触ってないよって人向けに、UIKitをリアルタイムプレビューする方法を解説します。
この記事で紹介しているコードはこちらにおいてあります。
リアルタイムプレビューのイメージ
ViewController上のTableViewの表示をデータを変更しながらチェック
カスタムしたTableViewCellの表示をいろんなパターンで同時にチェック
前提
Xcode Previewsを使うためにはデプロイメントターゲットをiOS13以降する必要があります。
iOS13より前をターゲットにしているプロジェクトに取り入れる方法については、Xcode Previewsを用いたUIKitベースのプロジェクトの開発効率化が参考になります。
環境
- Xcode 11.6
- Swift 5.2.4
- デプロイメントターゲット 13.0
UIViewをプレビューする
ここでは例として、カスタムTableViewCellをプレビューできるようにしていきます。
データを受け取ってテキストと画像をセットするconfigure
メソッドを追加したものです。
final class MyTableViewCell: UITableViewCell {
static let reuseIdentifier = "\(MyTableViewCell.self)"
func configure(with prop: CellProp) {
textLabel?.text = prop.title
imageView?.image = UIImage(systemName: prop.imageType.rawValue)
}
}
SwiftUIのViewでUIViewをラップする
Xcode PreviewsはSwiftUIのView用に作られているので、MyTableViewCell
をSwiftUIのViewでラップする必要があります。
UIViewをラップするためのプロトコルとして、UIViewRepresentable
というものが用意されています。
import SwiftUI
// SwiftUIのViewはstructで定義する
struct MyTableViewCellWrapper: UIViewRepresentable {
...
}
UIViewRepresentable
に準拠させるには、makeUIView/updateUIView
の二つのメソッドを定義します。
また、プレビュー時に使用するデータをセットするために、プロパティvar prop: CellProp
を追加しています。
struct MyTableViewCellWrapper: UIViewRepresentable {
var prop: CellProp
// 初期化メソッド
func makeUIView(context: Context) -> MyTableViewCell {
// 単純に初期化して返す
MyTableViewCell(style: .default, reuseIdentifier: MyTableViewCell.reuseIdentifier)
}
// SwiftUI側から更新がかかったときに呼ばれるメソッド
func updateUIView(_ cell: MyTableViewCell, context: Context) {
// 更新用のメソッドはupdateUIView内で呼ぶ
cell.configure(with: prop)
}
}
プレビュー用のstructを追加する
最後のステップとして、Xcode Previewsがプレビューを表示するときの設定を読み取れるように、PreviewProvider
プロトコルに準拠したstructを定義します。
このプロトコルに準拠させるために、static var previews: some View
を追加します。
View
はSwiftUIにおける基本的なViewを表すstructです。some
はSwiftUIのViewであれば、なんでもいいよという意味です。
struct MyTableViewCell_Previews: PreviewProvider {
static var previews: some View {
Group {
MyTableViewCellWrapper(prop: ("Pencil", .pencil))
MyTableViewCellWrapper(prop: ("Scribble", .scribble))
MyTableViewCellWrapper(prop: ("Scissors", .scissors))
}
.previewLayout(.fixed(width: 320, height: 50))
}
}
Group
は主にプレビューのために使われるSwiftUIのViewで、クロージャーに複数のViewを並べることができます。
previewLayout
メソッドでサイズを指定してやることで、次の画像の様にプレビュー時のサイズを調整することができます1。
Option+Command+リターンを入力(またはGUIからCanvasをクリック)すると、プレビュー用の画面が開きます。編集中のファイルに、PreviewProvider
プロトコルに準拠したstructがある場合は、そのプレビューが表示されます2。
MyTableViewCellWrapperに渡すprop
の値やプレビューサイズを編集すると、リアルタイムでプレビューが更新されます。
また、プレビューの右に表示されている再生ボタンを押すと、"Live Preview"がはじまり、タップイベントやスクロールなどのユーザーインタラクションをプレビュー画面上でテストすることができます3。
UIViewControllerをプレビューする
UIViewControllerもUIViewとほとんど同じ手順でプレビュー可能です。
まずは、UIViewControllerRepresentable
に準拠したstructでUIViewControllerをラップします。
struct ViewController_Wrapper: UIViewControllerRepresentable {
// プレビュー用のデータを返すスタブ
var presenter: PresenterStub
// 初期化メソッド
func makeUIViewController(context: Context) -> ViewController {
let vc = ViewController()
vc.presenter = presenter
presenter.view = vc
return vc
}
// SwiftUI側から更新がかかったときに呼ばれるメソッド
func updateUIViewController(_ vc: ViewController, context: Context) {
// 更新用のメソッド
presenter.didPushFetchButton()
}
}
今回はプレビュー用のデータを渡しやすいように、ViewControllerが表示するデータはPresenterInput
というプロトコルで管理するようにしています。
protocol PresenterInput {
var data: [CellProp] { get }
func didPushFetchButton()
}
final class ViewController: UIViewController {
var presenter: PresenterInput!
...
}
PreviewProvider
に準拠したstruct内でViewを初期化する際に、プレビュー用のデータを返すスタブを注入します。
このように実装することで、プレビューしたいデータごとにスタブを用意して、複数のデータについての表示を同時に確認することも可能です。
struct ViewController_Wrapper: UIViewControllerRepresentable {
// プレビュー用のデータを返すスタブ
var presenter: PresenterStub
...
}
struct ViewController_Previews: PreviewProvider {
static var previews: some View {
let presenter = PresenterStub()
return ViewController_Wrapper(presenter: presenter)
}
}
次の画像は、PesenterStub
内のコードを変更したときに、リアルタイムで表示が変わっている様子です。
おわりに
Viewのレイアウトをコードでやっちゃう派の人は、Xcode Previewsの利点を最大限に活用できるのではないでしょうか。
ストーリーボード派の方も、来るSwiftUI時代に備えて、この機会に一度チャレンジしてみてはいかがでしょうか!
本記事で紹介したコードはこちらに置いてあります。参考にしてみてください。
参考
Xcode Previewsを用いたUIKitベースのプロジェクトの開発効率化
Introducing SwiftUI