【问题标题】:Get the current position of ScrollView in SwiftUI?获取 SwiftUI 中 ScrollView 的当前位置?
【发布时间】:2019-06-08 03:43:52
【问题描述】:

我看到了 position 属性,但我认为它只是用来设置 x 和 y,但我不知道如何识别当前位置。

或者完全如何使用onHover之类的事件收入属性?

【问题讨论】:

    标签: swift swiftui


    【解决方案1】:

    What is Geometry Reader in SwiftUI?,特别是关于GeometryGetter的讨论。如果您将GeometryGetter 放在ScrollView 内容的顶部,它将使用您传递给它的绑定发出其帧。此帧的原点将是滚动视图的负内容偏移量。

    【讨论】:

      【解决方案2】:

      在下面的示例中,您将看到如何使用GeometryReader 获取滚动视图中内容的水平位置。但是,我还没有成功找出如何设置滚动位置。 (Xcode 11.0 beta 6 (11M392q))

      struct TimelineView: View {
      
      
          @State private var posX: CGFloat = 0
      
      
          var body: some View {
      
              GeometryReader { geo in
      
                  VStack {
      
                      Text("\(self.posX)")
      
                      ScrollView(.horizontal, showsIndicators: true) {
      
                          VStack {
      
                              GeometryReader { innerGeo -> Text in
      
                                  self.posX = innerGeo.frame(in: .global).minX
      
                                  return Text("")
                              }
      
                              TimelineGridView()
                          }
                      }
                      .position(x: geo.size.width / 2, y: geo.size.height / 2)
                  }
              }
          }
      }
      

      地点:

      struct TimelineGridView: View {
      
      
          var body: some View {
      
              VStack {
      
                  ForEach(0...10, id: \.self) { rowIndex in
      
                      TimelineRowView()
                  }
              }
          }
      }
      
      struct TimelineRowView: View {
      
      
          var body: some View {
      
              HStack {
      
                  ForEach(0...100, id: \.self) { itemIndex in
      
                      TimelineCellView()
                  }
              }
          }
      }
      
      struct TimelineCellView: View {
      
      
          var body: some View {
      
              Rectangle()
                  .fill(Color.yellow)
                  .opacity(0.5)
                  .frame(width: 10, height: 10, alignment: .bottomLeading)
          }
      }
      ```
      

      【讨论】:

      • 适用于检测 Swift 5 中的滚动视图位置
      【解决方案3】:

      您可以通过@maxnatchanon 使用TrackableScrollView

      代码如下:

      //
      //  TrackableScrollView.swift
      //  TrackableScrollView
      //
      //  Created by Frad LEE on 2020/6/21.
      //  Copyright © 2020 Frad LEE. All rights reserved.
      //
      
      import SwiftUI
      
      /// A trackable and scrollable view. Read [this link](https://medium.com/@maxnatchanon/swiftui-how-to-get-content-offset-from-scrollview-5ce1f84603ec) for more.
      ///
      /// The trackable scroll view displays its content within the trackable scrollable content region.
      ///
      /// # Usage
      ///
      /// ``` swift
      /// struct ContentView: View {
      ///     @State private var scrollViewContentOffset = CGFloat(0) // Content offset available to use
      /// 
      ///     var body: some View {
      ///         TrackableScrollView(.vertical, showIndicators: false, contentOffset: $scrollViewContentOffset) {
      ///             ...
      ///         }
      ///     }
      /// }
      /// ```
      
      struct TrackableScrollView<Content>: View where Content: View {
          let axes: Axis.Set
          let showIndicators: Bool
          @Binding var contentOffset: CGFloat
          let content: Content
      
          /// Creates a new instance that’s scrollable in the direction of the given axis and can show indicators while scrolling.
          /// - Parameters:
          ///   - axes: The scrollable axes of the scroll view.
          ///   - showIndicators: A value that indicates whether the scroll view displays the scrollable component of the content offset, in a way that’s suitable for the platform.
          ///   - contentOffset: A value that indicates  offset of content.
          ///   - content: The scroll view’s content.
          init(_ axes: Axis.Set = .vertical, showIndicators: Bool = true, contentOffset: Binding<CGFloat>, @ViewBuilder content: () -> Content) {
              self.axes = axes
              self.showIndicators = showIndicators
              _contentOffset = contentOffset
              self.content = content()
          }
      
          var body: some View {
              GeometryReader { outsideProxy in
                  ScrollView(self.axes, showsIndicators: self.showIndicators) {
                      ZStack(alignment: self.axes == .vertical ? .top : .leading) {
                          GeometryReader { insideProxy in
                              Color.clear
                                  .preference(key: ScrollOffsetPreferenceKey.self, value: [self.calculateContentOffset(fromOutsideProxy: outsideProxy, insideProxy: insideProxy)])
                          }
                          VStack {
                              self.content
                          }
                      }
                  }
                  .onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in
                      self.contentOffset = value[0]
                  }
              }
          }
      
          private func calculateContentOffset(fromOutsideProxy outsideProxy: GeometryProxy, insideProxy: GeometryProxy) -> CGFloat {
              if axes == .vertical {
                  return outsideProxy.frame(in: .global).minY - insideProxy.frame(in: .global).minY
              } else {
                  return outsideProxy.frame(in: .global).minX - insideProxy.frame(in: .global).minX
              }
          }
      }
      
      struct ScrollOffsetPreferenceKey: PreferenceKey {
          typealias Value = [CGFloat]
      
          static var defaultValue: [CGFloat] = [0]
      
          static func reduce(value: inout [CGFloat], nextValue: () -> [CGFloat]) {
              value.append(contentsOf: nextValue())
          }
      }
      

      【讨论】:

      • 我认为您应该更清楚地表明这不是您的代码,而是从 GitHub 存储库中复制的 - 我知道您已将“by @maxnatchanon”放在顶部,但根本不清楚.我的印象是你写了它,并且正准备在一个项目中把它归功于你。
      【解决方案4】:

      如果您没有找到任何选项。您仍然可以将标准UIScrollViewdelegatesUIViewRepresentable 一起使用,方法是使单独的结构符合它。

      您可以在 SwiftUI 教程中找到更多详细信息: https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit

      【讨论】:

      • 谢谢。我不知道为什么我不能在我不能使用“扩展”的结构中使用这个例子。你能为我的案例写一个示例代码吗?请帮帮我
      【解决方案5】:

      我为此创建了 SwiftPackage(纯 SwiftUI ?)

      通过这个库,你可以得到ScrollView的位置。

      https://github.com/kazuooooo/PositionScrollView

      Medium post

      import Foundation
      import SwiftUI
      
      /// Extended ScrollView which can controll position
      public struct MinimalHorizontalExample: View, PositionScrollViewDelegate {
          /// Page size of Scroll
          var pageSize = CGSize(width: 200, height: 300)
          
          // Create PositionScrollViewModel
          // (Need to create in parent view to bind the state between this view and PositionScrollView)
          @ObservedObject var psViewModel = PositionScrollViewModel(
              pageSize: CGSize(width: 200, height: 300),
              horizontalScroll: Scroll(
                  scrollSetting: ScrollSetting(pageCount: 5, afterMoveType: .stickNearestUnitEdge),
                  pageLength: 200 // Page length of direction
              )
          )
         
          public var body: some View {
              return VStack {
                  PositionScrollView(
                      viewModel: self.psViewModel,
                      delegate: self
                  ) {
                      HStack(spacing: 0) {
                          ForEach(0...4, id: \.self){ i in
                              ZStack {
                                  Rectangle()
                                      .fill(BLUES[i])
                                      .border(Color.black)
                                      .frame(
                                          width: self.pageSize.width, height: self.pageSize.height
                                  )
                                  Text("Page\(i)")
                                      .foregroundColor(Color.white)
                                      .font(.system(size: 24, weight: .heavy, design: .default))
                              }
                          }
                          
                      }
                  }
                    // Get page via scroll object
                  Text("page: \(self.psViewModel.horizontalScroll?.page ?? 0)")
                    // Get position via scroll object
                  Text("position: \(self.psViewModel.horizontalScroll?.position ?? 0)")
              }
          }
          
          struct SampleView_Previews: PreviewProvider {
              static var previews: some View {
                  return MinimalHorizontalExample()
              }
          }
          
          // Delegate methods of PositionScrollView
            // You can monitor changes of position
          public func onScrollStart() {
              print("onScrollStart")
          }
          public func onChangePage(page: Int) {
              print("onChangePage to page: \(page)")
          }
          
          public func onChangePosition(position: CGFloat) {
              print("position: \(position)")
          }
          
          public func onScrollEnd() {
              print("onScrollEnd")
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2021-04-08
        • 1970-01-01
        • 2020-01-15
        • 1970-01-01
        • 2019-10-20
        • 1970-01-01
        • 1970-01-01
        • 2011-07-15
        相关资源
        最近更新 更多