使用 scrollTargetBehavior 自定义 ScrollView 对齐效果

了解如何使用 iOS17 上新引入的 scrollTargetBehavior 修饰器,为你的滚动视图添加更多效果。

使用 scrollTargetBehavior 自定义 ScrollView 对齐效果

SwiftUI 从 iOS13 开始提供了 ScrollView 组件,用于快速创建支持滑动的视图效果。默认情况下,ScrollView 基于滑动手势的速度,来计算滑动停止的位置——遵循苹果的非线性动画效果,因此动画非常自然。

从 iOS17 开始,SwiftUI 引入了新的 scrollTargetBehavior 修饰符,允许我们自定义 ScrollView 的滚动行为,我们可以用它创建更高级的滑动效果。

你可以在这里找到完整的实例代码:

GitHub - Jewel591/SwiftUI-ScrollView-scrollTargetBehavior-Demo
Contribute to Jewel591/SwiftUI-ScrollView-scrollTargetBehavior-Demo development by creating an account on GitHub.

ScrollView 默认无限制滚动效果

我们创建如下实示例代码,创建苹果 App Store 的卡片式 UI 布局效果:

private var featuredSection: some View {
    VStack(alignment: .leading, spacing: 12) {
        Text("今日推荐")
            .font(.title)
            .fontWeight(.bold)

        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 20) {
                ForEach(apps) { app in
                    FeaturedAppCard(app: app)
                }
            }
        }
    }
}

默认情况下,ScrollView 组件会基于减速率和滚动手势的状态来计算滚动手势的结束位置。滚动可能在任意位置停止。

但在苹果的 App Store 中,每次滑动会停留在固定的位置,这种对其方式会使得滚动更加的自然:

0:00
/0:05

在此之前,为了实现上面这样的效果,你可能需要深入定制 UIScrollView,编写大量代码来监听滚动行为。iOS17 中引入的 .ScrollTargetBehavior 修饰器,正是为了能够更简单的实现以上需求。

使用 scrollTargetBehavior 修饰器

创建分页效果(.paging)

你只需要将 scrollTargetBehavior 修饰符,添加到 ScrollView 上即可轻松使用它:

private var featuredSection: some View {
    VStack(alignment: .leading, spacing: 12) {
        Text("今日推荐")
            .font(.title)
            .fontWeight(.bold)

        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 20) {
                ForEach(apps) { app in
                    FeaturedAppCard(app: app)
                }
            }
        }
        .scrollTargetBehavior(.paging)
    }
}

使用 .paging 模式,用户在滚动时,会自动对齐到最近的页面,形成分页浏览的体验。常用于一页一页浏览内容。

但你可能已经发现了一个问题:随着卡片滑动,后续的卡片前半部分可能会被屏幕遮挡。造成这个问题的原因是:分页模式下,默认每次滑动的距离为一张卡片的宽度,因此无法确保每张卡片的头部位置固定。

要解决这个问题,我们需要使用 .viewAligned 模式。

创建视图对齐效果(.viewAligned)

.scrollTargetBehavior(.viewAligned) 修饰器需要搭配 .scrollTargetLayout() 修饰器一起使用。

可以使用 scrollTargetLayout() 修改器配置哪些视图应用于对齐。将此修改器应用于布局容器(如 LazyVStack 或 HStack),SwiftUI 会自动创建基于视图的对齐滚动效果。

看起来有点难懂,我们来看看实际的效果:

private var featuredSection: some View {
    VStack(alignment: .leading, spacing: 12) {
        Text("今日推荐")
            .font(.title)
            .fontWeight(.bold)

        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 20) {
                ForEach(apps) { app in
                    FeaturedAppCard(app: app)
                }
            }
            .scrollTargetLayout()
        }
        .scrollTargetBehavior(.viewAligned)
    }
}

由于我们将 .scrollTargetLayout() 设置在 HStack 上,因此ScrollView 将自动对齐每个 HStack 子组件的顶部:

使用示例

0:00
/0:06

https://developer.apple.com/videos/play/wwdc2024/10151/