一、概述

在文章01-SwiftUI|了解SwiftUI中,咱们现已了解了何为 SwiftUI,咱们接下来逐步了解怎么经过SwiftUI构建咱们的App页面。

  • 首要,咱们经过一篇文章了解下怎么创立和组合View
  • 紧接着,咱们经过本篇文章了解怎么构建列表(相似TableView)与导航(相似NavigationController)

二、构建列表与导航

完结了根底的地标概况 view 后,咱们需求为用户供给检查完整地标列表,以及检查每个地标概况的办法。

在本文中,咱们将会创立可显现任何地标信息的 view ,并动态生成翻滚列表,用户能够点按该列表以检查地标的详细视图。另外,咱们还将运用 Xcodecanvas 来显现不同设备的巨细,以此来微调 UI。

下载项目文件并按照以下步骤操作。

  • 估计完结时刻:35 分钟
  • 初始项目文件:下载

1. 了解样本数据

在 上一个教程 中,咱们把数据硬编码到了一切自界说 view 中。在本文中,咱们来学习怎么将数据传递到自界说 view 中并显现。

下载初始项目并了解一下样本数据。

  • 1.1 在 Project navigator 中,挑选 Models > Landmark.swift

Landmark.swift 声明晰一个 Landmark 结构体,用来存储 app 需求显现的一切地标数据,并从 landmarkData.json 导入一组地标数据。

Landmark.swift

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)
    }
    func image(forSize size: Int) -> Image {
        ImageStore.shared.image(name: imageName, size: size)
    }
    enum Category: String, CaseIterable, Codable, Hashable {
        case featured = "Featured"
        case lakes = "Lakes"
        case rivers = "Rivers"
    }
}
struct Coordinates: Hashable, Codable {
    var latitude: Double
    var longitude: Double
} 
  • 1.2 在 Project navigator 中,挑选 Resources > landmarkData.json

咱们会在本教程的剩余部分以及随后的一切内容中运用此样本数据。

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"
    },
    ...
] 
  • 1.3 需求留意的是, 上一个教程 中的 ContentView 类型现在更名为 LandmarkDetail

接下来咱们还会创立多个 view 类型。

LandmarkDetail.swift

import SwiftUI
struct LandmarkDetail: View {
    var body: some View {
        VStack {
            MapView()
                .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_Preview: PreviewProvider {
    static var previews: some View {
        LandmarkDetail()
    }
} 

2. 创立 Row View

咱们在本文中构建的第一个 view 是用于显现每个地标概况的 rowrow 将地标数据存储在 landmark 特点中,这样一个 row 就能够显现任何地标。稍后咱们会把多个 row 组合成一个地标列表。

2.1 创立一个新的 SwiftUI view,命名为 LandmarkRow.swift

2.2 假如预览没有显现,请挑选 Editor > Editor and Canvas , 然后单击 Get Started

2.3 给 LandmarkRow 增加一个存储特点 landmark

当你增加 landmark 特点时,预览会停止工作,因为 LandmarkRow 类型在初始化时需求一个 landmark 实例。

LandmarkRow.swift

import SwiftUI
struct LandmarkRow: View {
    var landmark: Landmark
    var body: some View {
        Text("Hello World")
    }
}
struct LandmarkRow_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkRow()
    }
} 

为了康复预览,咱们需求修正 PreviewProvider

2.4 在 LandmarkRow_Previews 的静态特点 previews 中,给 LandmarkRow 的初始化办法增加 landmark 参数,并将 landmarkData 数组的第一个元素赋值landmark 参数。

这时预览就会显现 Hello World 的文字。

LandmarkRow.swift

import SwiftUI
struct LandmarkRow: View {
    var landmark: Landmark
    var body: some View {
        Text("Hello World")
    }
}
struct LandmarkRow_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkRow(landmark: landmarkData[0])
    }
} 

康复预览后,咱们就能够构建 row 的布局了。

2.5 把现有的 text view 嵌套到一个 HStack 中。

LandmarkRow.swift

import SwiftUI
struct LandmarkRow: View {
    var landmark: Landmark
    var body: some View {
        HStack {
            Text("Hello World")
        }
    }
}
struct LandmarkRow_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkRow(landmark: landmarkData[0])
    }
} 

2.6 将 text view 的内容修正成 landmark.name

