SwiftUI:无限滚动水平 ScrollView

问题描述 投票:0回答:2

我需要使用

ScrollView
创建无限水平滚动来滚动全景图像。所以我想从两侧无限滚动,有什么办法可以做到吗?我已经搜索过也许使用
ScrollViewReader
可以实现这个但没有运气。

ScrollView(.horizontal, showsIndicators: false) {
    Image("panorama")
        .resizable()
        .scaledToFill()
} 
ios swift swiftui scrollview
2个回答
2
投票

实际上对于这样准备好的图像,只加载一次就足够了,我们只需要为它提供图像展示器(注意:他们不复制内存,而是使用相同的加载图像)。其他一切都只是关于动态移动当前屏幕项目的布局,具体取决于向左或向右拖动当前项目的方向。

使用 Xcode 13.4 / iOS 15.5 测试

代码主要部分:

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

和用法

    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
    }

完整的测试代码在这里


0
投票

您可以将 3 张相同的全景图像并排放置(以便能够在边缘上滚动)并添加自定义 DragGesture,这基本上会跳回中间图像的相对位置。

这张图片是一个不好的例子,显然它会在真实的 360° 图像上工作得更好;)

编辑
现在有预测的结束位置+动画和 5 张图片。

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
                })
        )
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.