SwiftUI - 列表行中的多个按钮

125

假设我有一个List和一行中的两个按钮,如何区分哪个按钮被点击而不会高亮整个行?

对于这个示例代码,当行中的任何一个按钮被点击时,都会调用这两个按钮的操作回调。

// a simple list with just one row
List {

    // both buttons in a HStack so that they appear in a single row
    HStack {
        Button {
            print("button 1 tapped")
        } label: {
            Text("One")
        }
            
        Button {
            print("button 2 tapped")
        } label: {
            Text("Two")
        }
    }
}

当我只点击其中一个按钮一次时,我看到两个按钮的回调都被调用了,这不是我想要的:

button 1 tapped
button 2 tapped

4
听起来像是一个漏洞。你应该为此提交一份雷达报告。http://feedbackassistant.apple.com/ - Fogmeister
4个回答

258

您可以应用任何按钮样式(例如,.bordered.borderless.borderedProminent等),但不能使用.automatic样式。

    List([1, 2, 3], id: \.self) { row in
        HStack {
            Button(action: { print("Button at \(row)") }) {
                Text("Row: \(row) Name: A")
            }
            .buttonStyle(.borderless)
            
            Button(action: { print("Button at \(row)") }) {
                Text("Row: \(row) Name: B")
            }
            .buttonStyle(.plain)
        }
    }

3
如果您需要维护按钮动画(高亮等),这是最佳解决方案。 - sabiland
31
非常有效,但为什么? - Toby
23
是的,这对我来说真的太意外了。你是怎么发现这个的? - user652038
5
在文档中花费了大量时间。 - Ramis
2
非常好。它完美地工作,即使在所有行上都有NavigationLink。 - Luc-Olivier
显示剩余6条评论

31

List行中包含Button时似乎出现了特定问题。

解决方法:

List {
  HStack {
    Text("One").onTapGesture { print("One") }
    Text("Two").onTapGesture { print("Two") }
  }
}

这将产生所期望的输出。

您还可以使用Group而不是Text,从而为"按钮"设计一个复杂的外观。


2
不幸的是,这个 bug 不仅限于列表中的按钮。我能够在列表内的 HStack 中重现多个 NavigationLink 的相同问题。 - BlueSolrac
1
这只是一种解决方法。使用BorderlessButtonStyle()是最好的方法。 - brokenrhino
虽然这个方法可以运行,但请记住,这个更改会有更多的影响。无障碍API(例如VoiceOver用户)将不再将“One”和“Two”识别为按钮。您需要给它一个按钮特征。我认为添加buttonStyle是更好的方式。 - n-develop

10

SwiftUI与其他方式不同之处在于,您不会创建特定的实例,例如UIButton,因为您可能在Mac应用程序中。使用SwiftUI,您会请求一个按钮类型的东西。

在这种情况下,因为您在列表行中,系统会给您一个完整大小的、可以点击任何地方触发操作的按钮。而且由于您添加了两个按钮,当您点击任何地方时,两个按钮都会触发。

您可以添加两个单独的视图,并为它们提供.onTapGesture,使它们本质上充当按钮,但您将失去单元格行的点击效果和SwiftUI提供的任何其他自动按钮功能。

List {
    HStack {
        Text("One").onTapGesture {
            print("Button 1 tapped")
        }

        Spacer()

        Text("Two").onTapGesture {
            print("Button 2 tapped")
        }
    }
}

5
tapAction不再可用(Xcode 11.2 beta)。请使用.onTapGesture()。 - Carla Camargo
同时,你必须小心,因为轻触手势只会应用于文本本身的可见区域。你视图中的每个透明区域都不可点击。 - Kn3cht

6

您需要创建自己的ButtonStyle样式:

  struct MyButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
      configuration.label
        .foregroundColor(.accentColor)
        .opacity(configuration.isPressed ? 0.5 : 1.0)
    }
  }

  struct IdentifiableString: Identifiable {
    let text: String
    var id: String { text }
  }

  struct Test: View {
    var body: some View {
      List([
        IdentifiableString(text: "Line 1"),
        IdentifiableString(text: "Line 2"),
      ]) {
        item in
        HStack {
          Text("\(item.text)")
          Spacer()
          Button(action: { print("\(item.text) 1")}) {
            Text("Button 1")
          }
          Button(action: { print("\(item.text) 2")}) {
            Text("Button 2")
          }
        }
      }.buttonStyle(MyButtonStyle())
    }
  }

2
实际上,您不需要自定义样式。只需使用 BorderlessButtonStyle() 即可。 - Anton
这是一个更通用的解决方案,不仅仅使用其中几个内置样式。正是我一直在寻找的。谢谢。 - DouglasGClarke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接