当前位置:   article > 正文

SwiftUI中ScrollViewReader的使用(自动滚动ScrollView)_swiftui scrollviewreader

swiftui scrollviewreader

ScrollViewReader是我最喜欢的SwiftUI新版本的新功能之一。在iOS 14发布之前,控制ScrollView滚动位置并不容易。如果希望滚动视图滚动到特定位置,我们必须找到自己的解决方案。

使用ScrollViewReader,只需几行代码,就可以使滚动视图滚动到特定位置。本篇文章,我们将探究一下ScrollViewReader的使用。

关于ScrollView的使用,想必大家都不陌生了,先看一下下面这个示例:

struct ScrollViewReaderDemo: View {
  var body: some View {
    ScrollView {
      ForEach(0..<50) { index in
        Text("This is item \(index)")
          .font(.headline)
          .frame(height: 150)
          .frame(maxWidth: .infinity)
          .background(Color.white)
          .cornerRadius(10)
          .shadow(radius: 10)
          .padding()
      }
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这里插入图片描述
上面是一个比较简单的ScrollView使用方法,我们可以手动滑动界面。如果我们想要代码控制滚到哪里,或者是满足某个条件后,自动滚动,那如何处理呢,以前在UIKit中,我们能持有UIScrollView的实例变量,然后调用相关方法,那么在SwiftUI中有这么一个实例变量吗?

答案是肯定有的,那就是使用ScrollViewReader

ScrollViewReader是通过使用代理来滚动到已知的子视图,从而提供程序化滚动的视图。
ScrollViewReader的构造闭包里面返回了一个ScrollViewProxy类型的实例对象,通过这个对象调用scrollTo(_:anchor:)方法实现滚动。

func scrollTo<ID>(
    _ id: ID,
    anchor: UnitPoint? = nil
) where ID : Hashable
  • 1
  • 2
  • 3
  • 4

id: 子视图的唯一标识。
anchor: 滚动动作的对齐行为。

如果anchor为nil,则此方法会找到已标识视图,并滚动最小值以使已标识视图完全可见。
如果anchor非nil,它将定义已标识视图和滚动视图中要对齐的点。例如,将anchor设置为top将标识视图的顶部与滚动视图的顶部对齐。类似地,将anchor设置为bottom将标识视图的底部与滚动视图的底部对齐,依此类推。

下面的代码中,添加了一个Button,点击后将ScrollView滚动指定的位置,尤其要注意的是记得给每个子视图添加id修饰符,要不然滚动的时候就找不到指定视图了。

struct ScrollViewReaderDemo: View {
  var body: some View {
    ScrollViewReader { proxy in
      ScrollView {
        Button("Scroll to specific item") {
          withAnimation{
            proxy.scrollTo(30, anchor: .bottom)
          }
        }

        ForEach(0..<50) { index in
          Text("This is item \(index)")
            .font(.headline)
            .frame(height: 150)
            .frame(maxWidth: .infinity)
            .background(Color.white)
            .cornerRadius(10)
            .shadow(radius: 10)
            .padding()
            .id(index)
        }
      }
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

在这里插入图片描述
上面的代码能够轻松的实现ScrollView的滚动,ScrollViewReader闭包返回的proxy代理了ScrollView,进而操作滚动,问题来了,proxy的作用域只在ScrollViewReader闭包内,如果点击滚动的按钮在外面,或者在导航栏上,那该如何实现呢?

struct ScrollViewReaderDemo: View {
  @State private var scrollToIndex: Int?

  var body: some View {
    NavigationStack {
      ScrollViewReader { proxy in
        ScrollView {
          ForEach(0..<50) { index in
            Text("This is item \(index)")
              .font(.headline)
              .frame(height: 150)
              .frame(maxWidth: .infinity)
              .background(Color.white)
              .cornerRadius(10)
              .shadow(radius: 10)
              .padding()
              .id(index)
          }
        }
        .onChange(of: scrollToIndex) { newValue in
          if let newValue {
            withAnimation {
              proxy.scrollTo(newValue, anchor: .top)
            }
          }
        }
      }
      .navigationTitle("Title")
      .navigationBarTitleDisplayMode(.inline)
      .toolbar(content: {
        ToolbarItem(placement: .topBarTrailing) {
          Button("Scroll") {
            scrollToIndex = 30
          }
        }
      })
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

在这里插入图片描述
上面代码中将点击滚动的按钮放到了导航栏的右侧,这个时候在Button的点击事件内已经访问不到proxy代理了。
虽然访问不到了,但是我们可以通过一个状态变量(@State修饰的变量)的变化来触发滚动。

@State private var scrollToIndex: Int?
  • 1

然后在能访问到proxy代理的区域内监听scrollToIndex的变化,然后滚动视图。代码中给ScrollView添加了onChange修饰符,并添加观察对象scrollToIndex,当scrollToIndex变化的时候onChange修饰符闭包会被触发,并返回最新的scrollToIndex的值。

.onChange(of: scrollToIndex) { newValue in
  if let newValue {
    withAnimation {
      proxy.scrollTo(newValue, anchor: .top)
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Button的事件里面修改scrollToIndex的值即可。

.toolbar(content: {
  ToolbarItem(placement: .topBarTrailing) {
    Button("Scroll") {
      scrollToIndex = 30
    }
  }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

通过这种方法就实现了外部点击,内部ScrollView滚动的效果了。当然后,上面代码只是提供了一个实现外部触发滚动的参考,具体还得看大家的业务和设计需求了。

另外ScrollViewReader也可以和List组合使用,滚动List

写在最后

ScrollViewReaderSwiftUI框架的一个很好的补充。现在无需开发自己的解决方案,就可以轻松地指示任何滚动视图滚动到特定位置。

最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小惠珠哦/article/detail/780899
推荐阅读
相关标签
  

闽ICP备14008679号