LandmarkRow.swift

import SwiftUI
struct LandmarkRow: View {
    var landmark: Landmark
    var body: some View {
        HStack {
            Text(landmark.name)
        }
    }
}
struct LandmarkRow_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkRow(landmark: landmarkData[0])
    }
} 

2.7 在 text view 前增加一个图片来完结 row

LandmarkRow.swift

import SwiftUI
struct LandmarkRow: View {
    var landmark: Landmark
    var body: some View {
        HStack {
            landmark.image(forSize: 50)
            Text(landmark.name)
        }
    }
}
struct LandmarkRow_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkRow(landmark: landmarkData[0])
    }
} 

3. 自界说 Row 的预览

Xcode的 canvas 会自动识别并显现当时编辑器中符合 PreviewProvider 协议的任何类型。 preview provider 回来一个或多个 view ,其中包括了用来配置巨细和设备的选项。

经过自界说 preview provider 的回来值,咱们能够让预览来显现需求的内容。

  • 3.1 在 LandmarkRow_Previews 中,把 landmark 的参数改成 landmarkData 数组的第二个元素。

预览会立即从第一个元素切换到第二个元素的显现。

LandmarkRow.swift

import SwiftUI
struct LandmarkRow: View {
    var landmark: Landmark
    var body: some View {
        HStack {
            landmark.image(forSize: 50)
            Text(landmark.name)
        }
    }
}
struct LandmarkRow_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkRow(landmark: landmarkData[1])
    }
} 

  • 3.2 用 previewLayout(_:) 办法设置 row 在列表中的大约巨细。

LandmarkRow.swift

import SwiftUI
struct LandmarkRow: View {
    var landmark: Landmark
    var body: some View {
        HStack {
            landmark.image(forSize: 50)
            Text(landmark.name)
        }
    }
}
struct LandmarkRow_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkRow(landmark: landmarkData[1])
            .previewLayout(.fixed(width: 300, height: 70))
    }
} 

咱们能够在 preview provider 中运用 Group 来回来多个预览。

  • 3.3 把回来的 row 包装到一个 Group 中,而且把第一个 row 增加回来。

Group 是一个组合 view 的容器。 Xcode 会在 canvas 中把 Group 的子 view 作为分隔的预览渲染出来。

LandmarkRow.swift

import SwiftUI
struct LandmarkRow: View {
    var landmark: Landmark
    var body: some View {
        HStack {
            landmark.image(forSize: 50)
            Text(landmark.name)
        }
    }
}
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 声明的外面来精简代码。

一个 view 的子项会承继 view 的上下文设置,比方这儿的预览设置。

LandmarkRow.swift

import SwiftUI
struct LandmarkRow: View {
    var landmark: Landmark
    var body: some View {
        HStack {
            landmark.image(forSize: 50)
            Text(landmark.name)
        }
    }
}
struct LandmarkRow_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            LandmarkRow(landmark: landmarkData[0])
            LandmarkRow(landmark: landmarkData[1])
        }
        .previewLayout(.fixed(width: 300, height: 70))
    }
} 

preview provider 中编写的代码只会改动 Xcode 在 canvas 中的显现。因为 #if DEBUG 指令的存在,当 app 发布时,编译器会删去这些代码。

4. 创立地标列表

运用 SwiftUIList 类型能够显现渠道特有的列表 view 。列表的元素能够是静态的,就像咱们创立的 stacks 的子 view 一样;也能够是动态生成的。乃至能够把静态和动态生成的 view 混合在一起。

  • 4.1 创立一个新的 SwiftUI view,命名为 LandmarkList.swift

  • 4.2 把默许的 Text view 换成 List ,然后传入两个包括头两个地标数据的 LandmarkRow 方针,作为 List 的子项。

预览会以合适 iOS 款式的列表来显现这两个地标。

LandmarkList.swift

import SwiftUI
struct LandmarkList: View {
    var body: some View {
        List {
            LandmarkRow(landmark: landmarkData[0])
            LandmarkRow(landmark: landmarkData[1])
        }
    }
}
struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
    }
} 

5. 动态化列表

相比于给 list 指定单个元素,咱们还能够直接从调集中生成 row

