如何在 SwiftUI 中创建同步 ScrollView

Dun*_*ald 5 scrollview swiftui

我正在尝试在 SwiftUI 中创建两个同步ScrollViews,这样在一个中滚动将导致在另一个中进行相同的滚动。

我正在使用ScrollViewOffset底部显示的类来获取滚动视图偏移值,但无法弄清楚如何滚动其他视图。

我似乎能够通过防止在一个视图中滚动并在另一个视图上设置内容位置()来“破解”它 - 有什么方法可以实际将滚动视图内容滚动到某个位置 - 我知道 ScrollViewReader 似乎允许滚动显示内容项,但我似乎找不到任何可以将内容滚动到偏移位置的内容。

使用position()的问题是它实际上并没有改变ScrollViews滚动条的位置——似乎没有ScrollView.scrollContentsTo(point: CGPoint)。

 @State private var scrollOffset1: CGPoint = .zero 

 HStack {
   ScrollViewOffset(onOffsetChange: { offset in
                    scrollOffset1 = offset
                    print("New ScrollView1 offset: \(offset)")
                }, content: {
                    
                    VStack {
                        
                        ImageView(filteredImageProvider: self.provider)
                            .frame(width: imageWidth, height: imageHeight)
                    }
                    .frame(width: imageWidth + (geometry.size.width - 20) * 2, height: imageHeight + (geometry.size.height - 20) * 2)
                    .border(Color.white)
                    .id(0)
   })

   ScrollView([]) {
                
                VStack {
                    
                    ImageView(filteredImageProvider: self.provider, showEdits: false)
                        .frame(width: imageWidth, height: imageHeight)
                }
                .frame(width: imageWidth + (geometry.size.width - 20) * 2, height: imageHeight + (geometry.size.height - 20) * 2)
                .border(Color.white)
                .id(0)
                .position(x: scrollOffset1.x, y: scrollOffset1.y + (imageHeight + (geometry.size.height - 20) * 2)/2)
                
     }

}


//
//  ScrollViewOffset.swift
//  ZoomView
//
//

import Foundation
import SwiftUI

struct ScrollViewOffset<Content: View>: View {
    let onOffsetChange: (CGPoint) -> Void
    let content: () -> Content
    
    init(
        onOffsetChange: @escaping (CGPoint) -> Void,
        @ViewBuilder content: @escaping () -> Content
    ) {
        self.onOffsetChange = onOffsetChange
        self.content = content
    }
    
    var body: some View {
        ScrollView([.horizontal, .vertical]) {
            offsetReader
            content()
                .padding(.top, -8)
        }
        .coordinateSpace(name: "frameLayer")
        .onPreferenceChange(ScrollOffsetPreferenceKey.self, perform: onOffsetChange)
    }
    
    var offsetReader: some View {
        GeometryReader { proxy in
            Color.clear
                .preference(
                    key: ScrollOffsetPreferenceKey.self,
                    value: proxy.frame(in: .named("frameLayer")).origin
                )
        }
        .frame(width: 0, height: 0)
    }
}

private struct ScrollOffsetPreferenceKey: PreferenceKey {
    static var defaultValue: CGPoint = .zero
    static func reduce(value: inout CGPoint, nextValue: () -> CGPoint) {}
}
Run Code Online (Sandbox Code Playgroud)

小智 11

同步滚动视图。

在此示例中,您可以滚动左侧滚动视图,右侧滚动视图将同步到同一位置。在此示例中,右侧的滚动视图被禁用,并且位置仅通过使用偏移量进行同步。

但是使用相同的逻辑和代码,您可以使左右滚动视图在滚动时同步。

import SwiftUI

struct ContentView: View {
    
    @State private var offset = CGFloat.zero
    
    var body: some View {
        
        HStack(alignment: .top) {
            
            // MainScrollView
            ScrollView {
                VStack {
                    ForEach(0..<100) { i in
                        Text("Item \(i)").padding()
                    }
                }
                .background( GeometryReader {
                    Color.clear.preference(key: ViewOffsetKey.self,
                                           value: -$0.frame(in: .named("scroll")).origin.y)
                })
                .onPreferenceChange(ViewOffsetKey.self) { value in
                    print("offset >> \(value)")
                    offset = value
                }
            }
            .coordinateSpace(name: "scroll")
            
            
            // Synchronised with ScrollView above
            ScrollView {
                VStack {
                    ForEach(0..<100) { i in
                        Text("Item \(i)").padding()
                    }
                }
                .offset(y: -offset)
            }
            .disabled(true)
        }
    }
}

struct ViewOffsetKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}
Run Code Online (Sandbox Code Playgroud)