WWDC 23 已经到来,有新SwiftUI 框架中有很多改变和新增的有新功能。在本文中将主要介绍 SwiftUI 中数据流、有新动画、有新ScrollView、有新搜索、有新新手势等功能的有新新变化。
Swift 5.9 引入了宏功能,有新成为 SwiftUI 数据流的有新核心。SwiftUI 不再使用 Combine,而是使用新的 Observation 框架。Observation 框架为我们提供了 Observable 协议,必须使用它来允许 SwiftUI 订阅更改并更新视图。
@Observablefinal class Store { var products: [String] = [] var favorites: [String] = [] func fetch() async { try? await Task.sleep(nanoseconds: 1_000_000_000) // load products products = [ "Product 1", "Product 2" ] }}
不需要在代码中遵循 Observable 协议。相反,可以使用 @Observable 宏来标记你的类型,它会自动为符合 Observable 协议。也不再需要 @Published 属性包装器,因为 SwiftUI 视图会自动跟踪任何可观察类型的可用属性的更改。
struct ProductsView: View { @State private var store = Store() var body: some View { List(store.products, id: \.self) { product in Text(verbatim: product) } .task { if store.products.isEmpty { await store.fetch() } } }}
以前,有一系列的属性包装器,如 State、StateObject、ObservedObject 和 EnvironmentObject,你应该了解何时以及为何使用它们。
现在,状态管理变得更加简单。对于值类型(如字符串和整数)和符合 Observable 协议的引用类型,只需使用 State 属性包装器。
struct FavoriteProductsView: View { let store: Store var body: some View { List(store.favorites, id: \.self) { product in Text(verbatim: product) } }}
在上面的示例中,有一个接受 Store 类型的视图。在之前的 SwiftUI 框架版本中,应该使用 @ObservedObject 属性包装器来订阅更改。现在不需要了,因为 SwiftUI 视图会自动跟踪符合 Observable 协议的类型的更改。
struct EnvironmentViewExample: View { @Environment(Store.self) private var store var body: some View { Button("Fetch") { Task { await store.fetch() } } }}struct ProductsView: View { @State private var store = Store() var body: some View { List(store.products, id: \.self) { product in Text(verbatim: product) } .task { if store.products.isEmpty { await store.fetch() } } .toolbar { NavigationLink { EnvironmentViewExample() } label: { Text(verbatim: "Environment") } } .environment(store) }}
还可以使用 Environment 属性包装器与 environment 视图修饰符配对,将可观察类型放入 SwiftUI 环境中。不需要使用 @EnvironmentObject 属性包装器或 environmentObject 视图修饰符。同样的 Environment 属性包装器现在适用于可观察类型。
struct BindanbleViewExample: View { @Bindable var store: Store var body: some View { List($store.products, id: \.self) { $product in TextField(text: $product) { Text(verbatim: product) } } }}
每当需要从可观察类型中提取绑定时,可以使用新的 Bindable 属性包装器。
动画始终是 SwiftUI 框架中最重要的部分。在 SwiftUI 中轻松实现任何动画,但之前的框架版本缺少一些现在具有的功能。
struct AnimationExample: View { @State private var value = false var body: some View { Text(verbatim: "Hello") .scaleEffect(value ? 2 : 1) .onTapGesture { withAnimation { value.toggle() } completion: { print("Animation have finished") } } }}
如上例所示,我们有了新版本的 withAnimation 函数,允许提供动画完成处理程序。这是一个很好的补充,现在您可以构建阶段性动画。
enum Phase: CaseIterable { case start case loading case finish var offset: CGFloat { // Calculate offset for the particular phase switch self { case start: 100.0 case loading: 0.0 case finish: 50.0 } }}struct PhasedAnimationExample: View { @State private var value = false var body: some View { PhaseAnimator(Phase.allCases, trigger: value) { phase in LoadingView() .offset(x: phase.offset) } animation: { phase in switch phase { case .start: .easeIn(duration: 0.3) case .loading: .easeInOut(duration: 0.5) case .finish: .easeOut(duration: 0.1) } } }}
SwiftUI 框架引入了新的 PhaseAnimator 视图,它遍历阶段序列,允许为每个阶段提供不同的动画,并在阶段更改时更新内容。还有 KeyframeAnimator 视图,可以使用关键帧来实现动画。
今年 ScrollView 有了很多优秀的新增功能。首先,可以使用 scrollPosition 视图修饰符来观察内容偏移量。
struct ContentView: View { @State private var scrollPosition: Int? = 0 var body: some View { ScrollView { Button("Scroll") { scrollPosition = 80 } ForEach(1..<100, id: \.self) { number in Text(verbatim: number.formatted()) } .scrollTargetLayout() } .scrollPosition(id: $scrollPosition) }}
如上例所示,使用 scrollPosition 视图修饰符将内容偏移量绑定到一个状态属性上。每当用户滚动视图时,它会通过设置第一个可见视图的标识来更新绑定。还可以通过编程方式滚动到任何视图,但是,应该使用 scrollTargetLayout 视图修饰符来告诉 SwiftUI 框架在哪里查找标识以更新绑定。
struct ContentView: View { var body: some View { ScrollView { ForEach(1..<100, id: \.self) { number in Text(verbatim: number.formatted()) } .scrollTargetLayout() } .scrollTargetBehavior(.paging) }}
可以通过使用 scrollTargetBehavior 视图修饰符来更改滚动行为。它允许在滚动视图中启用分页。
与搜索相关的视图修饰符也有一些很好的新增功能。例如,可以通过编程方式聚焦到搜索字段。
struct ProductsView: View { @State private var store = Store() @State private var query = "" @State private var scope: Scope = .default var body: some View { List(store.products, id: \.self) { product in Text(verbatim: product) } .task { if store.products.isEmpty { await store.fetch() } } .searchable(text: $query, isPresented: .constant(true), prompt: "Query") .searchScopes($scope, activation: .onTextEntry) { Text(verbatim: scope.rawValue) } }}
如上例所示,可以使用可搜索视图修饰符的 isPresented 参数来显示/隐藏搜索字段。还可以使用 searchScopes 视图修饰符的 activation 参数来定义范围的可见性逻辑。
新增的 RotateGesture 和 MagnifyGesture 使我们能够跟踪视图的旋转和放大。
struct RotateGestureView: View { @State private var angle = Angle(degrees: 0.0) var rotation: some Gesture { RotateGesture() .onChanged { value in angle = value.rotation } } var body: some View { Rectangle() .frame(width: 200, height: 200, alignment: .center) .rotationEffect(angle) .gesture(rotation) }}
增加了全新的 ContentUnavailableView 类型,当需要显示空视图时可以使用它。示例如下:
struct ProductsView: View { @State private var store = Store() var body: some View { List(store.products, id: \.self) { product in Text(verbatim: product) } .background { if store.products.isEmpty { ContentUnavailableView("Products list is empty", systemImage: "list.dash") } } .task { if store.products.isEmpty { await store.fetch() } } }}
还有新增了新的视图修饰符,允许调整列表中的间距。可以使用 listRowSpacing 和 listSectionSpacing 视图修饰符来设置列表中所需的间距。EnvironmentValues 结构体包含了一系列与最新平台更新相关的新属性,例如 isActivityFullscreen 和 showsWidgetContainerBackground。Swift Charts 也具有可滚动和可动画的功能。
#Preview { ContentView()}
还有一个新的 Preview 宏,可以让我们轻松地为 UIKit 和 SwiftUI 构建预览,只需几行代码。
SwiftUI 框架中有许多小的新增功能,我们将会继续分享。希望能帮到你。
责任编辑:武晓燕 来源: Swift社区 Swift视图修饰符(责任编辑:焦点)
寰亚传媒(08075.HK)中期亏损收窄至1916万港元 每股亏损4.58港仙
广西汽车集团与中国移动在车联网领域建立战略合作关系 形成运营新模式
银保监会: 督促银行机构筑牢内控合规“防火墙” 夯实高质量发展根基
金通灵(300091.SZ):南通科创未减持公司股份 减持计划期限届满