经过传递一个数据调集和一个给每个元素供给 view 的闭包来让 list 显现调集的元素。 list 经过传递的闭包来把每个调集中的元素转换成子 view 。

  • 5.1 移除现有的两个静态地标 row ,然后给 List 的初始化办法传递 landmarkData

list 运用 identifiable 的数据,咱们能够运用以下两个办法之一来让数据变成 identifiable :调用 identified(by:) 办法,运用 key path 特点来唯一标识每个元素,或许让数据类型遵从 Identifiable 协议。

LandmarkList.swift

import SwiftUI
struct LandmarkList: View {
    var body: some View {
        List(landmarkData.identified(by: .id)) { landmark in
        }
    }
}
struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
    }
} 
  • 5.2 在闭包中回来 LandmarkRow ,咱们就完结了自动生成内容的 list

这会给 landmarkData 数组中的每一个元素创立一个 LandmarkRow

LandmarkList.swift

import SwiftUI
struct LandmarkList: View {
    var body: some View {
        List(landmarkData.identified(by: .id)) { landmark in
            LandmarkRow(landmark: landmark)
        }
    }
}
struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
    }
} 

接下来,咱们经过给 Landmark 类型增加遵从 Identifiable 的声明来简化代码。

  • 5.3 切换到 Landmark.swift ,声明遵从 Identifiable 协议。

Landmark 类型声明晰 Identifiable 协议需求的 id 特点后,咱们就完结了对 Landmark 的修正。

Landmark.swift

import SwiftUI
import CoreLocation
struct Landmark: Hashable, Codable, Identifiable {
    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)
    }
    func image(forSize size: Int) -> Image {
        ImageStore.shared.image(name: imageName, size: size)
    }
    enum Category: String, CaseIterable, Codable, Hashable {
        case featured = "Featured"
        case lakes = "Lakes"
        case rivers = "Rivers"
    }
}
struct Coordinates: Hashable, Codable {
    var latitude: Double
    var longitude: Double
} 

  • 5.4 切回 LandmarkList,删去 identified(by:) 的调用。

从现在开端,咱们能够直接运用 Landmark 元素的调集。

LandmarkList.swift

import SwiftUI
struct LandmarkList: View {
    var body: some View {
        List(landmarkData) { landmark in
            LandmarkRow(landmark: landmark)
        }
    }
}
struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
    }
} 

6. 在列表和概况之间设置导航

虽然列表现已能显现了,可是咱们还不能经过点击单个地标来检查地标概况页面。

list 嵌入一个 NavigationView 中,并把每个 row 嵌套在一个 NavigationButton 中来设置到方针 view 的转场,这样 list 就具有了导航功用。

  • 6.1 把自动创立地标的 list 嵌入到一个 NavigationView 中。

LandmarkList.swift

import SwiftUI
struct LandmarkList: View {
    var body: some View {
        NavigationView {
            List(landmarkData) { landmark in
                LandmarkRow(landmark: landmark)
            }
        }
    }
}
struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
    }
} 

调用 navigationBarTitle(_:) 办法来设置 list 显现时导航栏的标题。

LandmarkList.swift

import SwiftUI
struct LandmarkList: View {
    var body: some View {
        NavigationView {
            List(landmarkData) { landmark in
                LandmarkRow(landmark: landmark)
            }
            .navigationBarTitle(Text("Landmarks"))
        }
    }
}
struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
    }
} 

  • 6.3 在 list 的闭包中,把回来的 row 包装在一个 NavigationButton 中,并把 LandmarkDetail view 作为方针。

LandmarkList.swift

import SwiftUI
struct LandmarkList: View {
    var body: some View {
        NavigationView {
            List(landmarkData) { landmark in
                NavigationButton(destination: LandmarkDetail()) {
                    LandmarkRow(landmark: landmark)
                }
            }
            .navigationBarTitle(Text("Landmarks"))
        }
    }
}
struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
    }
} 

  • 6.4 切换到实时形式后能够直接在预览中尝试导航功用。单击 Live Preview 按钮,然后点击地标来访问概况页面。

7. 给子 View 传递数据

LandmarkDetail 现在仍然运用硬编码的数据来显现地标。像 LandmarkRow 一样,LandmarkDetail 类型和它组合的其他 view 都需求一个 landmark 特点作为它们的数据源。

在开端子 view 的内容时,咱们会把 CircleImageMapViewLandmarkDetail 的显现从硬编码改为传入的数据。

  • 7.1 在 CircleImage.swif 中,增加存储特点 image

