SwiftUI 按钮仅点击文本部分

SwiftUI Button tap only on text portion

我的按钮的背景区域未检测到用户交互。与所述按钮交互的唯一方法是点击按钮的文本/标签区域。如何让整个 Button 都可以点击?

struct ScheduleEditorButtonSwiftUIView: View {

    @Binding  var buttonTagForAction : ScheduleButtonType
    @Binding  var buttonTitle        : String
    @Binding  var buttonBackgroundColor : Color


    let buttonCornerRadius = CGFloat(12)

    var body: some View {

        Button(buttonTitle) {
              buttonActionForTag(self.buttonTagForAction)
        }.frame(minWidth: (UIScreen.main.bounds.size.width / 2) - 25, maxWidth: .infinity, minHeight: 44)

                            .buttonStyle(DefaultButtonStyle())
                            .lineLimit(2)
                            .multilineTextAlignment(.center)
                            .font(Font.subheadline.weight(.bold))
                            .foregroundColor(Color.white)

                            .border(Color("AppHighlightedColour"), width: 2)
                           .background(buttonBackgroundColor).opacity(0.8)

                            .tag(self.buttonTagForAction)
                            .padding([.leading,.trailing], 5)
       .cornerRadius(buttonCornerRadius)

    }
}

这解决了我这边的问题:

var body: some View {
    GeometryReader { geometry in
        Button(action: {
            // Action
        }) {
            Text("Button Title")
                .frame(
                    minWidth: (geometry.size.width / 2) - 25,
                    maxWidth: .infinity, minHeight: 44
                )
                .font(Font.subheadline.weight(.bold))
                .background(Color.yellow).opacity(0.8)
                .foregroundColor(Color.white)
                .cornerRadius(12)

        }
        .lineLimit(2)
        .multilineTextAlignment(.center)
        .padding([.leading,.trailing], 5)
    }
}

您使用 UIScreen 而不是 GeometryReader 有什么原因吗?

我认为这是一个更好的解决方案,将 .frame 值添加到 Text() 按钮将覆盖整个区域

Button(action: {
    //code
}) {
    Text("Click Me")
    .frame(minWidth: 100, maxWidth: .infinity, minHeight: 44, maxHeight: 44, alignment: .center)
    .foregroundColor(Color.white)
    .background(Color.accentColor)
    .cornerRadius(7)
}

回答有点晚,但我找到了两种方法 —

选项 1:使用几何 Reader

Button(action: {
}) {
    GeometryReader { geometryProxy in
        Text("Button Title")
            .font(Font.custom("SFProDisplay-Semibold", size: 19))
            .foregroundColor(Color.white)
            .frame(width: geometryProxy.size.width - 20 * 2) // horizontal margin
            .padding([.top, .bottom], 10) // vertical padding
            .background(Color.yellow)
            .cornerRadius(6)
    }
}

选项 2:将 HStack 与间隔符一起使用

HStack {
    Spacer(minLength: 20) // horizontal margin
    Button(action: {

    }) {
        Text("Hello World")
            .font(Font.custom("SFProDisplay-Semibold", size: 19))
            .frame(maxWidth:.infinity)
            .padding([.top, .bottom], 10) // vertical padding
            .background(Color.yellow)
            .foregroundColor(Color.white)
            .cornerRadius(6)
    }
    Spacer(minLength: 20)
}.frame(maxWidth:.infinity)

我在这里的思考过程是,虽然选项 1 更简洁,但我会选择选项 2,因为它与其父级的大小耦合较少(通过 GeometryReader)并且更符合我认为 SwiftUI 的含义使用 HStackVStack

正确的解决方案是使用 .contentShape() API.

Button(action: action) {
  HStack {
    Spacer()
    Text("My button")
    Spacer()
  }
}
.contentShape(Rectangle())

您可以更改提供的形状以匹配按钮的形状;如果您的按钮是 RoundedRectangle,您可以提供它。

您可以通过添加修饰符来定义命中测试的内容形状:contentShape(_:eoFill:)

