これは Swift Advent Calendar 2019 24日目のエントリーです。
昨日23日は Moto0124 さんの CGAffineTransformを知る でした。
はてなエンジニアAdvent Calendar 2019 での Deletable Table with TextField on SwiftUI に続いて、今回も SwiftUI ネタです。
そもそもSwift Playgroundsって?
ここでいう Swift Playgrounds
はXcode に付属のPlaygroundではなく、iPad アプリとしての Swift Playgrounds を指しています。
以降、 Swift Playgrounds
は 「iPad アプリのSwift Playground」、 Xcode Playground
はXcode に付属のPlaygroundと使い分けます。
ドキュメント: https://developer.apple.com/documentation/swift_playgrounds
SwiftUIをSwift Playgroundsで扱う
SwiftUIのアナウンス直後から、Swift Playground 3.1以降でSwiftUIが実行できることは話題 になっていました。
Swift Playgrounds 3.1のVersion History
us: • Build with the SwiftUI framework in new playgrounds you create
jp: • 新しく作成するプレイグラウンドでは、SwiftUIフレームワーク を使用してビルドできます
SwiftUIで表示してみる
すでに広く知られている話だとは思うけれど、まずは単純に表示してみましょう。
import SwiftUI
import PlaygroundSupport
struct ContentView : View {
var body : some View {
VStack {
Text("Hello" )
.font(.largeTitle)
.foregroundColor(.primary)
Text("world" )
.font(.title)
.foregroundColor(.secondary)
}
}
}
let host = UIHostingController(rootView: ContentView ())
PlaygroundPage.current.liveView = host
Swift Playgroundsで実行
Xcode Playground用ファイルとしてExportする
便利な使い方として、Xcode Playground用の拡張子 .playground
として作成することもできます。
ファイル作成時に Starting Points > Xcode Playground
を選択して、始める必要があります。
Starting PointsからXcode Playgroundを選択
ここから作成したファイルをiCloud やAirdrop でmacOS に送り、Xcode で開くと、実行できます。
Xcode Playgroundで実行
再利用できるViewの作り方
簡単なレイアウトのViewを作ってみるにはこれだけでも十分便利ですが、複雑なレイアウトを作ったり、再利用しやすいViewを作ったりには不向きに思えます。
また、Playground上での作成と言っても、最終的にはアプリ開発 にViewを組み込みたいでしょう。
Swift Playgroundsでも、複数のSwiftファイルを扱うことができるので、各パーツのViewを別々のSwiftファイルに分割してみます。
参考: https://developer.apple.com/documentation/swift_playgrounds/structuring_content_for_swift_playgrounds/using_modules_to_share_code_in_a_playground_book
ファイルを追加
左上のファイルアイコンから、ファイルの追加ができます。
今回は3ファイル作成しました。
左上のアイコンから、ファイルを追加
LargeTitleView.swift
import SwiftUI
struct LargeTitleView : View {
var body : some View {
Text("Hello" )
.font(.largeTitle)
.foregroundColor(.primary)
}
}
TitleView.swift
import SwiftUI
struct TitleView : View {
var body : some View {
Text("World" )
.font(.title)
.foregroundColor(.secondary)
}
}
Preview
のみ public
修飾子でアクセスできるようにしておきます。
import SwiftUI
public struct Preview : View {
public init () {}
public var body : some View {
VStack {
LargeTitleView()
TitleView()
}
}
}
main
Swift Playgroundのmainファイルでは、Preview を UIHostingController
で描画するようにします。
Swift Playgroundsでのmainと、その他のファイルとでは、別Module扱いになるため、 public
修飾子ないものにはアクセスできません。
import
はいらない。
import SwiftUI
import PlaygroundSupport
let preview = Preview()
let host = UIHostingController(rootView: preview )
PlaygroundPage.current.liveView = host
実行結果
これ以降はPreview と各Viewを書き換えるだけで、実行ができるようになりました。
こうすることで、各View毎のstructをそれぞれファイルとして分割できたり、 public
修飾子を書き忘れたり、 init()
実装し忘れで実行できなかったりがなくなります。
iCloud 経由で、Xcode のアプリプロ ジェクトに追加もできるので、非常に便利ですね。
もちろん、copy&pasteするだけでも、動きます。
Xcode Playgroundで実行する時には注意点があって、Xcode Playground上でのファイルリンクは特殊で、struct定義から直接ファイルジャンプはできません。
上部のペインから、辿ってください。
Jump to Definitionでは、ファイルジャンプできない
まとめ
iPad Proを買ってからというもの、Swift Playgroundsを使いたいと思うことが幾度とありましたが、やっと活用方法を見つけた気がします。
当たり前ですが、iCloud でファイルを同期する場合は、macOS で共有ディレクト リを開いていたりすると、最新の変更が更新されず、消えてしますこともあるので、ご注意を!
また、まだまだ不安定なのか、たまに .playground/Contents.swft
が破損して、開けなくなることもありました。 *1
おまけ
このエントリー用の検証していて、以下のコードを書けることに気づいた。
なにか使い道ないかな。
import SwiftUI
public struct Preview <Body: View>: View {
private let _body : Body
public init (body: Body ) {
self ._body = body
}
public var body : some View {
_body
}
}
let preview = Preview(body: ContentView ())