3

I need to create an infinite horizontal scrolling with ScrollView to scroll a panorama image. So I would like to make this scrolling infinite from both sides, is there any possible way to make it? I have searched maybe using ScrollViewReader can achieve this but no luck.

ScrollView(.horizontal, showsIndicators: false) {
    Image("panorama")
        .resizable()
        .scaledToFill()
} 
2
  • Do you mean 360º panorama image? Please attach a demo of image. Commented Jun 6, 2022 at 6:55
  • SceneKit might be better for this than SwiftUI. Commented Jun 8, 2022 at 14:06

2 Answers 2

2
+200

Actually for such prepared image it is enough to load it only once, and we need only to Image presenters for it (note: they do not copy memory, but use the same loaded image). Everything else is just about layout on the fly moving current of-screen item depending on drag direction to left or to the right of current one.

Tested with Xcode 13.4 / iOS 15.5

demo

Main part of code:

struct PanoramaView: View {
    let image: UIImage

    private struct Item: Identifiable, Equatable {
        let id = UUID()
        var pos: CGFloat!
    }

    @State private var items = [Item(), Item()]

    // ...

    var body: some View {
        GeometryReader { gp in
            let centerY = gp.size.height / 2
            ForEach($items) { $item in
                Image(uiImage: image)
                    .resizable().aspectRatio(contentMode: .fill)
                    .position(x: item.pos ?? 0, y: centerY)
                    .offset(x: dragOffset)
            }
        }
        .background(GeometryReader {
            Color.clear.preference(key: ViewSizeKey.self,
                                   value: $0.frame(in: .local).size)
        })
        .onPreferenceChange(ViewSizeKey.self) {
            setupLayout($0)
        }
        .contentShape(Rectangle())
        .gesture(scrollGesture)
    }

and usage

    let panorama = UIImage(contentsOfFile: Bundle.main.path(forResource: "panorama", ofType: "jpeg")!)!
    var body: some View {
        PanoramaView(image: panorama)
            .frame(height: 300)   // to demo of dynamic internal layout
    }

Complete test code is here

Sign up to request clarification or add additional context in comments.

3 Comments

It is a custom preference key, the declaration is present in referenced project.
It's works great! Just one thing is there any possible way make gesture scrolls smoothly like scrollview instead of stop after dragging?
Actually it is all about animation parameters. We do not know internal ScrollView settings, but you can configure to your own needs manipulating with animation duration and after-drag remaining distance to ease out.
0

You can put 3 identical panorama images next to each other (to be able to scroll over the edges) and add a custom DragGesture, that basically jumps back to the relative position of the middle image.

This image is a bad example, obviously it will work better with a real 360° image ;)

EDIT:
now with predicted end location + animation and 5 images.

enter image description here

struct ContentView: View {
    
    @State private var dragOffset = CGFloat.zero
    @State private var offset = CGFloat.zero

    let imageWidth: CGFloat = 500
    
    var body: some View {
        HStack(spacing: 0) {
            ForEach(0..<5) { _ in
                Image("image")
                    .resizable()
                    .frame(width: imageWidth)
            }
        }
        .offset(x: offset + dragOffset)
        .gesture(
            DragGesture()
                .onChanged({ value in
                    dragOffset = value.translation.width
                })
                .onEnded({ value in
                    withAnimation(.easeOut) {
                        dragOffset = value.predictedEndTranslation.width
                    }
                    offset = (offset + dragOffset).remainder(dividingBy: imageWidth)
                    dragOffset = 0
                })
        )
    }
}

6 Comments

What about it reaches the end of the third image? by the way I need scroll view behavior
it will never reach any end, because it always jumps back to the middle image. And what do you mean by "scrollview behaviour"?
Thank you I will test it, I mean smooth scrolling
ah ... scroll to predicted end location with animation ... that can also be done with DragGesture, but might get a little trickier. You might then use 5 images overall, and wait for the animation to stop before jumping back to the middle.
See edited code – seems it is NOT necessary to wait for animation to end.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.