前言
我们的滚动 API 中又有一个重要的新增功能:滚动可见性。现在,你可以获取可见标识符列表,或者快速检查并监控 ScrollView 内视图的可见性状态。本周,我们将学习如何使用新的 onScrollTargetVisibilityChange
和 onScrollVisibilityChange
视图修饰符。
视图修饰符
让我们先从 onScrollTargetVisibilityChange
视图修饰符开始。它设计得易于使用,允许你将其附加到具有滚动目标布局的任何 ScrollView 上。让我们通过一个示例来探讨这个修饰符的使用。
swift
struct ContentView: View {
@State private var visible: [Int] = []
var body: some View {
ScrollView {
LazyVStack {
ForEach(1..<100, id: \.self) { item in
Text(verbatim: item.formatted())
}
}
.scrollTargetLayout()
}
.onScrollTargetVisibilityChange(idType: Int.self) { identifiers in
visible = identifiers
}
.onChange(of: visible) {
print(visible)
}
}
}
如上例所示,我们在懒加载栈(LazyVStack)上使用 scrollTargetLayout
视图修饰符,以便允许 ScrollView 针对栈的子视图进行目标识别,而不是针对栈本身。
要了解有关 scrollTargetLayout
视图修饰符的更多信息,请查看我的文章《掌握 SwiftUI 中的 ScrollView:滚动几何》。
应用场景
我们还将 onScrollTargetVisibilityChange
视图修饰符附加到 ScrollView 上,提供标识符类型和操作闭包。在操作闭包内,我们获取可见标识符列表,并可以对可见项执行所需的操作。
有时,视图需要在其可见性状态在 ScrollView 中发生变化时进行响应。对于这些情况,SwiftUI 框架引入了 onScrollVisibilityChange
视图修饰符,你可以将其附加到 ScrollView 内的任何视图上以处理其可见性。
swift
struct VideoPlayerView: View {
let url: URL
@State var player: AVPlayer?
var body: some View {
VideoPlayer(player: player)
.task {
if player == nil {
player = AVPlayer(url: url)
}
}
.onScrollVisibilityChange { isVisible in
if isVisible {
player?.play()
} else {
player?.pause()
}
}
}
}
上例定义了 VideoPlayerView
视图,该视图在其可见时自动播放视频内容。正如你所见,我们将 onScrollVisibilityChange
视图修饰符附加到视图本身,并提供一个操作闭包。我们在操作闭包内获得可见性参数,并可以对其变化进行响应。
可见性
onScrollVisibilityChange
和 onScrollTargetVisibilityChange
修饰符都具有 threshold
参数。threshold
参数允许我们调整需要可见的视口部分的数量,以触发操作闭包。默认情况下,SwiftUI 框架使用 0.5 作为阈值,这意味着至少 50% 的视图需要可见,SwiftUI 才会运行操作。但你可以轻松调整此值。
swift
struct VideoPlayerView: View {
let url: URL
@State var player: AVPlayer?
var body: some View {
VideoPlayer(player: player)
.task {
if player == nil {
player = AVPlayer(url: url)
}
}
.onScrollVisibilityChange(threshold: 0.1) { isVisible in
if isVisible {
player?.play()
} else {
player?.pause()
}
}
}
}
在上述示例中,我们定义了阈值,这意味着 SwiftUI 将在视图至少有 10% 可见时运行操作闭包。同样,当视图从可见状态转换为不可见状态,即显示的视口部分少于 10% 时,也会运行该闭包。
完整示例
上面对视图修饰符有了初步了解,它的设计得易于使用,允许你将其附加到具有滚动目标布局的任何 ScrollView 上。让我们通过一个示例来探讨这个修饰符的使用。
示例代码如下:
swift
import SwiftUI
import AVKit
struct ContentView: View {
@State private var visible: [Int] = []
var body: some View {
ScrollView {
LazyVStack {
ForEach(1..<100, id: \.self) { item in
Text(verbatim: item.formatted())
.frame(width: 100, height: 100)
.background(item % 2 == 0 ? Color.blue : Color.red)
.cornerRadius(10)
.padding(5)
}
}
.scrollTargetLayout()
}
.onScrollTargetVisibilityChange(idType: Int.self) { identifiers in
visible = identifiers
}
.onChange(of: visible) { newVisible in
print("Visible items: \(newVisible)")
}
.navigationTitle("ScrollView Demo")
}
}
struct VideoPlayerView: View {
let url: URL
@State var player: AVPlayer?
var body: some View {
VideoPlayer(player: player)
.frame(height: 200)
.task {
if player == nil {
player = AVPlayer(url: url)
}
}
.onScrollVisibilityChange { isVisible in
if isVisible {
player?.play()
} else {
player?.pause()
}
}
}
}
@main
struct ScrollViewVisibilityApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
VStack {
ContentView()
Spacer()
VideoPlayerView(url: URL(string: "https://www.example.com/video.mp4")!)
.padding()
}
}
}
}
}
这个示例 Demo 展示了如何使用 onScrollTargetVisibilityChange
和 onScrollVisibilityChange
视图修饰符来跟踪 ScrollView 中的视图可见性。整个示例分为两个部分:一个是显示带有多个文本视图的 ScrollView,另一个是显示一个视频播放器视图。
ContentView
- ScrollView 和 LazyVStack :使用
ScrollView
包裹一个LazyVStack
,在其中放置 1 到 99 的数字。每个数字都显示在一个Text
视图中,并有不同的背景颜色。 - scrollTargetLayout :在
LazyVStack
上应用scrollTargetLayout
视图修饰符,以允许 ScrollView 针对栈的子视图进行目标识别。 - onScrollTargetVisibilityChange :在 ScrollView 上应用
onScrollTargetVisibilityChange
视图修饰符,并提���标识符类型和操作闭包。在操作闭包内,获取可见标识符列表并赋值给visible
状态变量。 - onChange :监听
visible
状态变量的变化,并打印当前可见的项。
VideoPlayerView
- VideoPlayer :定义一个视频播放器视图,使用
AVPlayer
播放视频。 - task :在
task
修饰符中初始化播放器。 - onScrollVisibilityChange :在视频播放器视图上应用
onScrollVisibilityChange
视图修饰符,并提供一个操作闭包。在操作闭包内,根据可见性状态来播放或暂停视频。
ScrollViewVisibilityApp
- 主应用入口 :定义主应用入口
ScrollViewVisibilityApp
,将ContentView
和VideoPlayerView
组合到一个垂直堆栈中,并通过NavigationView
进行导航。
运行这个 Demo,你会看到一个带有多个文本视图的 ScrollView,当你滚动时,控制台会打印当前可见的项。此外,在页面底部有一个视频播放器,当视频播放器出现在视口内时,它会自动播放,当其离开视口时,会自动暂停。
总结
今天,我们学习了如何跟踪 ScrollView 内特定视图的可见性,并监控可见标识符列表。示例展示了如何使用 SwiftUI 的滚动可见性修饰符来增强用户体验和交互性。希望能对你有所帮助。