SwiftUI ScrollView content frame and offset

。_饼干妹妹 提交于 2020-05-17 03:55:47

问题


Can someone explain the behavior of this ScrollView test code -- why I can't scroll to the left and top edges, and why I can scroll beyond the right and bottom edges? And how to fix this. (Note that if we remove VStack the behavior does not change.)

    var body: some View {

        ScrollView.init([.vertical,.horizontal]) {

            VStack {
                Text("AAA")
                    .padding(8)
                    .frame(width: 1024, height: 1024)
                    .background(Color.orange)
                    .border(Color.red)
            }

        }
        .border(Color.blue)

    }

In this image is as far to the left and up as I can scroll -- it doesn't scroll to the edges:

But here I can scroll beyond the bottom and right edges:


回答1:


I have adapted @Asperi's answer to another question (SwiftUI How to align a view that's larger than screen width), and this works.

    @State private var offset : CGPoint = .zero

    var body: some View {

        ScrollView.init([.vertical,.horizontal]) {

            VStack {
                Text("AAA")
                    .padding(8)
                    .frame(width: 1024, height: 1024)
                    .background(Color.orange)
                    .border(Color.red)
            }
            .background(rectReader())
            .offset(x: self.offset.x, y: self.offset.y)

        }
        .border(Color.blue)

    }

    func rectReader() -> some View {
        return GeometryReader { (geometry) -> AnyView in
            let offset : CGPoint = CGPoint.init(
                x: -geometry.frame(in: .global).minX,
                y: -geometry.frame(in: .global).minY
            )
            if self.offset == .zero {
                DispatchQueue.main.async {
                    self.offset = offset
                }
            }
            return AnyView(Rectangle().fill(Color.clear))
        }
    }



回答2:


Or you can try to use one ScrollView for each direction, try this

import SwiftUI

struct ContentView: View {
    var body: some View {

        ScrollView(.horizontal, showsIndicators: true) {
            ScrollView(.vertical, showsIndicators: true) {
                Color.yellow.frame(width: 1024, height: 1024)
                    .overlay(Text("A").font(.largeTitle), alignment: .topLeading)
                    .overlay(Text("B").font(.largeTitle), alignment: .topTrailing)
                    .overlay(Text("C").font(.largeTitle), alignment: .bottomTrailing)
                    .overlay(Text("D").font(.largeTitle), alignment: .bottomLeading)
            }
        }

    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

UPDATE

to be able to see, what happens using .offset modifier, try imagine the result of this snippet

import SwiftUI

struct ContentView: View {
    var body: some View {

        HStack {
            Text("Hello").padding().background(Color.yellow).offset(x: 20, y: 40).border(Color.red)
            Text("World").padding().background(Color.pink).offset(x: -10, y: -10).border(Color.yellow)
            }.padding().border(Color.gray)

    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}




回答3:


I came across this question and answers as I was today looking for a solution of this "content view miss-placement problem" for many hours as I'm trying to scroll a (zoomed) image in any direction.

Neither of the solutions proposed here are "nice" or applicable in my case. But after finding a solution for myself, I want to document here how I would solve your problem.

The main problem is that the center of your content view (=VStack with Text element) is aligned with the center of the ScrollView. This is always the case: whether the content size is smaller or bigger than the ScrollView size. (Remember: The ScrollView size corresponds to the screen size, minus safe area edge insets). To align your content views top leading edge with the scrollviews top leading edge, you will have to offset your content by the amount which it is currently overlapping on the left side.

The good thing: it can be calculated directly using the GeometryReader and the known content size.

    var body: some View {
        GeometryReader { proxy -> AnyView in

            let insets = proxy.safeAreaInsets
            let sw = proxy.size.width + insets.leading + insets.trailing
            let sh = proxy.size.height + insets.top + insets.bottom

            let contentSize : CGFloat = 1024
            let dx: CGFloat = (contentSize > sw ? (contentSize - sw)/2 : 0.0)
            let dy: CGFloat = (contentSize > sh ? (contentSize - sh)/2 : 0.0)

            return AnyView(
                ScrollView([.vertical,.horizontal]) {
                    VStack {
                        Text("AAA")
                            .padding(8)
                            .frame(width: 1024, height: 1024)
                            .background(Color.orange)
                            .border(Color.red)
                    }
                    .offset(x: dx, y: dy)
                }
                .border(Color.blue)
            )
        }
    }


来源:https://stackoverflow.com/questions/60570282/swiftui-scrollview-content-frame-and-offset

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!