'Add border to only 1 button

I have a simple setup where I have 2 buttons and when a user clicks on one of them I want a border to show around it so that they know which one they clicked

I only ever want 1 button to have a border at once

I came up with this

import SwiftUI

struct TestView: View {
    
    @State var isBorder:Bool = false
    
    var body: some View {
        VStack{
        Button {
            isBorder.toggle()
        } label: {
            Label("Sports", systemImage: "sportscourt")
        }
        .foregroundColor(.black)
        .padding()
        .background(Color(hex: "00bbf9"))
        .cornerRadius(8)
        .overlay(isBorder ? RoundedRectangle(cornerRadius: 8).stroke(Color.black, lineWidth:2) : nil)
        Button {
            isBorder.toggle()
        } label: {
            Label("Firends", systemImage: "person")
        }
        .foregroundColor(.black)
        .padding()
        .background(Color(hex: "fee440"))
        .cornerRadius(8)
        .overlay(isBorder ? RoundedRectangle(cornerRadius: 8).stroke(Color.black, lineWidth:2) : nil)
        }
    }
}

struct TestView_Previews: PreviewProvider {
    static var previews: some View {
        TestView()
    }
}

But a border shows around both buttons because I am using 1 variable "isBorder"

How could I adapt my solution so that I can accommodate for more buttons



Solution 1:[1]

As the comments allready stated this should be encapsulated in its own view. But here you also have the additional problem with the shared state of what button is clicked.

First create an enum that holds all properties a button has that distinguishes it from others:

enum ButtonEnum: Int, CaseIterable{
    case sports, friends
    
    var systemIcon: String{
        switch self{
            
        case .sports:
            return "sportscourt"
        case .friends:
            return "person"
        }
    }
    
    var label: String{
        switch self{
            
        case .sports:
            return "Sports"
        case .friends:
            return "Friends"
        }
    }
    
    var backgroundColor: Color{
        switch self{
            
        case .sports:
            return Color("00bbf9")
        case .friends:
            return Color("fee440")
        }
    }
}

Then create a View that represents that button:

struct BorderedButton: View{
    
    @Binding var selected: ButtonEnum?
    var buttonType: ButtonEnum
    
    var action: () -> Void
    
    var body: some View{
        Button {
            selected = buttonType
            action()
        } label: {
            Label(buttonType.label, systemImage: buttonType.systemIcon)
        }
        .foregroundColor(.black)
        .padding()
        .background(buttonType.backgroundColor)
        .cornerRadius(8)
        .overlay(selected == buttonType ? RoundedRectangle(cornerRadius: 8).stroke(Color.black, lineWidth:2) : nil)
    }
}

and usage example:

struct TestView: View {
    
    @State private var selected: ButtonEnum? = nil
    
    var body: some View {
        VStack{
            BorderedButton(selected: $selected, buttonType: .friends) {
                print("friends pressed")
            }
            BorderedButton(selected: $selected, buttonType: .sports) {
                print("sports selected")
            }
        }
    }
}

This solution can be easily expanded by adding new cases to the enum.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 burnsi