★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/11075270.html
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
设置基本地标详细信息视图后,您需要为用户提供查看完整地标列表以及查看每个位置详细信息的方法。
您将创建可显示有关任何地标的信息的视图,并动态生成滚动列表,用户可以点按该列表以查看地标的详细视图。要微调UI,您将使用Xcode的画布以不同的设备大小呈现多个预览。
下载项目文件以开始构建此项目,并按照以下步骤操作。
一、了解样本数据
在[SwiftUI教程]2、创建和组合视图中,您将信息硬编码到所有自定义视图中。在这里,您将学习将数据传递到自定义视图中以供显示。
首先下载入门项目并熟悉示例数据。
第1步
在Project导航器中,选择Models > Landmark.swift
.
1 import SwiftUI 2 import CoreLocation 3 4 struct Landmark: Hashable, Codable { 5 var id: Int 6 var name: String 7 fileprivate var imageName: String 8 fileprivate var coordinates: Coordinates 9 var state: String 10 var park: String 11 var category: Category 12 13 var locationCoordinate: CLLocationCoordinate2D { 14 CLLocationCoordinate2D( 15 latitude: coordinates.latitude, 16 longitude: coordinates.longitude) 17 } 18 19 func image(forSize size: Int) -> Image { 20 ImageStore.shared.image(name: imageName, size: size) 21 } 22 23 enum Category: String, CaseIterable, Codable, Hashable { 24 case featured = "Featured" 25 case lakes = "Lakes" 26 case rivers = "Rivers" 27 } 28 } 29 30 struct Coordinates: Hashable, Codable { 31 var latitude: Double 32 var longitude: Double 33 }
第2步
在Project导航器中,选择Resources> landmarkData.json
.
1 [ 2 { 3 "name": "Turtle Rock", 4 "category": "Featured", 5 "city": "Twentynine Palms", 6 "state": "California", 7 "id": 1001, 8 "park": "Joshua Tree National Park", 9 "coordinates": { 10 "longitude": -116.166868, 11 "latitude": 34.011286 12 }, 13 "imageName": "turtlerock" 14 }, 15 { 16 "name": "Silver Salmon Creek", 17 "category": "Lakes", 18 "city": "Port Alsworth", 19 "state": "Alaska", 20 "id": 1002, 21 "park": "Lake Clark National Park and Preserve", 22 "coordinates": { 23 "longitude": -152.665167, 24 "latitude": 59.980167 25 }, 26 "imageName": "silversalmoncreek" 27 }, 28 ... 29 ]
第3步
请注意,现在可以命名创建和组合视图中的类型。ContentView
LandmarkDetail
1 import SwiftUI 2 3 struct LandmarkDetail: View { 4 var body: some View { 5 VStack { 6 MapView() 7 .frame(height: 300) 8 9 CircleImage() 10 .offset(y: -130) 11 .padding(.bottom, -130) 12 13 VStack(alignment: .leading) { 14 Text("Turtle Rock") 15 .font(.title) 16 17 HStack(alignment: .top) { 18 Text("Joshua Tree National Park") 19 .font(.subheadline) 20 Spacer() 21 Text("California") 22 .font(.subheadline) 23 } 24 } 25 .padding() 26 27 Spacer() 28 } 29 } 30 } 31 32 struct LandmarkDetail_Preview: PreviewProvider { 33 static var previews: some View { 34 LandmarkDetail() 35 } 36 }
二、创建行视图
第1步
创建一个名为的新SwiftUI视图。LandmarkRow.swift
第2步
如果预览不可见,请通过选择Editor > Editor and Canvas来显示画布,然后单击“Get Started”
第3步
添加landmark
为存储属性。LandmarkRow
三、自定义行预览
Xcode的画布自动识别并显示当前编辑器中符合协议的任何类型。预览提供程序返回一个或多个视图,其中包含用于配置大小和设备的选项。PreviewProvider
您可以从预览提供程序自定义返回的内容,以准确呈现对您最有帮助的预览。
第1步
在将参数更新为数组中的第二个元素。LandmarkRow_Previews
landmark
landmarkData
1 import SwiftUI 2 3 struct LandmarkRow: View { 4 var landmark: Landmark 5 6 var body: some View { 7 HStack { 8 landmark.image(forSize: 50) 9 Text(landmark.name) 10 } 11 } 12 } 13 14 struct LandmarkRow_Previews: PreviewProvider { 15 static var previews: some View { 16 LandmarkRow(landmark: landmarkData[1]) 17 } 18 }
第2步
使用修饰符设置近似列表中行的大小。previewLayout(_:)
1 import SwiftUI 2 3 struct LandmarkRow: View { 4 var landmark: Landmark 5 6 var body: some View { 7 HStack { 8 landmark.image(forSize: 50) 9 Text(landmark.name) 10 } 11 } 12 } 13 14 struct LandmarkRow_Previews: PreviewProvider { 15 static var previews: some View { 16 LandmarkRow(landmark: landmarkData[1]) 17 .previewLayout(.fixed( 300, height: 70)) 18 } 19 }
第3步
将返回的行换行Group
,然后再将第一行添加回来。
四、创建地标列表
List
类型时,可以显示特定于平台的视图列表。列表的元素可以是静态的,就像您目前创建的堆栈的子视图一样,或者是动态生成的。您甚至可以混合静态和动态生成的视图。第1步
创建一个名为的新SwiftUI视图。LandmarkList.swift
第2步
Text
用a 替换默认视图List
,并提供带有前两个标记的实例作为列表的子标记。LandmarkRow
五、使列表动态化
您可以直接从集合中生成行,而不是单独指定列表的元素。
您可以通过传递数据集合以及为集合中的每个元素提供视图的闭包来创建显示集合元素的列表。该列表使用提供的闭包将集合中的每个元素转换为子视图。
第1步
删除两个静态地标行,然后传递给初始化程序。landmarkData
List
六、在列表和详细信息之间设置导航
列表正确呈现,但您无法点击单个地标以查看该地标的详细信息页面。
您可以通过将导航功能嵌入到列表中,然后将每行嵌套在一个列表中来设置转换到目标视图,从而将导航功能添加到列表中。NavigationView
NavigationButton
第1步
将动态生成的地标列表嵌入到。NavigationView
1 import SwiftUI 2 3 struct LandmarkList: View { 4 var body: some View { 5 NavigationView { 6 List(landmarkData) { landmark in 7 LandmarkRow(landmark: landmark) 8 } 9 } 10 } 11 } 12 13 struct LandmarkList_Previews: PreviewProvider { 14 static var previews: some View { 15 LandmarkList() 16 } 17 }
第2步
调用修改器方法以在显示列表时设置导航栏的标题。navigationBarTitle(_:)
1 import SwiftUI 2 3 struct LandmarkList: View { 4 var body: some View { 5 NavigationView { 6 List(landmarkData) { landmark in 7 LandmarkRow(landmark: landmark) 8 } 9 .navigationBarTitle(Text("Landmarks")) 10 } 11 } 12 } 13 14 struct LandmarkList_Previews: PreviewProvider { 15 static var previews: some View { 16 LandmarkList() 17 } 18 }
第3步
在列表的闭包内,将返回的行包装在a中,将视图指定为目标。NavigationButton
LandmarkDetail
1 import SwiftUI 2 3 struct LandmarkList: View { 4 var body: some View { 5 NavigationView { 6 List(landmarkData) { landmark in 7 NavigationButton(destination: LandmarkDetail()) { 8 LandmarkRow(landmark: landmark) 9 } 10 } 11 .navigationBarTitle(Text("Landmarks")) 12 } 13 } 14 } 15 16 struct LandmarkList_Previews: PreviewProvider { 17 static var previews: some View { 18 LandmarkList() 19 } 20 }
第4步
您可以通过切换到实时模式直接在预览中尝试导航。单击“实时预览”按钮,然后点击地标以访问详细信息页面。
七、将数据传递到子视图
该视图仍然使用硬编码的细节,以显示其标志性建筑。就像它所包含的类型和视图需要使用属性作为其数据的源。LandmarkDetail
LandmarkRow
LandmarkDetail
landmark
从子视图开始,您将转换,然后显示传入的数据,而不是硬编码每一行。CircleImage
MapView
LandmarkDetail
第1步
在添加存储的属性。CircleImage.swift
image
CircleImage
1 import SwiftUI 2 3 struct CircleImage: View { 4 var image: Image 5 6 var body: some View { 7 image 8 .clipShape(Circle()) 9 .overlay(Circle().stroke(Color.white, lineWidth: 4)) 10 .shadow(radius: 10) 11 } 12 } 13 14 struct CircleImage_Preview: PreviewProvider { 15 static var previews: some View { 16 CircleImage() 17 } 18 }
第2步
更新预览提供程序以传递Turtle Rock的图像。
1 import SwiftUI 2 3 struct CircleImage: View { 4 var image: Image 5 6 var body: some View { 7 image 8 .clipShape(Circle()) 9 .overlay(Circle().stroke(Color.white, lineWidth: 4)) 10 .shadow(radius: 10) 11 } 12 } 13 14 struct CircleImage_Preview: PreviewProvider { 15 static var previews: some View { 16 CircleImage(image: Image("turtlerock")) 17 } 18 }
第3步
在,添加属性并转换代码以使用该属性而不是硬编码纬度和经度。MapView.swift
coordinate
MapView
1 import SwiftUI 2 import MapKit 3 4 struct MapView: UIViewRepresentable { 5 var coordinate: CLLocationCoordinate2D 6 7 func makeUIView(context: Context) -> MKMapView { 8 MKMapView(frame: .zero) 9 } 10 11 func updateUIView(_ view: MKMapView, context: Context) { 12 13 let span = MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02) 14 let region = MKCoordinateRegion(center: coordinate, span: span) 15 view.setRegion(region, animated: true) 16 } 17 } 18 19 struct MapView_Preview: PreviewProvider { 20 static var previews: some View { 21 MapView() 22 } 23 }
第4步
更新预览提供程序以传递数据数组中第一个地标的坐标。
1 import SwiftUI 2 import MapKit 3 4 struct MapView: UIViewRepresentable { 5 var coordinate: CLLocationCoordinate2D 6 7 func makeUIView(context: Context) -> MKMapView { 8 MKMapView(frame: .zero) 9 } 10 11 func updateUIView(_ view: MKMapView, context: Context) { 12 let span = MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02) 13 let region = MKCoordinateRegion(center: coordinate, span: span) 14 view.setRegion(region, animated: true) 15 } 16 } 17 18 struct MapView_Preview: PreviewProvider { 19 static var previews: some View { 20 MapView(coordinate: landmarkData[0].locationCoordinate) 21 } 22 }
第5步
在,向该类型添加属性。LandmarkDetail.swift
Landmark
LandmarkDetail
1 import SwiftUI 2 3 struct LandmarkDetail: View { 4 var landmark: Landmark 5 6 var body: some View { 7 VStack { 8 MapView() 9 .frame(height: 300) 10 11 CircleImage() 12 .offset(y: -130) 13 .padding(.bottom, -130) 14 15 VStack(alignment: .leading) { 16 Text("Turtle Rock") 17 .font(.title) 18 19 HStack(alignment: .top) { 20 Text("Joshua Tree National Park") 21 .font(.subheadline) 22 Spacer() 23 Text("California") 24 .font(.subheadline) 25 } 26 } 27 .padding() 28 29 Spacer() 30 } 31 } 32 } 33 34 struct LandmarkDetail_Preview: PreviewProvider { 35 static var previews: some View { 36 LandmarkDetail() 37 } 38 }
第6步
更新预览以使用第一个地标。landmarkData
1 import SwiftUI 2 3 struct LandmarkDetail: View { 4 var landmark: Landmark 5 6 var body: some View { 7 VStack { 8 MapView() 9 .frame(height: 300) 10 11 CircleImage() 12 .offset(y: -130) 13 .padding(.bottom, -130) 14 15 VStack(alignment: .leading) { 16 Text("Turtle Rock") 17 .font(.title) 18 19 HStack(alignment: .top) { 20 Text("Joshua Tree National Park") 21 .font(.subheadline) 22 Spacer() 23 Text("California") 24 .font(.subheadline) 25 } 26 } 27 .padding() 28 29 Spacer() 30 } 31 } 32 } 33 34 struct LandmarkDetail_Preview: PreviewProvider { 35 static var previews: some View { 36 LandmarkDetail(landmark: landmarkData[0]) 37 } 38 }
第7步
将所需数据传递给您的自定义类型。
1 import SwiftUI 2 3 struct LandmarkDetail: View { 4 var landmark: Landmark 5 6 var body: some View { 7 VStack { 8 MapView(coordinate: landmark.locationCoordinate) 9 .frame(height: 300) 10 11 CircleImage(image: landmark.image(forSize: 250)) 12 .offset(y: -130) 13 .padding(.bottom, -130) 14 15 VStack(alignment: .leading) { 16 Text(landmark.name) 17 .font(.title) 18 19 HStack(alignment: .top) { 20 Text(landmark.park) 21 .font(.subheadline) 22 Spacer() 23 Text(landmark.state) 24 .font(.subheadline) 25 } 26 } 27 .padding() 28 29 Spacer() 30 } 31 } 32 } 33 34 struct LandmarkDetail_Preview: PreviewProvider { 35 static var previews: some View { 36 LandmarkDetail(landmark: landmarkData[0]) 37 } 38 }
第8步
最后,在显示详细视图时,调用修改器为导航栏指定标题。navigationBarTitle(_:displayMode:)
1 import SwiftUI 2 3 struct LandmarkDetail: View { 4 var landmark: Landmark 5 6 var body: some View { 7 VStack { 8 MapView(coordinate: landmark.locationCoordinate) 9 .frame(height: 300) 10 11 CircleImage(image: landmark.image(forSize: 250)) 12 .offset(y: -130) 13 .padding(.bottom, -130) 14 15 VStack(alignment: .leading) { 16 Text(landmark.name) 17 .font(.title) 18 19 HStack(alignment: .top) { 20 Text(landmark.park) 21 .font(.subheadline) 22 Spacer() 23 Text(landmark.state) 24 .font(.subheadline) 25 } 26 } 27 .padding() 28 29 Spacer() 30 } 31 .navigationBarTitle(Text(landmark.name), displayMode: .inline) 32 } 33 } 34 35 struct LandmarkDetail_Preview: PreviewProvider { 36 static var previews: some View { 37 LandmarkDetail(landmark: landmarkData[0]) 38 } 39 }
第9步
在,切换应用程序的根视图。SceneDelegate.swift
LandmarkList
八、动态生成预览
接下来,您将向预览提供程序添加代码,以便以不同的设备大小呈现列表视图的预览。默认情况下,预览会以活动方案中设备的大小进行渲染。您可以通过调用修改器方法来更改预览设备。LandmarkList_Previews
previewDevice(_:)
第1步
首先,将当前列表预览更改为以iPhone SE的大小呈现。