WWDC22: SwiftUI 的新功能 part 3
发布
更新
字数
665
阅读
4 分钟
阅读量
1101
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
GridRow
和 gridCellColumns
修饰符可以构建碎片化的网格布局。
struct VIPDetailView: View {
var body: some View {
Grid {
GridRow {
NameHeadline()
.gridCellColumns(2)
}
GridRow {
CalendarIcon()
SymbolGrid()
}
}
}
}