一、概述
在文章01-SwiftUI|了解SwiftUI中,咱们现已了解了何为 SwiftUI
,咱们接下来逐步了解怎么经过SwiftUI
构建咱们的App页面。
- 首要,咱们经过一篇文章了解下怎么创立和组合View
- 紧接着,咱们经过本篇文章了解怎么
构建列表(相似TableView)与导航(相似NavigationController)
二、构建列表与导航
完结了根底的地标概况 view 后,咱们需求为用户供给检查完整
地标列表
,以及检查每个地标概况
的办法。在本文中,咱们将会创立可显现任何地标信息的
view
,并动态生成翻滚列表,用户能够点按该列表以检查地标的详细视图。另外,咱们还将运用Xcode
的canvas
来显现不同设备的巨细,以此来微调 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 是用于显现每个地标概况的 row
。 row
将地标数据存储在 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. 创立地标列表
运用 SwiftUI
的 List
类型能够显现渠道特有的列表 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 的内容时,咱们会把 CircleImage
、 MapView
和 LandmarkDetail
的显现从硬编码改为传入的数据。
- 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
相同的办法对调集进行操作,这样咱们就能够在任何能够运用子视图的当地运用它,比方 stacks
, lists
,groups
等。当数据元素像这儿运用的字符串一样是简略的值类型时,咱们能够运用 .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
时的差异。