SwiftUIのチュートリアルをやってみた その1(基本操作・リスト表示)
Patheeのメディアチームエンジニアの日向です。
先月のWWDC 2019ではiPadOSやSign in with Appleなどいろいろ発表がありましたが、一番衝撃的だったのはやはりSwiftUIですね。
シンプルなコードでUIを構築したり、リアルタイムでプレビューできたり、ドラッグアンドドロップで直感的な編集ができたり。今業務でやっているReactの考え方も活かせそう。
今回はそんなSwiftUIのチュートリアルを触ってみます。(Beta1なので変更されている場合があります)
SwiftUIのプレビュー機能などを使うにはmacOS Catalinaへのアップデートが必須なのですが、なぜかうまく動かなかったので、今回はMojaveにXcode11を入れて試しています。
各Tutorialではサンプルコードがダウンロードできるようになっています。開始時点のものと完了後の2つの状態のものが入っていて、チュートリアルの文章には載っていない部分や使用する画像が含まれているので配布されたものを使いましょう。
Creating and Combining Views
https://developer.apple.com/tutorials/swiftui/creating-and-combining-views
ここではランドマークの詳細画面のようなものを作りながら基本を学べます。
テキストの表示
struct ContentView: View { var body: some View { Text("Turtle Rock") .font(.title) .color(.green) } }
フォントサイズと色を変えたテキストを表示するコードはこんな感じになります。"Turtle Rock"というテキストをタイトル用のフォントで緑色にする、という直感的なコードです。
一番はじめに表示されるViewはSceneDelegate.swiftで設定されています。
window.rootViewController = UIHostingController(rootView: ContentView())
ContentView()の部分を変えれば最初に表示されるViewを変更できます。
var body: some View { VStack(alignment: .leading) { Text("Turtle Rock") HStack { Text("Joshua Tree National Park") Spacer() Text("California") } }.padding() }
HStackやVStackの中に入れてコンテンツを並べることでUIを構築できます。Spacerやpaddingでよしなに余白を設定してくれます。
丸い画像の作成
struct CircleImage: View { var body: some View { Image("turtlerock") .clipShape(Circle()) .overlay( Circle().stroke(Color.white, lineWidth: 4)) .shadow(radius: 10) } }
画像の場合はImageを設置します。clipShareで円状に画像を切り抜き、さらに輪郭線と影を追加します。
地図の表示
struct MapView: UIViewRepresentable { func makeUIView(context: Context) -> MKMapView { MKMapView(frame: .zero) } func updateUIView(_ view: MKMapView, context: Context) { let coordinate = CLLocationCoordinate2D( latitude: 34.011286, longitude: -116.166868) let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0) let region = MKCoordinateRegion(center: coordinate, span: span) view.setRegion(region, animated: true) } }
MKMapViewなどのSwiftUIに対応していないものを使うにはUIViewRepresentableに準拠したViewを作ります。makeUIViewの中でViewを作成してその設定をupdateUIViewに書きます。
最後に、テキスト・画像・マップをVStackの中に入れて、offsetで画像とマップが重なるようにすればランドマーク詳細画面が完成です。
Building Lists and Navigation
https://developer.apple.com/tutorials/swiftui/building-lists-and-navigation
ここではリストとナビゲーションについて学べます。
リストの表示
struct LandmarkRow: View { var landmark: Landmark var body: some View { HStack { landmark.image(forSize: 50) Text(landmark.name) } } } struct LandmarkList: View { var body: some View { List { LandmarkRow(landmark: landmarkData[0]) LandmarkRow(landmark: landmarkData[1]) } } }
リストは、UITableViewのときのようにdelegateやdataSourceを設定する必要がなく、表示したい要素をListで囲うだけです。 セルも今までのようにidentifierなどで指定する必要がありません。 LandmarkRowで使うlandmarkはLandmarkListでViewを作るときに渡します。
上記の場合は要素を一つずつ並べてましたが、配列を渡せるようにします。
List(landmarkData.identified(by: \.id)) { landmark in LandmarkRow(landmark: landmark) }
Listにただ配列を渡すだけではダメなようで、識別するための要素を指定する必要があるようです。ここではidentified(by: \.id)
とidを指定しています。
struct Landmark: Hashable, Codable, Identifiable { var id: Int var name: String } List(landmarkData) { landmark in LandmarkRow(landmark: landmark) }
LandmarkモデルをIdentifiableプロトコルに適合するように変更すると、idという要素を自動で使ってくれるようになるので省略できます。
ナビゲーションバーの表示と画面遷移
var body: some View { NavigationView { List(landmarkData) { landmark in NavigationButton(destination: LandmarkDetail(landmark: landmark)) { LandmarkRow(landmark: landmark) } } .navigationBarTitle(Text("Landmarks")) } }
ナビゲーションは、NavigationViewで囲うだけです。navigationBarTitleでタイトルを設定することができます。 List内の要素をNavigationButtonで囲い、destinationに遷移先のViewを指定すれば、画面遷移ができます。
最後に、遷移先のLandmarkDetailで表示する要素を、渡したlandmarkの内容を表示するように書き換えます。
他にも、プレビュー画面でViewの複数表示や複数のデバイスを同時に確認する方法などが載っていますが今回は試せませんでした。
おわりに
今回は基本的なViewの作り方とリスト表示をやってみました。
Swift 5.1で追加された新機能によって実現されている書き方が多くてSwiftのはずなのに違うものを触っているようで新鮮でした。
直感的なViewの構築や、今までのUITableViewのような面倒な記述をしなくても表示したいものをListで囲うだけでリスト表示できたりと、かなり簡単になっていて初めてiOSアプリを作ろうという人にも敷居が低くなったと思います。
次回は入力によって表示が変わる部分をやっていきます。