【问题标题】:SwiftUI pass scroll location up the View hierarchySwiftUI 将滚动位置向上传递到视图层次结构
【发布时间】:2021-02-03 07:52:15
【问题描述】:

我有一个 SwiftUI 应用程序。它有一个 ScrollView 和一个 Text,我希望 Text 显示 ScrollView 中元素的位置。

struct LinkedScrolling: View {
    @State var scrollPosition: CGFloat = 0

    var body: some View {
        VStack {
            Text("\(self.scrollPosition)")
            ScrollContent(scrollPosition: self.$scrollPosition)
        }
    }
}

此视图包含文本和滚动内容。这是滚动内容:

struct ScrollContent: View {
    @Binding var scrollPosition: CGFloat

    var body: some View {
        ScrollView(.horizontal) {
            GeometryReader { geometry -> AnyView in
                self.scrollPosition = geometry.frame(in: .global).minX

                let view = AnyView(HStack(spacing: 20) {
                        Rectangle()
                            .fill(Color.blue)
                            .frame(width: 400, height: 200)
                        Rectangle()
                            .fill(Color.red)
                            .frame(width: 400, height: 200)
                        Rectangle()
                        .fill(Color.green)
                            .frame(width: 400, height: 200)
                        Rectangle()
                        .fill(Color.orange)
                            .frame(width: 400, height: 200)
                        Rectangle()
                        .fill(Color.pink)
                            .frame(width: 400, height: 200)

                })
                return view
            }.frame(width: 5*400+4*20)
        }
    }
}

State 变量 scrollPosition 会在每次 ScrollView 中的元素移动时更新。 在 iOS 14.2 模拟器中使用该应用时,scrollPosition 不会更改,并且控制台会记录 [SwiftUI] Modifying state during view update, this will cause undefined behavior.

真正让我感到困惑的是,它可以在 Xcode 预览画布和 State 变量中工作,并且 Text 会按照我的意愿更改。

是否可以通过这种方式更改 State 变量? 如果是,我该如何尝试使其在模拟器上运行? 如果没有,还有其他方法可以实现我的目标吗?

感谢您的帮助!

【问题讨论】:

  • 我怀疑它是正确的方式,至少它会与用户交互冲突并可能有未来的错误。接下来应该会有帮助(它是垂直的,但水平的方法是一样的)stackoverflow.com/a/65871577/12299030.

标签: ios swift swiftui


【解决方案1】:

您可以使用DispatchQueue.main.async

DispatchQueue.main.async {
    self.scrollPosition = geometry.frame(in: .global).minX
}

或者更好的方式是使用.onReceive

这样

struct ScrollContent: View {
    @Binding var scrollPosition: CGFloat

    var body: some View {
        ScrollView(.horizontal) {
            GeometryReader { geometry in
                HStack(spacing: 20) {
                        Rectangle()
                            .fill(Color.blue)
                            .frame(width: 400, height: 200)
                        Rectangle()
                            .fill(Color.red)
                            .frame(width: 400, height: 200)
                        Rectangle()
                        .fill(Color.green)
                            .frame(width: 400, height: 200)
                        Rectangle()
                        .fill(Color.orange)
                            .frame(width: 400, height: 200)
                        Rectangle()
                        .fill(Color.pink)
                            .frame(width: 400, height: 200)

                }.onReceive(Just(geometry), perform: { _ in //<-- Here
                    self.scrollPosition = geometry.frame(in: .global).minX
                })
            }.frame(width: 5*400+4*20)
        }
    }
}

【讨论】:

  • 感谢您,使用 Combine 的解决方案非常有效!
猜你喜欢
  • 2023-02-12
  • 2020-07-27
  • 1970-01-01
  • 2020-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多