在SwiftUI ScrollView中获取和设置内容的位置

时间:2019-09-13 20:26:29

标签: uiscrollview swiftui

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

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)
    }

如何更改此代码,以便可以获取“快速褐狐”的当前位置并在以后恢复它?我只是想做一些像UIKit中contentOffset一样的事情。

我可以看到GeometryReader可能对于获取内容的当前框架有用,但是没有等效的作者。为滚动视图或文本设置.position().offset()也不会让我感到困惑。

任何帮助将不胜感激!

3 个答案:

答案 0 :(得分:3)

我一直在研究一个解决方案,并就通过编程设置内容偏移量https://gist.github.com/jfuellert/67e91df63394d7c9b713419ed8e2beb7

发布了我的工作要点。

答案 1 :(得分:1)

我弄乱了涉及一个ScrollView和一个或多个GeometryReaders的几种解决方案,但是最终,如果我只是忽略ScrollView并使用{{1} }和View.offset(x:y:)

在通过DragGesture的情况下,这可以通过像拖动LinearGradient一样拖动ScrollView或更新绑定来进行平移

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) } } 而不是CGPoint进行水平和垂直滚动。

答案 2 :(得分:0)

据我所知,使用常规的SwiftUI ScrollView,您可以通过带有proxy.frame(in.global).minY的GeometryReader获得位置(请参见下面的修改示例),但是您无法设置“ contentOffset ”。

实际上,如果您查看“调试视图层次结构”,您会注意到我们的内容视图嵌入到内部SwiftUI的其他内容视图中(滚动视图)。因此,您将相对于此内部视图而不是滚动视图进行偏移。

搜索了一段时间后,我找不到使用SwiftUI ScrollView进行操作的任何方法(我猜这将不得不等待Apple)。我能做的(用hacks)最好的方法是滚动到底部。

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

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)
         }
}