SwiftUI实战三:创建List视图和导航Navigation

系统: Mac OS 10.15.1, XCode 11.2.1,swift 5.0
写作时间:2019-11-30

1. 说明

此例子包含元素:List视图(代替以前的TableView), 导航。这个例子接着上一个例子SwiftUI实战二:组合视图和地图视图,把它当作详情页。
细节请查看官网教程

2. 元素以及代码下载

https://github.com/zgpeace/BuildingListsAndNavigation

初始项目请用以下目录StartingPoint > Landmarks
在这里插入图片描述

3. 数据模型

在这里插入图片描述
Landmark.swift定义了struct结构体数据的字段,
landmarkData.json存储了列表数据
Data.swift 实现加载文件landmarkData.json里的数据,并构建为Landmark.swift模型数组let landmarkData: [Landmark] = load("landmarkData.json")

LandmarkDetail.swift的内容就是上一节ContentView.swift里面的内容.

Landmark.swift需要实现协议Identifiable,否则在List调用会报错。

Lists work with identifiable data. You can make your data identifiable in one of two ways: by passing along with your data a key path to a property that uniquely identifies each element, or by making your data type conform to the Identifiable protocol.

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)
    }

    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
}

4. 创建Cell行视图

实现结果图如下:
在这里插入图片描述
创建SwiftUI view, 命名为 LandmarkRow.swift.

//
//  LandmarkRow.swift
//  Landmarks
//
//  Created by zgpeace on 2019/11/30.
//  Copyright © 2019 Apple. All rights reserved.
//

import SwiftUI

struct LandmarkRow: View {
    var landmark: Landmark
    
    var body: some View {
        HStack() {
            landmark.image
                .resizable()
                .frame(width: 50, height: 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))
    }
}

代码解析:

  1. 预览分组: Group可以同时预览多个视图
  2. previewLayout: 可以设置大小

预览效果:
在这里插入图片描述

5. 创建List视图

新建SwiftUI view, 命名为 LandmarkList.swift.

import SwiftUI

struct LandmarkList: View {
    var body: some View {
        NavigationView {
            List(landmarkData) { landmark in
                NavigationLink(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"], id: \.self) { deviceName in
            LandmarkList()
            .previewDevice(PreviewDevice(rawValue: deviceName))
        }
    }
}

代码解析:

  1. NavigationView: 添加导航栏
  2. .navigationBarTitle(Text("Landmarks")):导航栏设置title
  3. NavigationLink(destination: LandmarkDetail(landmark: landmark)):点击子项跳转到详情页

预览效果如下:
在这里插入图片描述
在这里插入图片描述

6. 圆形图片页数据改为动态获取

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_Previews: PreviewProvider {
    static var previews: some View {
        CircleImage(image: landmarkData[0].image)
    }
}

预览效果:
在这里插入图片描述

7. 地图页面数据改为动态获取

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: 2.0, longitudeDelta: 2.0)
        let region = MKCoordinateRegion(center: coordinate, span: span)
        view.setRegion(region, animated: true)
    }
}

struct MapView_Previews: PreviewProvider {
    static var previews: some View {
        MapView(coordinate: landmarkData[0].locationCoordinate)
    }
}

需要Live Preview才能看到地图:
在这里插入图片描述

8. 详情页数据改为动态获取

import SwiftUI

struct LandmarkDetail: View {
    var landmark: Landmark
    
    var body: some View {
        VStack {
            MapView(coordinate: landmark.locationCoordinate)
                .edgesIgnoringSafeArea(.top)
                .frame(height: 300)

            CircleImage(image: landmark.image)
                .offset(x: 0, 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_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkDetail(landmark: landmarkData[0])
    }
}

预览效果:
在这里插入图片描述

9. SceneDelegate初始页面更新

变动的函数如下:

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
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: LandmarkList())
        self.window = window
        window.makeKeyAndVisible()
    }
}

10. 还可以设置device为iPad预览

在这里插入图片描述

参考

https://developer.apple.com/tutorials/swiftui/building-lists-and-navigation

https://developer.apple.com/tutorials/swiftui/tutorials

发布了127 篇原创文章 · 获赞 12 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/zgpeace/article/details/103329294