编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

SwiftUI入门二:构建列表和导航(上)

wxchong 2024-07-26 23:06:17 开源技术 43 ℃ 0 评论

在完成了地标的详情视图的配置后,我们需要提供一个方法让用户查看全部的地标列表,并查看每个位置的详情。

我们会创建可以显示关于地标所有信息的视图,并且动态生成可滚动的列表,用户可以点击查看地标的详情。为了优化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])
        }
    }
}

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表