SwiftUI 6.0(iOS 18.0)滚动视图新增的滚动阶段(Scroll Phase)监听功能趣谈

2024-07-14 1418阅读

SwiftUI 6.0(iOS 18.0)滚动视图新增的滚动阶段(Scroll Phase)监听功能趣谈

何曾几时,在 SwiftUI 开发中的秃头小码农们迫切需要一种能够读取当前滚动状态的方法。

SwiftUI 6.0(iOS 18.0)滚动视图新增的滚动阶段(Scroll Phase)监听功能趣谈

在过去,他们往往需要借助于 UIKit 的神秘力量。不过这一切在 SwiftUI 6.0 中已成“沧海桑田”。

在本篇博文中,您将学到如下内容:

  • 1. ScrollView 滚动阶段简介
  • 2. 普度众生的 SwiftUI 6.0
  • 3. 滚动阶段更改上下文(ScrollPhaseChangeContext)
  • 4. 如何监听列表(List)的滚动阶段
  • 总结

    相信学完本课后,小伙伴们在需要监听滚动视图滚动阶段的应用场景中定能得心应手、游刃有余!

    那还等什么呢?让我们马上开始吧!Let‘s go!!!😃


    1. ScrollView 滚动阶段简介

    所谓滚动阶段(Scroll Phase)是指滚动视图在滚动前、滚动中以及滚动后所处的不同阶段。

    早在 macOS 10.9+ 的 CoreGraphics 中就有滚动阶段的概念了:

    SwiftUI 6.0(iOS 18.0)滚动视图新增的滚动阶段(Scroll Phase)监听功能趣谈

    SwiftUI 6.0(iOS 18.0)滚动视图新增的滚动阶段(Scroll Phase)监听功能趣谈

    如果我们能及时的读取各个滚动阶段的值,我们就可以根据它们为滚动视图提供更加“银杏化”的定制和更流畅滚动附加体验。

    在 SwiftUI 6.0 之前,我们无法使用行之有效的方法来读取滚动视图当前所处的滚动阶段,只有委身救助于 UIKit 的秉轴持钧。

    然而,这一切在 SwiftUI 6.0 中有了翻天覆地的变化!

    2. 普度众生的 SwiftUI 6.0

    自从 SwiftUI 6.0(iOS 18.0)开始,“顿悟”的苹果终于提供滚动阶段的监听功能了。

    一方面,我们有了描述滚动阶段的新类型 ScrollPhase:

    SwiftUI 6.0(iOS 18.0)滚动视图新增的滚动阶段(Scroll Phase)监听功能趣谈

    它包含 5 个枚举值分别对应于 5 种滚动阶段:

    @frozen public enum ScrollPhase : Equatable {
        case idle
        case tracking
        case interacting
        case decelerating
        case animating
        
        public var isScrolling: Bool { get }
    }
    

    这些滚动阶段的含义如下所示:

    • Idle - 表示当前滚动视图处于空闲状态,可以认为“嘛事没有”;
    • Tracking - 表示当前用户正轻触滚动视图但并没有开始滚动;
    • Interacting - 表示用户正在开始或继续滚动着视图的内容;
    • Decelerating -表示用户已结束滚动操作,滚动视图的滚动正在减速直至静止状态;
    • Animating - 表示滚动视图被 ScrollPosition 或 ScrollViewReader 类型通过代码动态滚动到了指定的位置;

      另一方面,我们有了新的视图改器方法 onScrollPhaseChange 专注于滚动阶段的监听:

      SwiftUI 6.0(iOS 18.0)滚动视图新增的滚动阶段(Scroll Phase)监听功能趣谈

      有了以上两者的珠联璧合,现在我们在 SwiftUI 6.0 即可轻而易举的监听任何滚动视图的滚动阶段啦:

      struct ContentView: View {
              
          var body: some View {
              ScrollView {
                  
                  ForEach(1...50, id: \.self) { i in
                      Text("Item \(i)")
                          .font(.title)
                          .padding()
                      
                      Divider()
                  }
              
              }
              .onScrollPhaseChange { old, new in
                  guard old != new else { return }
                  print("new phase: \(new)")
                  
              }
          }
      }
      

      在 Xcode 16beta 中运行效果如下所示:

      SwiftUI 6.0(iOS 18.0)滚动视图新增的滚动阶段(Scroll Phase)监听功能趣谈

      ScrollPhase 类型还提供一个 isScrolling 计算属性,我们可以用它来判断当前是否正在滚动。比如,假若视图正在被滚动我们就“遮挡”它的显示内容:

      struct ContentView: View {
          
          @State var isScrolling = false
              
          var body: some View {
              ScrollView {
                  
                  ForEach(1...50, id: \.self) { i in
                      Text("Item \(i)")
                          .font(.title)
                          .padding()
                      
                      Divider()
                  }
                  .redacted(reason: isScrolling ? .placeholder : [])
              
              }
              .onScrollPhaseChange { old, new in
                  guard old != new else { return }
                  print("正在滚动?\(new.isScrolling)")
                  isScrolling = new.isScrolling
              }
          }
      }
      

      执行效果如下图所示:

      SwiftUI 6.0(iOS 18.0)滚动视图新增的滚动阶段(Scroll Phase)监听功能趣谈

      3. 滚动阶段更改上下文(ScrollPhaseChangeContext)

      除此之外,SwiftUI 6.0 中新增的 onScrollPhaseChange 修改器还提供另一种重载(Overloading)形式,在该重载方法的闭包中我们会得到一个 ScrollPhaseChangeContext 上下文对象,使用它我们可以更多的掌控滚动的其它全局信息:

      SwiftUI 6.0(iOS 18.0)滚动视图新增的滚动阶段(Scroll Phase)监听功能趣谈

      nonisolated
      func onScrollPhaseChange(_ action: @escaping (ScrollPhase, ScrollPhase, ScrollPhaseChangeContext) -> Void) -> some View
      

      演示代码如下所示,可以看到在其中我们使用 ScrollPhaseChangeContext 上下文对象打印出了更多的与滚动相关的信息:

      struct ContentView: View {        
          var body: some View {
              ScrollView {
                  
                  ForEach(1...50, id: \.self) { i in
                      Text("Item \(i)")
                          .font(.title)
                          .padding()
                      
                      Divider()
                  }        
              }
              .onScrollPhaseChange { old, new, context in
                  guard old != new else { return }
                  print("\(new)\n\(context)")            
              }
          }
      }
      

      运行结果如下所示:

      SwiftUI 6.0(iOS 18.0)滚动视图新增的滚动阶段(Scroll Phase)监听功能趣谈

      ScrollPhaseChangeContext(geometry: , velocity: Optional((0.0, 0.0)))

      4. 如何监听列表(List)的滚动阶段

      虽然 SwiftUI 6.0 破茧而出的“大杀器” onScrollPhaseChange 对于我们监听滚动状态大有裨益,不过目前它只能应用在 ScrollView 视图的外层。这意味着,如果将其放在 List 上将会“徒劳无功”:

      struct ContentView: View {        
          var body: some View {
              List {
                  ForEach(1...50, id: \.self) { i in
                      Text("Item \(i)")
                          .font(.title)
                          .padding()
                  }        
              }
              .onScrollPhaseChange { old, new, context in
                  guard old != new else { return }
                  print("\(new)\n\(context)")
              }
          }
      }
      

      上述代码附着在 List 之上的 onScrollPhaseChange 修改器回调闭包将会无所事事,直接沦为“不舞之鹤”。

      诚然我们可以使用 ScrollView 来平替 List,不过如果能在 List 上直接监听滚动阶段岂不更妙?

      在 iOS 18.0beta 中,我们可以通过将 List 包裹在 Form 容器中暂时绕开此问题:

      struct ContentView: View {        
          var body: some View {
              Form {
                  List {
                      ForEach(1...50, id: \.self) { i in
                          Text("Item \(i)")
                              .font(.title)
                              .padding()
                      }
                  }
              }
              .onScrollPhaseChange { old, new, context in
                  guard old != new else { return }
                  print("\(new)\n\(context)")
              }
          }
      }
      

      运行代码可以看到,我们用 onScrollPhaseChange 修改器成功的捕获到了 List 中滚动阶段的改变以及其它滚动信息:

      SwiftUI 6.0(iOS 18.0)滚动视图新增的滚动阶段(Scroll Phase)监听功能趣谈

      我不确定这一情况在 iOS 18.0 正式版中是否能够修复,让我们拭目以待吧!

      总结

      在本篇博文中,我们介绍了 SwiftUI 6.0(iOS 18.0)滚动视图最新的滚动阶段(Scroll Phase)监听功能,并讨论了如何在原本不支持该功能的列表(List)上使用它。

      感谢观赏,再会啦!😎

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]