WWDC22: SwiftUI 的新功能 part 3

发布
更新
字数 665
阅读 4 分钟
阅读量 916

Photos

新的照片选择器组件 PhotosPicker 可以放在任何位置,然后通过 view builder 构建可以唤出选择器的按钮样式,使用 PhotosPicker 选择照片时通过绑定(Binding选中项目 PhotosPickerItem,访问实际照片或视频。

import PhotosUI
import CoreTransferable

struct ContentView: View {
    @ObservedObject var viewModel: FilterModel = .shared
    
    var body: some View {
        NavigationStack {
            Gallery()
                .navigationTitle("Birthday Filter")
                .toolbar {
                    // 照片选择器
                    PhotosPicker(
                        // 绑定选中项目
                        selection: $viewModel.imageSelection,
                        // 仅访问照片
                        matching: .images
                    ) {
                        // 构建按钮样式
                        Label("Pick a photo", systemImage: "plus.app")
                    }
                    Button {
                        viewModel.applyFilter()
                    } label: {
                        Label("Apply Filter", systemImage: "camera.filters")
                    }
                }
        }
    }
}

struct Gallery: View {
    @ObservedObject var viewModel: FilterModel = .shared

    var body: some View {
        VStack {
            // 根据状态切换显示内容
            switch viewModel.imageState {
            // 成功获取到照片
            case .success(let image):
                image
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .draggable(image)
            case .loading:
                ProgressView()
            case .empty:
                Text("No Photo \(Image(systemName: "photo"))")
                    .font(.title2)
                    .fontWeight(.semibold)
                Text("Drag and drop a photo or press\n \(Image(systemName: "plus.app")) to choose a photo manually.")
                    .foregroundColor(.secondary)
                    .multilineTextAlignment(.center)
            case .failure:
                Image(systemName: "exclamationmark.triangle.fill")
                    .font(.system(size: 40))
                    .foregroundColor(.white)
            }
        }
        .padding()
    }
}

@MainActor
class FilterModel: ObservableObject {
    static let shared = FilterModel()

    enum ImageState {
        case empty, loading(Progress), success(Image), failure(Error)
    }

    @Published private(set) var processedImage: Image?
    @Published var imageState: ImageState = .empty
    @Published var imageSelection: PhotosPickerItem? = nil {
        didSet {
            // 绑定的 `PhotosPickerItem`,加载照片
            if let imageSelection = imageSelection {
                let progress = loadTransferable(from: imageSelection)
                imageState = .loading(progress)
            } else {
                imageState = .empty
            }
        }
    }

    func applyFilter() { /* Apply your filter */ }

    private func loadTransferable(from imageSelection: PhotosPickerItem) -> Progress {
        // 使用 `loadTransferable` 方法加载图片
        return imageSelection.loadTransferable(type: Image.self) { result in
            DispatchQueue.main.async {
                guard imageSelection == self.imageSelection else { return }
                switch result {
                case .success(let image?):
                    self.imageState = .success(image)
                case .success(nil):
                    self.imageState = .empty
                case .failure(let error):
                    self.imageState = .failure(error)
                }
            }
        }
    }
}

Sharing

新的 ShareLink API 用于分享内容

if let item = viewModel.processedImage {
    ShareLink(
        item: item, preview: SharePreview("Birthday Effects"))
}

dropDestination

新的 dropDestination API 可以接受一个有效负载,在不同的 App 间传送数据。

@State private var image: Image?

var body: some View {
    Gallery(...)
    .dropDestination(
        payloadType: Image.self
    ) { receivedImages, location in
       image = receivedImages.first
       return !receivedImages.isEmpty
    }
}

许多标准协议,如字符串和图像,已经符合 Transferable,如果是其他自定义类型,需要实现 Transferable,了解更多关于 Transferable 的信息

Graphics and layout

Color

Color 支持渐变色阴影修改器,支持文本、符号。

struct CalendarIcon: View {
    var body: some View {
        VStack {
            Image(systemName: "calendar")
            Text("June 6")
        }
        .background(in: Circle().inset(by: -20))
        // 渐变
        .backgroundStyle(.blue.gradient)
        .foregroundStyle(
            // 阴影
            .white.shadow(.drop(radius: 1, y: 1.5))
        )
        .padding(20)
    }
}

文本动画

文本支持平滑的字体、颜色等样式动画过渡效果。

Text("Happy Birthday, SwiftUI")
	.font(expandMessage ? .largeTitle.weight(.heavy) ? .body)
    .foregroundStyle(expandMessage ? mintWithShadow : primaryWithoutShadow)
	.onTapGesture {
        withAnimation {
            expandMessage.toggle()
        }
    }

网格布局

使用 Grid GridRowgridCellColumns 修饰符可以构建碎片化的网格布局。

struct VIPDetailView: View {
    var body: some View {
        Grid {
            GridRow {
                NameHeadline()
                    .gridCellColumns(2)
            }
            GridRow {
                CalendarIcon()
                SymbolGrid()
            }
        }
    }
}