【问题标题】:SwiftUI: onChange with @Binding from outside viewSwiftUI:从外部查看带有@Binding 的 onChange
【发布时间】:2022-01-09 14:18:31
【问题描述】:

已更新以提供完整的可重现示例。

我有一个视图MainView 有两个子视图MainTextViewTableOfContentsView。我想在TableOfContentsView 中选择一个项目,它会触发MainTextView 中的滚动位置变化。

我在MainView 中有一个@State var scrollPosition: Int,它被传递给MainTextView 中的@Binding var scrollPosition: Int。这是我的 3 个视图的代码。

struct MainView: View {

    @State private var showingToc = false
    @State private var scrollPosition = 0

    var body: some View {
        
        VStack(alignment: .leading) {
            
            Text("Table of Contents (chapter \(scrollPosition + 1))")
                .onTapGesture {
                    showingToc = !showingToc
                }

            Divider()

            if showingToc {
                TableOfContentsView(
                    onChapterSelected: { chapter in
                        scrollPosition = chapter
                        showingToc = false
                    }
                )
            } else {
                MainTextView(scrollPosition: $scrollPosition)
            }
        }
    }
}
struct TableOfContentsView: View {

    var onChapterSelected: (Int) -> Void
    
    var body: some View {
        ScrollView {
            VStack {
                ForEach(0 ..< 20) { i in
                    Text("Chapter \(i + 1)")
                    .onTapGesture {
                        onChapterSelected(i)
                    }
                }
            }
        }
    }
}
struct MainTextView: View {
    
    let lorumIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."

    @Binding var scrollPosition: Int
    @State private var scrollProxy: ScrollViewProxy? = nil

    var body: some View {
        ScrollView {
            ScrollViewReader { proxy in
                
                VStack(alignment: .leading) {
                    ForEach(0 ..< 20) { i in
                        VStack(alignment: .leading) {
                            
                            Text("Chapter \(i + 1)")
                                .frame(maxWidth: .infinity, alignment: .center)
                                .padding(.top)

                            Text(lorumIpsum)
                                .font(.system(size: 18, design: .serif))
                                .padding(.top)
                        }
                        .padding(.bottom, 20)
                        .id(i)
                    }
                }
                .padding(.leading)
                .padding(.trailing)
                .onAppear {
                    scrollProxy = proxy
                }
            }
        }
        .onChange(of: scrollPosition) { target in
            scrollProxy?.scrollTo(target, anchor: .top)
        }
    }
}

我的 .onChange 函数永远不会被调用 - 如果我在里面放置一个断点,它永远不会到达断点。我在这里想念什么?如何观察从视图外部更改的@Binding

【问题讨论】:

  • 需要最小可重现示例
  • 这个方法---- onChange(of: scrollPosition) { target in proxy.scrollTo(target, anchor: .top) } in MainTextView会在滚动位置改变时触发。在此块之外使用 proxy.scrollTo。
  • 已更新以提供可重现的示例
  • @MattRobertson 可重现的示例按预期滚动。您是在设备/模拟器上运行还是使用 Swift 预览?请注意,在最近的 Xcode 更新中,断点支持已从 Swift 预览中删除。除了断点,还有其他一些你期待的行为吗?
  • @mani 我在 iPhone 13 模拟器上运行。当我选择一个新章节时,Binding 值会更新,但滚动位置不会改变,如果我在调用滚动位置更改的行上放置断点,则永远不会到达。

标签: swift swiftui onchange


【解决方案1】:

.onChange 不会被调用,因为 MainTextViewshowingToc = true 时不存在,因为条件块:

if showingToc {
  TableOfContentsView(onChapterSelected: { chapter in
      scrollPosition = chapter
      showingToc = false
    }
  )
} else {
  MainTextView(scrollPosition: $scrollPosition)
}

您可能需要考虑将 TOC 显示为叠加层。但要让您当前的代码正常工作,您需要在 .onAppearMainTextView 中调用 proxy.scrollTo

.onAppear {
  scrollProxy = proxy
  scrollProxy?.scrollTo(scrollPosition, anchor: .top)
}

【讨论】:

  • 谢谢,不过我确实有这个。我更新了我的 OP 以提供一个完整的工作示例,表明从未调用过 .onChange
  • 啊,太棒了,谢谢。我已经习惯了 Android 上的 Jetpack Compose,并且正在学习 SwiftUI。这是 Compose 的纯函数式方法和 SwiftUI 的基于结构的方法之间的一个有趣的区别。
  • 我对 Jetpack Compose 没有任何经验 - 但我想知道如果您将 MainTextView 的实例保留为成员并始终重复使用相同的实例会发生什么。或者您可以将TableOfContentsView 放在MainTextView 上方的ZStack 中。
  • 是的,我最终选择了 ZStack 方法。感谢您的建议!
猜你喜欢
  • 1970-01-01
  • 2017-02-09
  • 1970-01-01
  • 2016-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-26
  • 2019-10-22
相关资源
最近更新 更多