【问题标题】:Getting and setting the position of content in a SwiftUI ScrollView在 SwiftUI ScrollView 中获取和设置内容的位置
【发布时间】:2020-01-15 16:54:05
【问题描述】:

我有一个水平滚动视图,我想以编程方式设置一个位置。这是视图的主体:

let radius = CGFloat(25)
let scrollWidth = CGFloat(700)
let scrollHeight = CGFloat(100)
let spacing = CGFloat(20)
let circleDiameter = CGFloat(50)

...

    var body: some View {
        GeometryReader { viewGeometry in
            ScrollView () {
                VStack(spacing: 0) {
                    Spacer(minLength: spacing)

                    Circle()
                        .fill(Color.black.opacity(0.5))
                        .scaledToFit()
                        .frame(width: circleDiameter, height: circleDiameter)

                    ScrollView(.horizontal) {
                        Text("The quick brown fox jumps over the lazy dog. Finalmente.")
                            .font(Font.title)
                            .frame(width: scrollWidth, height: scrollHeight)
                            .foregroundColor(Color.white)
                            .background(Color.white.opacity(0.25))
                    }
                    .frame(width: viewGeometry.size.width, height: scrollHeight)
                    .padding([.top, .bottom], spacing)

                    Circle()
                        .fill(Color.white.opacity(0.5))
                        .scaledToFit()
                        .frame(width: circleDiameter, height: circleDiameter)

                    Spacer(minLength: spacing)
                }
                .frame(width: viewGeometry.size.width)
            }
            .background(Color.orange)
        }
        .frame(width: 324 / 2, height: spacing * 4 + circleDiameter * 2 + scrollHeight) // testing
        .cornerRadius(radius)
        .background(Color.black)
    }

如何更改此代码,以便我可以获取“The quick brown fox”的当前位置并在以后恢复它?我只是在尝试做一些我们一直在 UIKit 中使用 contentOffset 所做的事情。

我可以看到GeometryReader 可能对获取内容的当前帧很有用,但没有等效的编写器。为滚动视图或文本设置 .position().offset() 也没有让我有任何收获。

任何帮助将不胜感激!

【问题讨论】:

标签: uiscrollview swiftui


【解决方案1】:

我一直在尝试一个解决方案,并发布了我在以编程方式设置内容偏移方面所做工作的要点https://gist.github.com/jfuellert/67e91df63394d7c9b713419ed8e2beb7

【讨论】:

    【解决方案2】:

    使用常规的 SwiftUI ScrollView,据我所知,您可以使用 GeometryReader 和 proxy.frame(in: .global).minY 获得位置(请参阅下面的修改示例),但您不能设置“contentOffset ”。

    实际上,如果您查看 Debug View Hierarchy,您会注意到我们的内容视图嵌入到滚动视图的内部 SwiftUI 其他内容视图中。所以你将抵消这个内部视图而不是滚动视图。

    搜索了很长一段时间后,我找不到任何使用 SwiftUI ScrollView 的方法(我想这将不得不等待 Apple)。我能做的最好的事情(使用黑客)是滚动到底部。

    更新:我之前犯了一个错误,因为它是在垂直滚动上。现在更正了。

    class SGScrollViewModel: ObservableObject{
        var scrollOffset:CGFloat = 0{
            didSet{
                print("scrollOffset: \(scrollOffset)")
            }
        }
    
    }
    
    
    struct ContentView: View {
        public var scrollModel:SGScrollViewModel = SGScrollViewModel()
    
        let radius = CGFloat(25)
         let scrollWidth = CGFloat(700)
         let scrollHeight = CGFloat(100)
         let spacing = CGFloat(20)
         let circleDiameter = CGFloat(50)
    
             var body: some View {
                var topMarker:CGFloat = 0
                let scrollTopMarkerView =  GeometryReader { proxy -> Color in
                    topMarker = proxy.frame(in: .global).minX
                    return Color.clear
                }
                let scrollOffsetMarkerView =  GeometryReader { proxy -> Color in
                    self.scrollModel.scrollOffset = proxy.frame(in: .global).minX - topMarker
                    return Color.clear
                }
    
                return GeometryReader { viewGeometry in                    
                     ScrollView () {
                         VStack(spacing: 0) {
    
                            Spacer(minLength: self.spacing)
    
                             Circle()
                                 .fill(Color.black.opacity(0.5))
                                 .scaledToFit()
                                .frame(width: self.circleDiameter, height: self.circleDiameter)
                             scrollTopMarkerView.frame(height:0)
                             ScrollView(.horizontal) {
                                 Text("The quick brown fox jumps over the lazy dog. Finally.")
                                     .font(Font.title)
                                    .frame(width: self.scrollWidth, height: self.scrollHeight)
                                     .foregroundColor(Color.white)
                                     .background(Color.white.opacity(0.25))
                                     .background(scrollOffsetMarkerView)
                             }
                             .frame(width: viewGeometry.size.width, height: self.scrollHeight)
                             .padding([.top, .bottom], self.spacing)
    
                             Circle()
                                 .fill(Color.white.opacity(0.5))
                                 .scaledToFit()
                                .frame(width: self.circleDiameter, height: self.circleDiameter)
    
                            Spacer(minLength: self.spacing)
                         }
                         .frame(width: viewGeometry.size.width)
                     }
                     .background(Color.orange)
                 }
                 .frame(width: 324 / 2, height: spacing * 4 + circleDiameter * 2 + scrollHeight) // testing
                 .cornerRadius(radius)
                 .background(Color.black)
             }
    }
    

    【讨论】:

      【解决方案3】:

      我搞砸了几个解决方案,其中涉及一个ScrollView 和一个或多个GeometryReaders,但最终我发现如果我忽略ScrollView 并使用View.offset(x:y:)DragGesture 推出自己的解决方案,一切都会变得更容易:

      这允许通过像ScrollView 一样拖动LinearGradient 或通过更新绑定来平移LinearGradient,在这种情况下通过Slider@

      struct SliderBinding: View {
          @State var position = CGFloat(0.0)
          @State var dragBegin: CGFloat?
      
          var body: some View {
              VStack {
                  Text("\(position)")
                  Slider(value: $position, in: 0...400)
                  ZStack {
                      LinearGradient(gradient: Gradient(colors: [.blue, .red]) , startPoint: .leading, endPoint: .trailing)
                          .frame(width: 800, height: 200)
                          .offset(x: position - 400 / 2)
                  }.frame(width:400)
                  .gesture(DragGesture()
                              .onChanged { gesture in
                                  if (dragBegin == nil) {
                                      dragBegin = self.position
                                  } else {
                                      position = (dragBegin ?? 0) + gesture.translation.width
                                  }
                              }
                              .onEnded { _ in
                                  dragBegin = nil
                              }
                  )
              }
              .frame(width: 400)
          }
      }
      
      

      为简洁起见,省略了将拖动操作限制为滚动区域的大小。这段代码允许水平滚动,使用CGPoints而不是CGSize来实现水平和垂直。

      【讨论】:

        猜你喜欢
        • 2019-10-20
        • 1970-01-01
        • 2021-04-08
        • 1970-01-01
        • 2020-05-10
        • 1970-01-01
        • 2022-12-23
        • 1970-01-01
        相关资源
        最近更新 更多