我目前使用的是**Xcode 14.3.1版**(14E300c).

代码中的滚动视图有一个我用来迭代的对象数组ForEach().当用户滚动时,假设对象为30(出于演示目的,每个对象都有编号),锚值为0.00135(您可以将其视为相对于所述对象的偏移值),我希望用户能够在取消初始化视图之前返回到准确的位置;几乎与每个社交媒体应用程序完全一样.

我曾try 使用ScrollViewReader.scllTo(<id:,锚:)来完成此操作,但无济于事.它与预览完美配合,但我一使用模拟器或设备(IPhone14),它就开始随机调整我的ScrollView内容高度.

有没有人能解释一下,为什么这在预览版中可以完美地工作,但在设备上却不能;我如何才能在没有这个奇怪的错误的情况下在设备上工作?

因此,只有当您到达可滚动内容的末尾(底部)附近时,问题才会发生;奇怪的是,如果您滚动到可滚动内容的开头(顶部)附近,它会自动修复.

**// VIEW THAT IS PROBABLY CAUSING THE ISSUE**
struct ScrollViewWithSavedPosition: View {
    @ObservedObject var scrollViewSavedValue: ScrollViewSavedValue
    @State private var isViewLoaded: Bool = false
    let geoProxy: GeometryProxy
    let maxScrollableHeight: CGFloat = 4809
    
    var body: some View {
        ScrollViewReader { scrollProxy in
            ScrollView(showsIndicators: false) {
                LazyVStack {
                    ForEach(0..<34) { poster in
                        ZStack {
                            Rectangle()
                                .fill(.black)
                                .frame(width: geoProxy.size.width * 0.4,
                                       height: geoProxy.size.height * 0.2)
                            
                            Text("\(poster)")
                                .foregroundColor(.orange)
                        }
                    }
                }
                .id("scrollPosition")
                .background(
                    GeometryReader {
                        Color.orange
                            .preference(key: CGPointPK2.self,
                                        value: $0.frame(in: .global).origin)
                    }
                )
                .onPreferenceChange(CGPointPK2.self) { scrollPosition in
                    DispatchQueue.main.async {
                        isViewLoaded = true
                    }
                    
                    if isViewLoaded {
                        let offsetValue = (-1 * (scrollPosition.y - geoProxy.safeAreaInsets.top)) / maxScrollableHeight
                        scrollViewSavedValue.scrollOffsetValue = offsetValue
                    }
                    
                    print(scrollViewSavedValue.scrollOffsetValue)
                }
            }
            .onAppear {
                scrollProxy.scrollTo("scrollPosition", anchor: UnitPoint(x: 0, y: scrollViewSavedValue.scrollOffsetValue))
            }
        }
        .preferredColorScheme(.light)
    }
}

struct CGPointPK2: PreferenceKey {
    static var defaultValue: CGPoint = .zero
    static func reduce(value: inout CGPoint, nextValue: () -> CGPoint) { }
}

class ScrollViewSavedValue: ObservableObject {
    @Published var scrollOffsetValue: CGFloat = 0
    @Published var selectedTab: Int = 1
}

struct TabBarView: View {
    @ObservedObject var scrollViewSavedValue: ScrollViewSavedValue
    let geoProxy: GeometryProxy
    
    var body: some View {
        VStack {
            Spacer()
            
            ZStack {
                Rectangle()
                    .fill(.blue)
                    .frame(width: geoProxy.size.width, height: 80)
                
                HStack(spacing: 100) {
                    Button {
                        DispatchQueue.main.async {
                            scrollViewSavedValue.selectedTab = 1
                        }
                    } label: {
                        ZStack {
                            Circle()
                                .fill(.black)
                                .frame(width: 60, height: 60)
                            
                            Text("View 1")
                                .foregroundColor(.white)
                                .font(.system(size: 12))
                                .fontWeight(.bold)
                        }
                    }
                    
                    Button {
                        DispatchQueue.main.async {
                            scrollViewSavedValue.selectedTab = 2
                        }
                    } label: {
                        ZStack {
                            Circle()
                                .fill(.black)
                                .frame(width: 60, height: 60)
                            
                            Text("View 2")
                                .foregroundColor(.white)
                                .font(.system(size: 12))
                                .fontWeight(.bold)
                        }
                    }
                }
            }
        }
        .ignoresSafeArea()
    }
}

struct PresentationView: View {
    @StateObject private var scrollViewSavedValue = ScrollViewSavedValue()
    
