DEV Community

Cover image for Fixing the issue of an unclickable SwiftUI Plain Button when tapping on the empty area
D. Prameswara
D. Prameswara

Posted on • Originally published at swift.my.id on

Fixing the issue of an unclickable SwiftUI Plain Button when tapping on the empty area

If you're using a Button widget in SwiftUI, by default, the entire label of the Button is clickable. This is the default tap area for the Button if you use a button style other than Plain. For example:

struct ContentView: View {
    @State private var show = false
    var body: some View {
        Button {
            show.toggle()
        } label: {
            HStack {
                Image(systemName: "plus")
                Text("Add data")
            }
            .padding()
            .frame(maxWidth: .infinity)
        }
        .background {
            RoundedRectangle(cornerRadius: 10.0)
                .stroke(.primary, lineWidth: 0.2)
        }
        .padding()
        .sheet(isPresented: $show){
            Text("Pop Window")
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Fixing the issue of an unclickable SwiftUI Plain Button when tapping on the empty area

The example above is a Button that contains an icon and text, with border modifier to make it appear as an outlined Button. The default style for this button is automatic.

And as you can notice, you can click on all parts of the button. Even in the areas that appear empty.

However, styles like this are sometimes not what you want, because all parts inside the button will appear as a hyperlink or be colored in blue. Therefore, the button style is usually changed to .plain, which makes the color of content inside the button become default , and than allows you to customize it as needed.

struct ContentView: View {
    @State private var show = false
    var body: some View {
        Button {
            show.toggle()
        } label: {
            HStack {
                Image(systemName: "plus")
                Text("Add data")
            }
            .padding()
            .frame(maxWidth: .infinity)
        }
        .buttonStyle(.plain)
        .background {
            RoundedRectangle(cornerRadius: 10.0)
                .stroke(.primary, lineWidth: 0.2)
        }
        .padding()
        .sheet(isPresented: $show){
            Text("Pop Window")
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

However, this style has a side effect, which is make not all parts of the button can be clicked. Only the parts containing text or an image can be clicked. This is why the empty parts of a plain button cannot be clicked.

To fix this issue, it's actually quite simple. Just add the .contentShape modifier within the Button's content. Remember, it should be inside the Button's content, not the Button it self.

func contentShape<S>(
    _ shape: S,
    eoFill: Bool = false
) -> some
Enter fullscreen mode Exit fullscreen mode

So, the code can be modified as follows.

struct ContentView: View {
    @State private var show = false
    var body: some View {
        Button {
            show.toggle()
        } label: {
            HStack {
                Image(systemName: "plus")
                Text("Add data")
            }
            .padding()
            .frame(maxWidth: .infinity)
            .contentShape(RoundedRectangle(cornerRadius: 10.0))
        }
        .buttonStyle(.plain)
        .background {
            RoundedRectangle(cornerRadius: 10.0)
                .stroke(.primary, lineWidth: 0.2)
        }
        .padding()
        .sheet(isPresented: $show){
            Text("Pop Window")
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

With this modification, all parts of a button with the .plain style now will be clickable.

The .contentShape modifier is very useful in situations like above, or in other cases, such as when you want to add buttons inside List items.

Happy experimenting, and I hope this little tip is helpful.

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

Top comments (0)

Sentry growth stunted Image

If you are wasting time trying to track down the cause of a crash, it’s time for a better solution. Get your crash rates to zero (or close to zero as possible) with less time and effort.

Try Sentry for more visibility into crashes, better workflow tools, and customizable alerts and reporting.

Switch Tools

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay