SwiftUI 按钮仅点击文本部分

问题描述 投票:0回答:13

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

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)

    }
}
swiftui
13个回答
84
投票

正确的解决方案是使用

.contentShape()
API。

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

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

RoundedRectangle
,您可以提供它。


80
投票

我认为这是一个更好的解决方案,将

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

27
投票

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

contentShape(_:eoFill:)

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

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()) // Add this inside Button.
}
.background(Color.green)
.cornerRadius(4)
.buttonStyle(PlainButtonStyle())

24
投票

这解决了我的问题:

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
是有原因的吗?


21
投票

简答

确保

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

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

长答案

分为两部分:

  1. Text
    的内容(ex.
    Button
    )需要拉伸
  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())
}

5
投票

你可能会这样做:

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())

2
投票

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

选项 1:使用几何阅读器

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 与 Spacer 结合使用

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 的使用方式
HStack
VStack


2
投票

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

对于按钮:

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

在文本的情况下,当

.contentShape(Rectangle())
没有
Text
时,我只需要
.background
修饰符,以使整个文本框架响应点击手势,而对于按钮,我使用我的文本或带框架的图像视图既不需要 .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()
    }
}

0
投票

这样可以让按钮区域正常展开 但是如果颜色是.clear,就不行🤷u200d♂️

                Button(action: {
                   doSomething()
                }, label: {
                    ZStack {
                        Color(.white)
                        Text("some texts")
                    }
                })

0
投票

当我使用

HStack
然后它适用于按钮的整个宽度,这很好,但是我遇到了整个按钮高度点击在角落不起作用的问题,我在下面的代码中修复了它:

Button(action:{
                print("Tapped Button")
            }) {
                VStack {
                   //Vertical whole area covered
                    Text("")
                    Spacer()
                    HStack {
                     //Horizontal whole area covered
                        Text("")
                        Spacer()
                    }
                }
            }

0
投票

如果您的应用需要同时支持 iOS/iPadOS 和 macOS,您可能需要参考我的代码!

Xcode 14.1 / iOS 14.1 / macOS 13.0 / 12-09-2022

Button(action: {
    print("Saved to CoreData")
    
}) {
    Text("Submit")
    .frame(minWidth: 100, maxWidth: .infinity, minHeight: 44, maxHeight: 60, alignment: .center)
    .foregroundColor(Color.white)
    #if !os(macOS)
    .background(Color.accentColor)
    #endif
}
#if os(macOS)
.background(Color.accentColor)
#endif
.cornerRadius(7)

0
投票

更简单的解决方法是添加

.frame(maxWidth: .infinity)
修饰符。 并将您的按钮包装在 ContainerView 中。您可以随时更改正在使用的按钮的大小。

    Button(action: tapped) {
        HStack {
            if let icon = icon {
                icon
            }

            Text(title)
        }
        .frame(maxWidth: .infinity) // This one
    }

0
投票

这些是 SwiftUI 的行为,通常会让开发人员感到惊讶:

  • 当您使用
    .frame
    修改按钮时,它会更改容器,但按钮大小没有改变。那是因为按钮标签是固定大小的。
  • 当您随后使用
    .contentShape
    修改框架时,它不会在您修改框架时更改按钮可点击区域

使用

Button(title)
的初始化使用固定大小的标签,你不能做太多。解决方案是自己创建标签视图。

Button {
  // action
} label: {
  Text(title)
    .frame(minWidth: 100, minHeight: 44)
}
© www.soinside.com 2019 - 2024. All rights reserved.