Search code examples
swiftuiwatchos

My SwiftUI Button is not tappable for minus image systemName


This is a fully reproducible example View to show the problem:

import SwiftUI

struct SwitchView: View {
    @State private var timeMode = 1
    var body: some View {
        HStack(spacing: 0) {
            Button {
                timeMode = 0
            } label: {
                Image(systemName: "minus") //When I change to `plus` then everything is fine✅, but I need minus here.
                    .resizable()
                    .scaledToFit()
                    .frame(width: 24 , height: 24, alignment: .center)
            }
            .frame(width: 40, height: 40)
            .background(Color.gray.opacity(timeMode == 0 ? 1 : 0))
            .foregroundColor(timeMode == 0 ? Color.black : Color.gray)
            .cornerRadius(20)
            .buttonStyle(.plain)
            Button {
                timeMode = 1
            } label: {
                Image(systemName: "plus")
                    .resizable()
                    .scaledToFit()
                    .frame(width: 24 , height: 24, alignment: .center)
            }
            .frame(width: 40, height: 40)
            .background(Color.gray.opacity(timeMode == 1 ? 1 : 0))
            .foregroundColor(timeMode == 1 ? Color.black : Color.gray)
            .cornerRadius(20)
            .buttonStyle(.plain)
        }
        .background(Color.gray.opacity(0.3))
        .cornerRadius(20)
    }
}

struct SwitchView_Previews: PreviewProvider {
    static var previews: some View {
        SwitchView()
    }
}

enter image description here

minus button doesn't work correctly. It doesn't detect taps every time. Try to tap it above or below minus shape. When you tap on the simulator EXACTLY on the shape of minus then it works. But of course I want it to be tappable a whole Button, not just its shape.

Why does an image matter here? How can I find the issue?


Solution

  • You need to explicitly tell SwiftUI to consider the whole shape of the Button as tappable. This can be achieved using the modifier .contentShape(...).

    Here is how to use it:

                Button {
                    timeMode = 0
                } label: {
                    Image(systemName: "minus") //When I change to `plus` then everything is fine✅, but I need minus here.
                        .resizable()
                        .scaledToFit()
                        .frame(width: 24 , height: 24, alignment: .center)
                }
    
                .contentShape(Rectangle())    // <- Here!!!
    
                .frame(width: 40, height: 40)
                .background(Color.gray.opacity(timeMode == 0 ? 1 : 0))
                .foregroundColor(timeMode == 0 ? Color.black : Color.gray)
                .cornerRadius(20)
                .buttonStyle(.plain)
    

    The Rectangle() shape will also accept taps outside of the round corners of the minus button, so you could try using Capsule() or Circle() instead and see if it works better.