【问题标题】:SwiftUI: Menu inside list cell with onTapGesture triggers gestureSwiftUI:带有onTapGesture的列表单元格内的菜单触发手势
【发布时间】:2020-12-29 13:06:49
【问题描述】:

我在 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" 也会打印到控制台。这对我来说是个问题,因为在我的真实示例中,我在点击手势中折叠单元格,当然我不希望在用户按下菜单按钮时发生这种情况。

我发现,当我长按按钮时,手势不会被触发。但在我看来,这不是可接受的用户体验,我花了一段时间才自己找出来。

我还注意到,当我将Menu 替换为常规Button 时,按下时不会触发单元格手势(仅使用BorderlessButtonStylePlainButtonStyle,否则他根本不活动)。但我不知道如何从它的操作中打开菜单。

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")
    }
}

【问题讨论】:

  • 是 iOS 还是 macOS 项目?
  • 抱歉,它在 iOS14.3 和 Xcode 12.3 上
  • 我现在也在 macOS 上对其进行了测试,打开菜单时不会触发单元格手势。尽管 Mac 上的菜单看起来非常不同(实际上是 DropDown),但我希望两个平台上的行为相同。所以我刚刚向 Apple 报告了一个错误。

标签: swiftui swiftui-list


【解决方案1】:

或者,您可以覆盖菜单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())

【讨论】:

  • 谢谢,虽然我认为@Asperi 的答案更优雅一些,而且可能更适合未来证明,但我会将其标记为已接受的答案。事实上,在我的真实示例中,单元格要复杂得多,并且菜单按钮不是右侧的最后一个子视图。主要问题是,我在我的应用程序的不同位置重用了这个单元格,并且希望根据使用的位置有不同的 onTapGestures,所以我将在单元格主体之外应用那个手势。在这种情况下,您的解决方案要好得多。
  • 除此之外,感谢使用 contentShape 而不是背景的提示,以使垫片可点击。不知道,我还是 SwftUI 的新手,正在使用它开发我的第一个更大的应用程序。
【解决方案2】:

我不确定这是一个错误还是预期的行为,但我建议不要与之抗争,而是避免这种重叠(因为即使今天取得了成功,它也可能停止在不同的系统/更新上工作)。

这是可能的解决方案(使用 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()
            }
        }
    }

【讨论】:

  • 谢谢,这当然也解决了问题,也许是更优雅和面向未来的解决方案,但我接受了@pawello2222 的回答。原因见我对他的回答的评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-16
  • 2012-03-11
  • 2022-11-07
  • 1970-01-01
  • 2012-04-05
  • 2020-04-28
相关资源
最近更新 更多