重要的是你必须在 Button 的内容中应用。

Button(action: {}) {
    Text("Select file")
       .frame(width: 300)
       .padding(100.0)
       .foregroundColor(Color.black)
       .contentShape(Rectangle()) // Add this line
}
.background(Color.green)
.cornerRadius(4)
.buttonStyle(PlainButtonStyle())

另一个

Button(action: {}) {
    VStack {
       Text("Select file")
           .frame(width: 100)
       Text("Select file")
           .frame(width: 200)
    }
    .contentShape(Rectangle())
}
.background(Color.green)
.cornerRadius(4)
.buttonStyle(PlainButtonStyle())

简答

确保 Text(或按钮内容)跨越触摸区域的长度,并使用 .contentShape(Rectangle())

Button(action:{}) {
  HStack {
    Text("Hello")
    Spacer()
  }
  .contentShape(Rectangle())
}

长答案

分为两部分:

  1. Button的内容(例如Text)需要拉伸
  2. 命中测试需要考虑的内容

拉伸内容(例如Text):

// Solution 1 for stretching content
HStack {
  Text("Hello")
  Spacer()
}

// Solution 2 for stretching content
Text("Hello")
  .frame(maxWidth: .infinity, alignment: .leading)

// Alternatively, you could specify a specific frame for the button.

要考虑命中测试的内容,请使用 .contentShape(Rectangle()):

// Solution 1
Button(action:{}) {
  HStack {
    Text("Hello")
    Spacer()
  }
  .contentShape(Rectangle())
}

// Solution 2
Button(action:{}) {
  Text("Hello")
    .frame(maxWidth: .infinity, alignment: .leading)
    .contentShape(Rectangle())
}

您可能正在这样做:

Button { /*to do something on button click*/} 
label: { Text("button text").foregroundColor(Color.white)}
.frame(width: 45, height: 45, alignment: .center)
.background(Color.black)

解法:

Button(action: {/*to do something on button click*/ }) 
{ 
HStack { 
Spacer()
Text("Buttton Text")
Spacer() } }
.frame(width: 45, height: 45, alignment: .center)
.foregroundColor(Color.white)
.background(Color.black).contentShape(Rectangle())

当我遇到同样的问题时,我正在处理需要用户交互的按钮和文本。在查看并测试了许多答案(包括来自 post 的一些答案)之后,我最终使其按以下方式工作:

对于按钮:

/* WITH IMAGE */
Button {
    print("TAppeD")
} label: {
    Image(systemName: "plus")
        .frame(width: 40, height: 40)
}
/* WITH TEXT */
Button {
    print("TAppeD")
} label: {
    Text("My button")
       .frame(height: 80)
}
 

对于文本:

Text("PP")
    .frame(width: 40, height: 40)
    .contentShape(Rectangle())
    .onTapGesture {
        print("TAppeD")
    }

对于文本,当 Text 没有 .background 时,我只需要 .contentShape(Rectangle()) 修饰符,以使整个文本框架响应点击手势,而使用按钮时,我使用带框架的文本或图像视图,既不需要 .background 也不需要 .contentShape。

Image of the following code in preview (I'm not allowed to include pictures yet )

import SwiftUI

struct ContentView: View {
    @State var tapped: Bool = true
    var body: some View {
        VStack {
            RoundedRectangle(cornerRadius: 19)
                .frame(width: 40, height: 40)
                .foregroundColor(tapped ? .red : .green)
            Spacer()
            HStack (spacing: 0) {
                Text("PP")
                    .frame(width: 40, height: 40)
                    .contentShape(Rectangle())
                    .onTapGesture {
                        tapped.toggle()
                    }
                
                Button {
                    print("TAppeD")
                    tapped.toggle()
                } label: {
                    Image(systemName: "plus")
                        .frame(width: 40, height: 40)
                }
                .background(Color.red)
                
                Button {
                    print("TAppeD")
                    tapped.toggle()
                } label: {
                    Text("My button")
                        .frame(height: 80)
                }
                .background(Color.yellow)
            }
            
            Spacer()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}