SwiftUI:从外部视图使用@Binding onChange
SwiftUI: onChange with @Binding from outside view
已更新以提供完整的可重现示例。
我有一个视图 MainView
,它有两个子视图,MainTextView
和 TableOfContentsView
。我想要 select 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
不会被调用,因为 MainTextView
在 showingToc = true
时不存在,因为条件块:
if showingToc {
TableOfContentsView(onChapterSelected: { chapter in
scrollPosition = chapter
showingToc = false
}
)
} else {
MainTextView(scrollPosition: $scrollPosition)
}
您可能要考虑将 TOC 显示为叠加层。但是要使您当前的代码正常工作,您需要在 MainTextView
的 .onAppear
中调用 proxy.scrollTo
:
.onAppear {
scrollProxy = proxy
scrollProxy?.scrollTo(scrollPosition, anchor: .top)
}
已更新以提供完整的可重现示例。
我有一个视图 MainView
,它有两个子视图,MainTextView
和 TableOfContentsView
。我想要 select 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
不会被调用,因为 MainTextView
在 showingToc = true
时不存在,因为条件块:
if showingToc {
TableOfContentsView(onChapterSelected: { chapter in
scrollPosition = chapter
showingToc = false
}
)
} else {
MainTextView(scrollPosition: $scrollPosition)
}
您可能要考虑将 TOC 显示为叠加层。但是要使您当前的代码正常工作,您需要在 MainTextView
的 .onAppear
中调用 proxy.scrollTo
:
.onAppear {
scrollProxy = proxy
scrollProxy?.scrollTo(scrollPosition, anchor: .top)
}