这是运用 SwiftUI 构建 view 时的常见形式。咱们的自界说 view 一般会为特定视图包装和封装一些 modifiers

CircleImage.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: 10)
    }
}
struct CircleImage_Preview: PreviewProvider {
    static var previews: some View {
        CircleImage()
    }
} 
  • 7.2 更新 preview provider ,传递一个 Turtle Rock 的图片。

CircleImage.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: 10)
    }
}
struct CircleImage_Preview: PreviewProvider {
    static var previews: some View {
        CircleImage(image: Image("turtlerock"))
    }
} 
  • 7.3 在 MapView.swift 中,给 MapView 增加一个 coordinate 特点,然后把经纬度的硬编码换成运用这个特点。

MapView.swift

import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
    var coordinate: CLLocationCoordinate2D
    func makeUIView(context: Context) -> MKMapView {
        MKMapView(frame: .zero)
    }
    func updateUIView(_ view: MKMapView, context: Context) {
        let span = MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02)
        let region = MKCoordinateRegion(center: coordinate, span: span)
        view.setRegion(region, animated: true)
    }
}
struct MapView_Preview: PreviewProvider {
    static var previews: some View {
        MapView()
    }
}  
  • 7.4 更新 preview provider ,传递数据数组中第一个地标的坐标。

MapView.swift

import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
    var coordinate: CLLocationCoordinate2D
    func makeUIView(context: Context) -> MKMapView {
        MKMapView(frame: .zero)
    }
    func updateUIView(_ view: MKMapView, context: Context) {
        let span = MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02)
        let region = MKCoordinateRegion(center: coordinate, span: span)
        view.setRegion(region, animated: true)
    }
}
struct MapView_Preview: PreviewProvider {
    static var previews: some View {
        MapView(coordinate: landmarkData[0].locationCoordinate)
    }
} 
  • 7.5 在 LandmarkDetail.swift 中,给 LandmarkDetail 类型增加 landmark 特点。

LandmarkDetail.swift

