はじめに
前回からの続きでSwiftUIのチュートリアル入門になります。
もと記事サイト
Step0
前回作成したナビゲーションの詳細画面が固定になっているので、今回はList
でタップされた対象のLandmarkの名前、マップ位置、画像名を詳細画面に渡して個別に表示できるようにします。
Step1
CircleImage.swift
にimage
プロパティを追加します。LandmarkDetail.swift
で表示するための画像をList
から受け取って保存します。
SwiftUIではよく使うパターンになるようなので覚えておきましょう!
また、body
内でImage("turtlerock")
を固定で生成しているのでここも書き換えます。
import SwiftUI
struct CircleImage: View {
var image: Image
var body: some View {
image
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 4))
.shadow(radius: 7)
}
}
struct CircleImage_Previews: PreviewProvider {
static var previews: some View {
CircleImage()
}
}
Step2
CircleImage
にimage
プロパティを設定したので、イニシャライザでimage
を設定する必要があります。そのためCircleImage_Previews
でエラーが表示されるので、CircleImage
のイニシャライザを設定します。ただし、LandmarkDetail.swift
の方でもイニシャライザが必要になり、エラーが表示されますが、ここでは一旦無視します。
import SwiftUI
struct CircleImage: View {
var image: Image
var body: some View {
image
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 4))
.shadow(radius: 7)
}
}
struct CircleImage_Previews: PreviewProvider {
static var previews: some View {
CircleImage(image: Image("turtlerock"))
}
}
Step3
MapView.swift
もStep2同様に動的に表示できるように変更していきます。
import SwiftUI
import MapKit
struct MapView: View {
var coordinate: CLLocationCoordinate2D
@State private var region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.166_868),
span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
)
var body: some View {
Map(coordinateRegion: $region)
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView(coordinate: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.166_868))
}
}
Step4
setRegion
メソッドを追加します。Map
が表示されるタイミングでこのメソッドが呼ばれ、イニシャライザで渡された座標位置(coordinate)を使ってMKCoordinateRegion
を生成し、プロパティに代入しています。
import SwiftUI
import MapKit
struct MapView: View {
var coordinate: CLLocationCoordinate2D
@State private var region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.166_868),
span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
)
var body: some View {
Map(coordinateRegion: $region)
}
private func setRegion(_ coordinate: CLLocationCoordinate2D) {
region = MKCoordinateRegion(
center: coordinate, span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
)
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView(coordinate: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.166_868))
}
}
Step5
Map
にonAppear
を追加し、Map
が表示されるタイミングでsetRegion
メソッドが呼ばれるようにしています。onAppear
はUIKitのviewWillAppear
にあたるようです。
struct MapView: View {
var coordinate: CLLocationCoordinate2D
@State private var region = MKCoordinateRegion()
var body: some View {
Map(coordinateRegion: $region)
.onAppear {
setRegion(coordinate)
}
}
private func setRegion(_ coordinate: CLLocationCoordinate2D) {
region = MKCoordinateRegion(
center: coordinate, span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
)
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView(coordinate: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.166_868))
}
}
Step6
LandmarkDetail.swift
にlandmark
プロパティを追加し、コンテンツを動的に表示できるようにします。 MapView
とCircleImage
のイニシャライザにパラメータがないのでエラーが表示されます。
取り合えず気にしないでおきましょう。
import SwiftUI
struct LandmarkDetail: View {
var landmark: Landmark
var body: some View {
VStack {
MapView()
.ignoresSafeArea(edges: .top)
.frame(height: 300)
CircleImage()
.offset(y: -60)
.padding(.bottom, -60)
VStack(alignment: .leading) {
Text("Turtle Rock")
.font(.title)
HStack {
Text("Joshua Tree National Park")
.font(.subheadline)
Spacer()
Text("California")
.font(.subheadline)
}
.font(.subheadline)
.foregroundColor(.secondary)
Divider()
Text("About Turtle Rock")
.font(.title2)
Text("Descriptive text goes here.")
}
.padding()
Spacer()
}
}
}
struct LandmarkDetail_Previews: PreviewProvider {
static var previews: some View {
LandmarkDetail(landmark: landmarks[0])
}
}
Step7
LandmarkList.swift
のLandmarkDetail
生成時に対象のlandmark
を渡すように変更します。
import SwiftUI
struct LandmarkList: View {
var body: some View {
NavigationView {
List(landmarks) { landmark in
NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
LandmarkRow(landmark: landmark)
}
}
.navigationTitle("Landmarks")
}
}
}
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
LandmarkList()
}
}
Step8
先程エラーが表示されていたLandmarkDetail.swift
に変更を加えていきます。
固定表示になっていた、Map
、CircleImage
、Text
を変更しています。
import SwiftUI
struct LandmarkDetail: View {
var landmark: Landmark
var body: some View {
VStack {
MapView(coordinate: landmark.locationCoordinate)
.ignoresSafeArea(edges: .top)
.frame(height: 300)
CircleImage(image: landmark.image)
.offset(y: -60)
.padding(.bottom, -60)
VStack(alignment: .leading) {
Text(landmark.name)
.font(.title)
HStack {
Text(landmark.park)
.font(.subheadline)
Spacer()
Text(landmark.state)
.font(.subheadline)
}
.font(.subheadline)
.foregroundColor(.secondary)
Divider()
Text("About \(landmark.name)")
.font(.title2)
Text(landmark.description)
}
.padding()
Spacer()
}
}
}
struct LandmarkDetail_Previews: PreviewProvider {
static var previews: some View {
LandmarkDetail(landmark: landmarks[0])
}
}
Step9
body
直下のVStack
をScrollView
に変更します。これで画面よりもコンテンツが大きければスクロールできます。またSpacer
は不要なので削除します。
import SwiftUI
struct LandmarkDetail: View {
var landmark: Landmark
var body: some View {
ScrollView {
MapView(coordinate: landmark.locationCoordinate)
.ignoresSafeArea(edges: .top)
.frame(height: 300)
CircleImage(image: landmark.image)
.offset(y: -60)
.padding(.bottom, -60)
VStack(alignment: .leading) {
Text(landmark.name)
.font(.title)
HStack {
Text(landmark.park)
.font(.subheadline)
Spacer()
Text(landmark.state)
.font(.subheadline)
}
.font(.subheadline)
.foregroundColor(.secondary)
Divider()
Text("About \(landmark.name)")
.font(.title2)
Text(landmark.description)
}
.padding()
}
}
}
struct LandmarkDetail_Previews: PreviewProvider {
static var previews: some View {
LandmarkDetail(landmark: landmarks[0])
}
}
Step10
最後に詳細画面にナビゲーションタイトルが表示されるようにします。
import SwiftUI
struct LandmarkDetail: View {
var landmark: Landmark
var body: some View {
ScrollView {
MapView(coordinate: landmark.locationCoordinate)
.ignoresSafeArea(edges: .top)
.frame(height: 300)
CircleImage(image: landmark.image)
.offset(y: -60)
.padding(.bottom, -60)
VStack(alignment: .leading) {
Text(landmark.name)
.font(.title)
HStack {
Text(landmark.park)
.font(.subheadline)
Spacer()
Text(landmark.state)
.font(.subheadline)
}
.font(.subheadline)
.foregroundColor(.secondary)
Divider()
Text("About \(landmark.name)")
.font(.title2)
Text(landmark.description)
}
.padding()
}
.navigationTitle(landmark.name)
.navigationBarTitleDisplayMode(.inline)
}
}
struct LandmarkDetail_Previews: PreviewProvider {
static var previews: some View {
LandmarkDetail(landmark: landmarks[0])
}
}
Step11
LandmarkList.swift
を選択してLive previewを実行します。
まとめ
これでリストで動的にデータを表示する方法がわかりましたね。
今回勉強になった箇所は、動的に表示する際には***_Previews
のイニシャライザに何かしら固定データを設定しておく必要があるところですかね。
コメント