SwiftUI:带有 onTapGesture 的列表单元格内的菜单触发手势
SwiftUI: Menu inside list cell with onTapGesture triggers gesture
我在 iOS14.3 (Xcode12.3) 上有一个 SwiftUI List
,在它的单元格中我想在用户使用时弹出 Menu
正在点击一个按钮。此外,这些单元格有一个 onTapGesture
,问题是按下菜单按钮时也会触发此手势。
示例:
List {
HStack {
Text("Cell content")
Spacer()
// Triggers also onTapGesture of cell
Menu(content: {
Button(action: { }) {
Text("Menu Item 1")
Image(systemName: "command")
}
Button(action: { }) {
Text("Menu Item 2")
Image(systemName: "option")
}
Button(action: { }) {
Text("Menu Item 3")
Image(systemName: "shift")
}
}) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
}
.background(Color(.systemBackground))
.onTapGesture {
print("Cell tapped")
}
}
如果点击省略号图像,菜单会打开,但 "Cell tapped"
也会打印到控制台。这对我来说是个问题,因为在我的真实世界示例中,我在轻击手势内折叠单元格,当然我不希望在用户按下菜单按钮时发生这种情况。
我发现,当我长按按钮时,手势不会被触发。但在我看来,这是不可接受的用户体验,我花了一段时间才找到答案。
我还注意到,当我用常规 Button
替换 Menu
时,按下时不会触发单元格手势(仅在 BorderlessButtonStyle
或 PlainButtonStyle
时触发,否则他根本不活跃)。但是我不知道如何通过它的操作打开菜单。
List {
HStack {
Text("Cell content")
Spacer()
// Does not trigger cells onTapGesture, but how to open Menu from action?
Button(action: { print("Button tapped") }) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
.buttonStyle(BorderlessButtonStyle())
}
.background(Color(.systemBackground))
.onTapGesture {
print("Cell tapped")
}
}
我不确定这是一个错误还是预期的行为,但与其对抗它,我建议避免这种重叠(因为即使今天成功,它也可能停止在不同的地方工作 systems/updates) .
这是可能的解决方案(使用 Xcode 12.1 / iOS 14.1 测试)
List {
HStack {
// row content area
HStack {
Text("Cell content")
Spacer()
}
.background(Color(.systemBackground)) // for tap on spacer area
.onTapGesture {
print("Cell tapped")
}
// menu area
Menu(content: {
Button(action: { }) {
Text("Menu Item 1")
Image(systemName: "command")
}
Button(action: { }) {
Text("Menu Item 2")
Image(systemName: "option")
}
Button(action: { }) {
Text("Menu Item 3")
Image(systemName: "shift")
}
}) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
}
}
或者,您可以覆盖菜单 onTapGesture
:
struct ContentView: View {
var body: some View {
List {
HStack {
Text("Cell content")
Spacer()
Menu(content: {
Button(action: {}) {
Text("Menu Item 1")
Image(systemName: "command")
}
Button(action: {}) {
Text("Menu Item 2")
Image(systemName: "option")
}
Button(action: {}) {
Text("Menu Item 3")
Image(systemName: "shift")
}
}) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
.onTapGesture {} // override here
}
.contentShape(Rectangle())
.onTapGesture {
print("Cell tapped")
}
}
}
}
此外,无需使用 .background(Color(.systemBackground))
敲击垫片。这不是很灵活 - 如果您想更改背景颜色怎么办?
您可以使用 contentShape
代替:
.contentShape(Rectangle())
我在 iOS14.3 (Xcode12.3) 上有一个 SwiftUI List
,在它的单元格中我想在用户使用时弹出 Menu
正在点击一个按钮。此外,这些单元格有一个 onTapGesture
,问题是按下菜单按钮时也会触发此手势。
示例:
List {
HStack {
Text("Cell content")
Spacer()
// Triggers also onTapGesture of cell
Menu(content: {
Button(action: { }) {
Text("Menu Item 1")
Image(systemName: "command")
}
Button(action: { }) {
Text("Menu Item 2")
Image(systemName: "option")
}
Button(action: { }) {
Text("Menu Item 3")
Image(systemName: "shift")
}
}) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
}
.background(Color(.systemBackground))
.onTapGesture {
print("Cell tapped")
}
}
如果点击省略号图像,菜单会打开,但 "Cell tapped"
也会打印到控制台。这对我来说是个问题,因为在我的真实世界示例中,我在轻击手势内折叠单元格,当然我不希望在用户按下菜单按钮时发生这种情况。
我发现,当我长按按钮时,手势不会被触发。但在我看来,这是不可接受的用户体验,我花了一段时间才找到答案。
我还注意到,当我用常规 Button
替换 Menu
时,按下时不会触发单元格手势(仅在 BorderlessButtonStyle
或 PlainButtonStyle
时触发,否则他根本不活跃)。但是我不知道如何通过它的操作打开菜单。
List {
HStack {
Text("Cell content")
Spacer()
// Does not trigger cells onTapGesture, but how to open Menu from action?
Button(action: { print("Button tapped") }) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
.buttonStyle(BorderlessButtonStyle())
}
.background(Color(.systemBackground))
.onTapGesture {
print("Cell tapped")
}
}
我不确定这是一个错误还是预期的行为,但与其对抗它,我建议避免这种重叠(因为即使今天成功,它也可能停止在不同的地方工作 systems/updates) .
这是可能的解决方案(使用 Xcode 12.1 / iOS 14.1 测试)
List {
HStack {
// row content area
HStack {
Text("Cell content")
Spacer()
}
.background(Color(.systemBackground)) // for tap on spacer area
.onTapGesture {
print("Cell tapped")
}
// menu area
Menu(content: {
Button(action: { }) {
Text("Menu Item 1")
Image(systemName: "command")
}
Button(action: { }) {
Text("Menu Item 2")
Image(systemName: "option")
}
Button(action: { }) {
Text("Menu Item 3")
Image(systemName: "shift")
}
}) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
}
}
或者,您可以覆盖菜单 onTapGesture
:
struct ContentView: View {
var body: some View {
List {
HStack {
Text("Cell content")
Spacer()
Menu(content: {
Button(action: {}) {
Text("Menu Item 1")
Image(systemName: "command")
}
Button(action: {}) {
Text("Menu Item 2")
Image(systemName: "option")
}
Button(action: {}) {
Text("Menu Item 3")
Image(systemName: "shift")
}
}) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
.onTapGesture {} // override here
}
.contentShape(Rectangle())
.onTapGesture {
print("Cell tapped")
}
}
}
}
此外,无需使用 .background(Color(.systemBackground))
敲击垫片。这不是很灵活 - 如果您想更改背景颜色怎么办?
您可以使用 contentShape
代替:
.contentShape(Rectangle())