在我的 iOS 应用程序中,我有一个 VStack,它由一个带有
List
的视图和一个带有 TextInput
的视图组成:
var body: some View {
NavigationView {
ScrollViewReader { scrollProxy in
VStack() {
NoteListView(notes: notes)
.onTapGesture { hideKeyboard() }
NoteInputView(scrollProxy: scrollProxy)
}
}
.navigationBarTitle(Text("Notes"))
}
}
func hideKeyboard() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
在我的
NoteListView
上,我附加了一个.swipeAction来删除注释:
List(notes) { note in
NotePreview(note: note)
.swipeActions(edge: .leading, allowsFullSwipe: true) {
Button(role: .destructive) {
delete(note)
} label: {
Label("Delete", systemImage: "trash")
}
.tint(.red)
}
}
虽然这成功隐藏了键盘,但它也阻止了滑动操作上的删除按钮识别点击手势。使用
.simultaneousGesture
并不能解决此问题,让删除按钮在点击时起作用的唯一方法是删除附加到父视图的任何点击手势 - 将 .highPriorityGesture
添加到按钮也不会触发。对我来说这似乎是一个 SwiftUI 错误。
我决定不再使用 VStack,而是改用 ZStack,只要键盘显示,它就会填满整个屏幕。当键盘显示时,Spacer 会捕获点击事件。当键盘未显示时,不应捕获敲击:
var body: some View {
NavigationView {
ScrollViewReader { scrollProxy in
ZStack() {
NoteListView(notes: notes)
NoteInputView(scrollProxy: scrollProxy)
}
}
.navigationBarTitle(Text("Notes"))
}
}
在我的
NoteInputView
中,我现在有 @FocusState
来跟踪 TextInput 是否具有焦点:
struct NoteInputView: View {
...
@FocusState var focusInputField: Bool
var body: some View {
ZStack(alignment: .bottom) {
if(focusInputField) {
Spacer()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.contentShape(Rectangle())
.simultaneousGesture(TapGesture().onEnded({ _ in
focusInputField = false
print("I'm still standing yeah yeah yeah")
print(focusInputField)
})).onTap
} else {
Spacer()
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
HStack() {
if(focusInputField) {
Button("", systemImage: "keyboard.chevron.compact.down") {
focusInputField = false
}
}
TextField("Enter a quick note...", text: $newNoteContent, axis: .vertical)
.lineLimit(1...5)
}
}
}
}
但是,这种方法并不可靠——有时,捕获点击的
Spacer
仍然会打印 I'm still standing yeah yeah yeah
,尽管 focusInputField
是假的。到目前为止,我还无法可靠地重现间隔物何时保留或何时消失。
很高兴听到其他解决方法,或关于为什么这可能不可靠的反馈。
虽然我仍然相信最初的问题(点击无法到达 SwipeAction 按钮)是 Apple 的一个错误,但至少我能够通过删除 else-conditional spacer 来修复解决方法。
在
ContentView
中,确保输入位于底部:
var body: some View {
NavigationView {
ScrollViewReader { scrollProxy in
ZStack() {
NoteListView(notes: notes)
NoteInputView(scrollProxy: scrollProxy)
.frame(maxHeight: .infinity, alignment: .bottom)
}
}
.navigationBarTitle(Text("Notes"))
}
}
然后,在 NoteInputView 中,删除 else-条件:
struct NoteInputView: View {
...
@FocusState var focusInputField: Bool
var body: some View {
ZStack(alignment: .bottom) {
if(focusInputField) {
Spacer()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.contentShape(Rectangle())
.simultaneousGesture(TapGesture().onEnded({ _ in
focusInputField = false
print("I'm still standing yeah yeah yeah")
print(focusInputField)
})).onTap
}
HStack() {
if(focusInputField) {
Button("", systemImage: "keyboard.chevron.compact.down") {
focusInputField = false
}
}
TextField("Enter a quick note...", text: $newNoteContent, axis: .vertical)
.lineLimit(1...5)
}
}
}
}
现在,只有当 TextInput 具有焦点时,带有点击手势的 Spacer 才会正确显示。 至于为什么它仍然保留在我原来的解决方法的视图中是任何人的猜测,但这确实有效。