在我的 SwiftUI 应用程序中,我在父视图中使用自定义工具栏。 该工具栏也适用于子视图。 当用户“完成”编辑时,我调用父级中的函数来执行一些任务。 然而,在孩子中,我不知道如何访问此事件来调用一些逻辑。 使用完成按钮不会调用子项的 onSubmit,因此我无法正确完成编辑。 您只能在创建工具栏的视图中访问工具栏的按钮事件吗?
如果我在子项上使用另一个工具栏,它会在工具栏本身上创建显示错误。
有从父级到子级的绑定,因此我无法将子视图创建为属性。 (即让 child = ChildView(),然后使用 child.alsoFinishedEditing())
我能想到的最接近的解决方案是在父级中创建一个 bool 标志,然后进行切换,监听子级中的 onChange 事件,但这似乎有点老套。
任何解决此问题的帮助将不胜感激。下面是最小的复制代码。
import SwiftUI
// In my parent view I'm handling some logic that needs a toolbar action
// when 'done'
struct ParentView: View {
// parent logic omitted
var body: some View {
VStack {
Text("Parent View")
.padding()
// child view that handles it's own state
ChildView()
.padding()
}
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
HStack {
Button(action: {
// how would I also call a function in the child?
finishEditing()
}, label: {
Text("Done").bold()
})
}
}
}
}
// Parent's function that gets called when Done is pressed
func finishEditing() {
// Parent logic omitted
print("Done Editing")
// How would I call a method in the child view?
}
}
struct ChildView: View {
@State var inputText: String = ""
var body: some View {
VStack {
// for this field the 'done' but is also present
// but when you tap it the onSubmit get's bypassed
// and we don't correctly finish our editing logic
TextField("Child Input", text: $inputText)
.padding()
.onSubmit {
alsoFinishedEditing()
}
}
}
func alsoFinishedEditing() {
// how can I access the 'done' button
// event from this child
}
}
如果我理解正确,您希望每个视图都有自己的方式来处理父视图添加的“完成”按钮的点击。
这可以通过自定义
PreferenceKey
来完成。每个视图都有自己的“点击完成后要做什么”的偏好。
struct DoneActionKey: PreferenceKey {
static let defaultValue: @MainActor () -> Void = {}
static func reduce(value: inout @MainActor () -> Void, nextValue: () -> @MainActor () -> Void) {
let curr = value
let next = nextValue()
value = {
curr()
next()
}
}
}
注意
curr()
和next()
的顺序。这决定了同级视图的“点击完成后要做什么”首选项的执行顺序。
然后您可以编写一个视图修饰符来更改该首选项,以便每个子视图都可以指定它们想要执行的操作。
extension View {
func onDone(_ action: @MainActor @escaping () -> Void) -> some View {
self.onSubmit {
action()
}
.transformPreference(DoneActionKey.self) { value in
let curr = value
value = {
action()
curr()
}
}
}
}
我也调用了
onSubmit
中的操作。如果这不是您想要的,您可以将其删除。再次注意 action()
和 curr()
的顺序。这决定了父视图和子视图的首选项的运行顺序。通过将 action()
放在第一位,父视图的操作将在子视图的操作之前运行,因为 value
参数代表 child 的 首选项,而不是父视图的首选项。
用途:
struct ParentView: View {
var body: some View {
VStack {
Text("Parent View")
.padding()
ChildView()
.padding()
}
.onDone {
finishEditing()
}
// add the toolbar via a backgroundPreferenceValue, so we have access to the preference value
.backgroundPreferenceValue(DoneActionKey.self) { doneAction in
Color.clear
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
HStack {
Button(action: {
doneAction()
}, label: {
Text("Done").bold()
})
}
}
}
}
}
func finishEditing() {
print("Done Editing")
}
}
struct ChildView: View {
@State var inputText: String = ""
var body: some View {
VStack {
TextField("Child Input", text: $inputText)
.padding()
.onDone {
alsoFinishedEditing()
}
}
}
func alsoFinishedEditing() {
print("Child Finishes Editing")
}
}
现在点击“完成”将打印
Done Editing
Child Finishes Editing