import SwiftUI
struct LandmarkDetail: View {
    var landmark: Landmark
    var body: some View {
        VStack {
            MapView()
                .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_Preview: PreviewProvider {
    static var previews: some View {
        LandmarkDetail()
    }
} 
  • 7.6 更新 preview provider ,运用 landmarkData 中的第一个地标。

LandmarkDetail.swift

import SwiftUI
struct LandmarkDetail: View {
    var landmark: Landmark
    var body: some View {
        VStack {
            MapView()
                .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_Preview: PreviewProvider {
    static var previews: some View {
        LandmarkDetail(landmark: landmarkData[0])
    }
} 
  • 7.7 将所需数据传递给咱们的自界说类型。

LandmarkDetail.swift

import SwiftUI
struct LandmarkDetail: View {
    var landmark: Landmark
    var body: some View {
        VStack {
            MapView(coordinate: landmark.locationCoordinate)
                .frame(height: 300)
            CircleImage(image: landmark.image(forSize: 250))
                .offset(y: -130)
                .padding(.bottom, -130)
            VStack(alignment: .leading) {
                Text(landmark.name)
                    .font(.title)
                HStack(alignment: .top) {
                    Text(landmark.park)
                        .font(.subheadline)
                    Spacer()
                    Text(landmark.state)
                        .font(.subheadline)
                }
            }
            .padding()
            Spacer()
        }
    }
}
struct LandmarkDetail_Preview: PreviewProvider {
    static var previews: some View {
        LandmarkDetail(landmark: landmarkData[0])
    }
} 
  • 7.8 最终,调用 navigationBarTitle(_:displayMode:) 办法,给导航栏增加显现概况 view 时的标题。

LandmarkDetail.swift

import SwiftUI
struct LandmarkDetail: View {
    var landmark: Landmark
    var body: some View {
        VStack {
            MapView(coordinate: landmark.locationCoordinate)
                .frame(height: 300)
            CircleImage(image: landmark.image(forSize: 250))
                .offset(y: -130)
                .padding(.bottom, -130)
            VStack(alignment: .leading) {
                Text(landmark.name)
                    .font(.title)
                HStack(alignment: .top) {
                    Text(landmark.park)
                        .font(.subheadline)
                    Spacer()
                    Text(landmark.state)
                        .font(.subheadline)
                }
            }
            .padding()
            Spacer()
        }
        .navigationBarTitle(Text(landmark.name), displayMode: .inline)
    }
}
struct LandmarkDetail_Preview: PreviewProvider {
    static var previews: some View {
        LandmarkDetail(landmark: landmarkData[0])
    }
} 
  • 7.9 在 SceneDelegate.swift 中,把 app 的 rootView 改成 LandmarkList

当咱们不运用预览而是在模拟器中独立运转 app 时,app 会以 SceneDelegate 中界说的 rootView 开端显现。

SceneDelegate.swift

import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        // Use a UIHostingController as window root view controller
        let window = UIWindow(frame: UIScreen.main.bounds)
        window.rootViewController = UIHostingController(rootView: LandmarkList())
        self.window = window
        window.makeKeyAndVisible()
    }
    // ...
} 

  • 7.10 在 LandmarkList.swift 中,给方针 LandmarkDetail 传递当时的地标。

LandmarkList.swift

import SwiftUI
struct LandmarkList: View {
    var body: some View {
        NavigationView {
            List(landmarkData) { landmark in
                NavigationButton(destination: LandmarkDetail(landmark: landmark)) {
                    LandmarkRow(landmark: landmark)
                }
            }
            .navigationBarTitle(Text("Landmarks"))
        }
    }
}
struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
    }
} 

  • 7.11 切换到实时预览,能够检查从列表导航到正确的地标概况 view 了。

8. 动态生成预览

接下来,咱们会在 LandmarkList_Previews 中增加代码以在不同的设备尺度上渲染列表。默许情况下,预览会以当时的 scheme 中设备的巨细进行渲染。咱们能够经过调用 previewDevice(_:) 办法来改动预览设备。

  • 8.1 首要,改动当时 list 的预览来显现 iPhone SE 的尺度。

咱们能够输入任何 Xcode scheme 菜单中显现的设备称号。

LandmarkList.swift

import SwiftUI
struct LandmarkList: View {
    var body: some View {
        NavigationView {
            List(landmarkData) { landmark in
                NavigationButton(destination: LandmarkDetail(landmark: landmark)) {
                    LandmarkRow(landmark: landmark)
                }
            }
            .navigationBarTitle(Text("Landmarks"))
        }
    }
}
struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
            .previewDevice(PreviewDevice(rawValue: "iPhone SE"))
    }
} 

  • 8.2 在 list 预览中用设备称号数组作为数据,将 LandmarkList 嵌入到 ForEach 实例中。

ForEach 以与 list 相同的办法对调集进行操作,这样咱们就能够在任何能够运用子视图的当地运用它,比方 stackslistsgroups 等。当数据元素像这儿运用的字符串一样是简略的值类型时,咱们能够运用 .self 作为标识符的 key path

LandmarkList.swift

import SwiftUI
struct LandmarkList: View {
    var body: some View {
        NavigationView {
            List(landmarkData) { landmark in
                NavigationButton(destination: LandmarkDetail(landmark: landmark)) {
                    LandmarkRow(landmark: landmark)
                }
            }
            .navigationBarTitle(Text("Landmarks"))
        }
    }
}
struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        ForEach(["iPhone SE", "iPhone XS Max"].identified(by: .self)) { deviceName in
            LandmarkList()
                .previewDevice(PreviewDevice(rawValue: deviceName))
        }
    }
} 

  • 8.3 运用 previewDisplayName(_:) 办法把设备称号作为 labels 增加到预览中。

LandmarkList.swift

import SwiftUI
struct LandmarkList: View {
    var body: some View {
        NavigationView {
            List(landmarkData) { landmark in
                NavigationButton(destination: LandmarkDetail(landmark: landmark)) {
                    LandmarkRow(landmark: landmark)
                }
            }
            .navigationBarTitle(Text("Landmarks"))
        }
    }
}
struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        ForEach(["iPhone SE", "iPhone XS Max"].identified(by: .self)) { deviceName in
            LandmarkList()
                .previewDevice(PreviewDevice(rawValue: deviceName))
                .previewDisplayName(deviceName)
        }
    }
} 

  • 8.4 咱们能够在 canvas 中体会不同的设备,比照它们在渲染 view 时的差异。