WWDC2019: SwiftUI实现你的第一个App

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

说明

本文代码实现WWDC2019 视频Introducing SwiftUI: Building Your First App,改进的地方在于用XCode 11.2.1实现,视频上的内容是在XCode 11 beta上实现,有些api已经废弃。

运行效果:
WWDC2019: SwiftUI实现你的第一个App_第1张图片
WWDC2019: SwiftUI实现你的第一个App_第2张图片

创建SwiftUI工程,以及新建SwiftUI类

新建Project,命名为RoomWWDC204。
细节请参考 SwiftUI实战:一统江湖的新框架

创建Model

在SwiftUI中数据是第一等公民,这里先创建数据类Room

import SwiftUI

struct Room: Identifiable {
    var id = UUID()
    var name: String
    var capacity: Int
    var hasVideo: Bool = false
    
    var imageName: String {
        return name
    }
    var thumbnailName: String {
        return name  + "Thumb"
    }
    
}

#if DEBUG
let testData = [
    Room(name: "room-1", capacity: 6, hasVideo: true),
    Room(name: "room-2", capacity: 8, hasVideo: false),
    Room(name: "room-3", capacity: 16, hasVideo: true),
    Room(name: "room-4", capacity: 10, hasVideo: true),
    Room(name: "room-5", capacity: 12, hasVideo: false),
    Room(name: "room-6", capacity: 8, hasVideo: false),
    Room(name: "room-7", capacity: 10, hasVideo: true),
    Room(name: "room-8", capacity: 7, hasVideo: false),
    Room(name: "room-9", capacity: 1, hasVideo: false),
]

#endif

解析:

  • id: 对象唯一标识
  • name: 图片名字
  • capacity: 房间容量
  • hasVideo: 是否正在视频会议
  • imageName: 图片名字
  • thumbnailName: 缩略图名字

观察者数据

为了实现增删改查数据,数据可以逐层传递下去.
创建类RoomStore

import SwiftUI
import Combine
import Foundation

class RoomStore: ObservableObject {
    
    @Published var rooms: [Room]
    
    init(rooms: [Room]) {
        self.rooms = rooms
    }
    
}

详情页

新建Cell页面RoomDetail

import SwiftUI

struct RoomDetail: View {
    let room: Room
    @State private var zoomed = false
    
    var body: some View {
        ZStack(alignment: .topLeading) {
            Image(room.imageName)
                .resizable()
                .aspectRatio(contentMode: zoomed ? .fill : .fit)
                .navigationBarTitle(Text(room.name), displayMode: .inline)
                .onTapGesture {
                    withAnimation(.easeInOut(duration: 2)) {
                        self.zoomed.toggle()
                    }
                }
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
            
            if room.hasVideo && !zoomed {
                Image(systemName: "video.fill")
                    .font(.title)
                    .padding(.all)
                    .transition(.move(edge: .leading))
            }
        }
    }
}

#if DEBUG
struct RoomDetail_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            NavigationView {
               RoomDetail(room: testData[0])
            }
            NavigationView {
               RoomDetail(room: testData[1])
            }
        }
    }
}
#endif

解析:

  • ZStack: Z轴垂直方向的容器,下面一层放了大的图片,上面一层放了左上角摄像头。
  • resizable:图片可缩放
  • aspectRatio: 缩放比例(fill拉伸图片屏幕,fit或者等比例缩小到全部展示)
  • navigationBarTitle: 导航栏文字
  • onTapGesture: 点击事件
  • withAnimation:动画
  • transition:转场动画

这个页面可以单独预览(快捷键: Command + Option + p), 这里用Group可以同时预览两个页面。
第一个hasVideo = true, 左上角有摄像头。WWDC2019: SwiftUI实现你的第一个App_第3张图片
第二个hasVideo = false. 左上角有没有摄像头。
WWDC2019: SwiftUI实现你的第一个App_第4张图片
@State根据属性去控制界面self.zoomed.toggle()
WWDC2019: SwiftUI实现你的第一个App_第5张图片
数据流

