在完成了地标的详情视图的配置后,我们需要提供一个方法让用户查看全部的地标列表,并查看每个位置的详情。
我们会创建可以显示关于地标所有信息的视图,并且动态生成可滚动的列表,用户可以点击查看地标的详情。为了优化UI,我们会使用Xcode的画布渲染多个不同设备尺寸的预览。 <下载>项目文件开始构建项目,并跟随下面的步骤。
01 了解样例数据
在第一个教程中,我们是将信息硬编码到视图中。这里我们学习如何将数据传入自定义视图显示。
通过下载启动项目开始并熟悉示例数据。
第一步
在项目导航器中,选择Models -> Landmark.swift。
Landmark.swift声明了Landmark结构体,它存储了所有的App需要展示的地标信息,并从landmarkData.json中导入一个地标数据的数组。
import SwiftUI
import CoreLocation
struct Landmark: Hashable, Codable {
var id: Int
var name: String
fileprivate var imageName: String
fileprivate var coordinates: Coordinates
var state: String
var park: String
var category: Category
var locationCoordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(
latitude: coordinates.latitude,
longitude: coordinates.longitude)
}
enum Category: String, CaseIterable, Codable, Hashable {
case featured = "Featured"
case lakes = "Lakes"
case rivers = "Rivers"
}
}
extension Landmark {
var image: Image {
ImageStore.shared.image(name: imageName)
}
}
struct Coordinates: Hashable, Codable {
var latitude: Double
var longitude: Double
}
第二步
在项目导航器中,选择Resources -> landmarkData.json。
我们会在本教程之后的部分及后续教程使用这些数据。
[
{
"name": "Turtle Rock",
"category": "Featured",
"city": "Twentynine Palms",
"state": "California",
"id": 1001,
"park": "Joshua Tree National Park",
"coordinates": {
"longitude": -116.166868,
"latitude": 34.011286
},
"imageName": "turtlerock"
},
{
"name": "Silver Salmon Creek",
"category": "Lakes",
"city": "Port Alsworth",
"state": "Alaska",
"id": 1002,
"park": "Lake Clark National Park and Preserve",
"coordinates": {
"longitude": -152.665167,
"latitude": 59.980167
},
"imageName": "silversalmoncreek"
},
...
]
第三步
注意<创建并组合视图>中的ContentView类型现在已经重命名为了LandmarkDetail。
我们会在这个教程及之后的教程中创建更多的视图。
import SwiftUI
struct LandmarkDetail: View {
var body: some View {
VStack {
MapView()
.edgesIgnoringSafeArea(.top)
.frame(height: 300)
CircleImage()
.offset(y: -130)
.padding(.bottom, -130)
VStack(alignment: .leading) {
Text("Turtle Rock")
.font(.title)
HStack(alignment: .top) {
Text("Joshua Tree National Park")
.font(.subheadline)
Spacer()
Text("California")
.font(.subheadline)
}
}
.padding()
Spacer()
}
}
}
struct LandmarkDetail_Previews: PreviewProvider {
static var previews: some View {
LandmarkDetail()
}
}
02 创建行视图
在这个教程要构建的第一个视图是显示每个地标详情的行视图。这个行视图将信息存储在一处属性中供地标显示,因此一个视图可以显示任意的地标。之后,我们会将多个行组合成地标列表。
第一步
创建一个新的SwiftUI视图,命名为LandmarkRow.swift。
第二步
如果预览器还不可见,通过在菜单栏选择Editor -> Canvas调出,然后点击Resume。
第三步
添加landmark作为LandmarkRow的存储属性。
在添加landmark属性之后,预览会停止工作,因为LandmarkRow类型在初始化的时候需要一个landmark实例
struct LandmarkRow: View {
var landmark: Landmark
var body: some View {
Text("Hello World")
要修复预览,我们需要修改一下预览提供器。
第四步
在LandmarkRow_Previews中的静态属性previews中,添加landmark参数到LandmarkRow初始化器中,并指定为`landmarkData数组的第一个元素。
预览会恢复工作,显示Hello World。
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
LandmarkRow(landmark: landmarkData[0])
}
}
修复这个问题后,我们开始构建行视图的布局。
第五步
将当前的文本视图嵌套到HStack中。
var body: some View {
HStack {
Text("Hello World")
}
}
}
第六步
用landmark属性的name字段修改文本视图。
var body: some View {
HStack {
Text(landmark.name)
}
}
第七步
在文本视图前面添加一个图片,后面添加一个Spacer控件完成行视图。
var body: some View {
HStack {
landmark.image
.resizable()
.frame(width: 50, height: 50)
Text(landmark.name)
Spacer()
03 定制行视图预览
Xcode的画布会在当前编辑器自动识别并显示任意遵从PreviewProvider协议的类型。一个预览提供器会返回一个或多个视图,还有配置尺寸和设备的选项。
我们可以在预览提供器中定制返回的内容,这样可以渲染对我们最有帮助的预览图。
第一步
在LandmarkRow_Previews中,将landmark参数设置为landmarkData数组中的第二个元素。
预览会立即变更从第一个landmark示例换成第二个。
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
LandmarkRow(landmark: landmarkData[1])
}
}
第二步
使用previewLayout(_:)修饰器设置行在列表中的预估尺寸。
static var previews: some View {
LandmarkRow(landmark: landmarkData[1])
.previewLayout(.fixed(width: 300, height: 70))
}
}
我们可以使用Group在预览提供器中返回多个预览视图。
第三步
将返回的行视图嵌套在Group中,并将第一行重新加回来。
Group是一个组合视图的容器。Xcode在画布中会将组的子视图渲染为单独的预览。
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
Group {
LandmarkRow(landmark: landmarkData[0])
.previewLayout(.fixed(width: 300, height: 70))
LandmarkRow(landmark: landmarkData[1])
.previewLayout(.fixed(width: 300, height: 70))
}
}
}
第四步
为了简化代码,我们将previewLayout(_:)调用移动到group声明的外面。
LandmarkRow(landmark: landmarkData[1])
}
.previewLayout(.fixed(width: 300, height: 70))
}
}
在预览提供器中编写的代码只会影响Xcode在画布中显示的内容。
04 创建地标列表
当使用SwiftUI的列表类型时,我们可以显示特定平台的列表视图。列表的元素可以是静态的比如目前创建的Stack中的子视图,也可以是动态生成的。甚至还可以将静态和动态生成的视图混合。
第一步
创建一个新的SwiftUI视图,命名为LandmarkList.swift。
第二步
用List替换默认的文本视图,并landmarkData数组头两个元素创建两个LandmarkRow实例作为子视图。
预览会显示两个地标显示在iOS样式的列表中。
struct LandmarkList: View {
var body: some View {
List {
LandmarkRow(landmark: landmarkData[0])
LandmarkRow(landmark: landmarkData[1])
}
}
}
本文暂时没有评论,来添加一个吧(●'◡'●)