    var body: some View {
        GeometryReader { geoProxy in
            switch scrollViewSavedValue.selectedTab {
            case 1:
                ScrollViewWithSavedPosition(scrollViewSavedValue: scrollViewSavedValue, geoProxy: geoProxy)
            default:
                Text("View 2")
                    .foregroundColor(.black)
            }
            
            TabBarView(scrollViewSavedValue: scrollViewSavedValue, geoProxy: geoProxy)
        }
    }
}

推荐答案

大家好,欢迎来到Stack Overflow,

在try 了一段时间后,我认为问题出在滚动高度不是恒定的这一事实上,这使得很难确定应该滚动到哪里.我最初以为可以通过数学计算出来,但经过一些测试后,我发现滚动高度不仅受顶部填充的影响,还受底部填充和选项卡栏的大小的影响.因此,每部手机都需要单独进行测试.

一个简单得多的解决方案是,当切换到视图2时,不删除滚动视图,我已将其重命名为ScrollPosterView.这可以通过使用opacitydisable修改器来完成.这确保滚动视图仍然被呈现,但不能被用户看到或操作.使用此方法,不需要保存滚动视图的滚动位置.由于这看起来像是一个社交媒体类型的应用程序,所以不需要重新加载图像,从而节省了处理能力.

这是这样做的(我简化了代码):

struct ContentView: View {

@State var selectedTab: Int = 1

var body: some View {
    VStack(spacing: 0) {
        ZStack {
            ScrollPosterView()
                .opacity(selectedTab == 1 ? 1 : 0)
                .disabled(selectedTab == 2)
            ScrollView {
                VStack {
                    Text("View 2")
                        .foregroundColor(.black)
                    Spacer()
                }
            }
            .disabled(selectedTab == 1)
            .opacity(selectedTab == 2 ? 1 : 0)

        }
    
        TabBarView(selectedTab: $selectedTab)
    }
}
}

其中,ScrollPosterView是:

struct ScrollPosterView: View {
var body: some View {
    ScrollView(showsIndicators: false) {
        LazyVStack(spacing: 10) {
            ForEach(0..<20) { poster in
                ZStack {
                    Rectangle()
                        .fill(.black)
                        .frame(width: 100,
                               height: 100)
                    
                    Text("\(poster)")
                        .foregroundColor(.orange)
                }
            }
        }
        .background(Color.orange)
    }
    .preferredColorScheme(.light)
}
}

TabBarView则是:

struct TabBarView: View {

@Binding var selectedTab: Int

var body: some View {
    ZStack {
        HStack(spacing: 100) {
            Button {
                selectedTab = 1
            } label: {
                ZStack {
                    Circle()
                        .fill(.black)
                        .frame(width: 60, height: 60)
                    
                    Text("View 1")
                        .foregroundColor(.white)
                        .font(.system(size: 12))
                        .fontWeight(.bold)
                }
            }
            
            Button {
                selectedTab = 2
            } label: {
                ZStack {
                    Circle()
                        .fill(.black)
                        .frame(width: 60, height: 60)
                    
                    Text("View 2")
                        .foregroundColor(.white)
                        .font(.system(size: 12))
                        .fontWeight(.bold)
                }
            }
        }
    }
    .frame(height: 70)
    .frame(maxWidth: .infinity)
    .background(Rectangle()
        .foregroundColor(.blue)
        .ignoresSafeArea())
}
}

Swift相关问答推荐

如何根据列表中的项目数量创建多个变量?

AirPods手势不发送AVAudioApp静音状态通知

数据不会被ARC删除在swift'

为什么我的引用类型对象在初始化后有3个强引用?

向文本元素添加背景色失败

是否可以循环遍历SWIFT子类并访问它们的静态变量S?

异步/等待函数的XCTAssertThrowsError

KMM-Swift铸造密封类

swift:只要框架名称中有一个带有框架名称的公共实体,就指定框架中公共 struct 的路径

我需要一些关于 SwiftUI 动画的帮助

从文字创建数组时的 Swift 宏

从 iPhone 中的安全飞地获取真正的随机数?

TextField 键入未完成

如何自己实现同一个 iOS 16 锁屏圆形小部件?

当使用 CGFloat 而不是数字文字时,为什么 node 的位置会发生变化?

Swift 中的Combine和didSet有什么区别?

带有屏幕参数的 NSWindow 初始化程序在初始化时导致致命错误

如何让不同的组件拥有不同的动画?

Swift:如何从函数返回类类型

如何快速从另一个视图控制器重新加载表格视图