数据权限 Source of Truth Derived Value
只读 Constant Property
读写 @State
ObservableObject
@ObservedObject

主界面List视图

tableview在这里就是List

//
//  ContentView.swift
//  RoomWWDC204
//
//  Created by zgpeace on 2019/11/24.
//  Copyright © 2019 zgpeace. All rights reserved.
//

import SwiftUI

struct ContentView: View {
    @ObservedObject var store = RoomStore(rooms: [])
    
    var body: some View {
        NavigationView {
//            List(store.rooms) { room in
            List {
                Section {
                    Button(action: addRoom) {
                        Text("Add Room")
                    }
                }
                
                Section {
                    ForEach(store.rooms) { room in
                        RoomCell(room: room)
                    }
                    .onDelete(perform: delete)
                    .onMove(perform: move)
                }
            }
            .navigationBarTitle(Text("Rooms"))
        .navigationBarItems(trailing: EditButton())
            .listStyle(GroupedListStyle())
        }
    
    }
    
    func addRoom() {
        store.rooms.append(Room(name: "Hall 2", capacity: 2000))
    }
    
    func delete(at offsets: IndexSet) {
        store.rooms.remove(atOffsets: offsets)
    }
    
    func move(from source: IndexSet, to destination: Int) {
        store.rooms.move(fromOffsets: source, toOffset: destination)
    }
}

struct RoomCell: View {
    let room: Room
    
    var body: some View {
        NavigationLink(destination: RoomDetail(room: room)) {
            Image(room.thumbnailName)
                .cornerRadius(8)
            VStack(alignment: .leading) {
                Text(room.name)
                Text("\(room.capacity) people")
                    .font(.subheadline)
                    .foregroundColor(.secondary)
            }
        }
    }
}

#if DEBUG
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ContentView(store: RoomStore(rooms: testData))
            
            ContentView(store: RoomStore(rooms: testData))
                .environment(\.sizeCategory, .extraExtraExtraLarge)
            
            ContentView(store: RoomStore(rooms: testData))
                .environment(\.colorScheme, .dark)
            
            ContentView(store: RoomStore(rooms: testData))
            .environment(\.layoutDirection, .rightToLeft)
        }
    }
}
#endif

解析:

  • listStyle: tableview的展示样式,比如这里就用GroupedListStyle()样式
  • onDelete: 可删除
  • onMove: 可移动

运行效果:
图片展示的是group里的第三种,黑暗模式ContentView(store: RoomStore(rooms: testData)) .environment(\.colorScheme, .dark)

WWDC2019: SwiftUI实现你的第一个App_第6张图片

SwiftUI优势解析

以前用UIKit的时候 Controller是控制中心,数据流比较复杂. 导致Controller膨胀很大。
WWDC2019: SwiftUI实现你的第一个App_第7张图片
导致UI Bugs出现不可控
WWDC2019: SwiftUI实现你的第一个App_第8张图片
事件状态转换、调用相当频繁
WWDC2019: SwiftUI实现你的第一个App_第9张图片
导致页面跟数据不同步
WWDC2019: SwiftUI实现你的第一个App_第10张图片

SwiftUI 只有body struct, 把状态统一为一种类型
WWDC2019: SwiftUI实现你的第一个App_第11张图片
WWDC2019: SwiftUI实现你的第一个App_第12张图片

SwiftUI是最快速构建伟大App的新框架
WWDC2019: SwiftUI实现你的第一个App_第13张图片

总结

细节请移步WWDC,WWDC的讲解包括UI的交互和原理。
推荐Mac观看WWCD的软件:WWDC for macOS

代码下载

https://github.com/zgpeace/RoomWWDC204/tree/master

参考

WWDC
https://developer.apple.com/videos/play/wwdc2019/204/

课件下载
https://devstreaming-cdn.apple.com/videos/wwdc/2019/204isgnpbqud244/204/204_introducing_swiftui_building_your_first_app.pdf

https://www.raywenderlich.com/3868932-wwdc-2019-top-10-videos

https://wwdc.io/

你可能感兴趣的:(iOS)