Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI Button selection

I'm trying to grasp SwiftUI concepts (finished Apple's SwiftUI tutorials) but it seems hard for me after UIKit decade.

I need to switch state of multiple buttons in HStack by clicking on them (UIKit's isSelected), and change their font and text (in UIKit world i would use attributedText property in if statement examinig isSelected property, all in @IBAction on TouchUpInside).

My first thought was to get "reference" of Button in its action block, but it feels like it's not the SwiftUI way (and is not even possible). I found the solution that's using Configurator and its isPressed property (which is not what I searching for), but i need the Button to behave like toggle actually. Is there any built-in isSelected substitution in SwiftUI, or I have to make my own View implementation with @State or @BindableObject that will encapsulate some gesture recognizer (seems pretty ugly). Thanks in advance!

like image 291
Aft3rmath Avatar asked Nov 14 '25 18:11

Aft3rmath


2 Answers

I came up with the custom View, that's encapsulating Button like this:

    import SwiftUI

struct SelectableButtonStyle: ButtonStyle {

    var isSelected = false

    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .frame(width: 60.0, height: 60.0, alignment: .center)
            .padding()
            .background(Color(#colorLiteral(red: 1, green: 0.8980392157, blue: 0.7058823529, alpha: 1)))
            .clipShape(RoundedRectangle(cornerRadius: isSelected ? 16.0 : 0.0))
            .overlay(RoundedRectangle(cornerRadius: isSelected ? 16.0 : 0.0).stroke(lineWidth: isSelected ? 2.0 : 0.0).foregroundColor(Color.pink))
            .animation(.linear)
    }
}


struct StatedButton<Label>: View where Label: View {


    private let action: (() -> ())?

    private let label: (() -> Label)?

    @State var buttonStyle = SelectableButtonStyle()

    init(action: (() -> ())? = nil, label: (() -> Label)? = nil) {
        self.action = action
        self.label = label
    }

    var body: some View {
        Button(action: {
            self.buttonStyle.isSelected = !self.buttonStyle.isSelected
            self.action?()
            print("isSelected now is \(self.buttonStyle.isSelected ? "true" : "false")")
        }) {
            label?()
        }
        .buttonStyle(buttonStyle)
    }    
}

Please let me know if this solution is not good and why, I really appreciate it. And also I'm struggling with pretty trivial problem: how to map my model's array elements to buttons (i.e. how to detect what button exactly has been tapped), but I think I have to create another question for this.

like image 102
Aft3rmath Avatar answered Nov 17 '25 09:11

Aft3rmath


I have a simple way to do this.

    @State var selected = false

    func createButton() -> some View {
        Button(action: {
            self.selected.toggle()
        }, label: {
            Text("Hello World")
                .padding(.all, 5)
                .background(selected ? Color.blue : Color.white)
                .foregroundColor(selected ? Color.white : Color.blue)
        })
            .overlay(
                RoundedRectangle(cornerRadius: 4)
                    .stroke(Color.blue, lineWidth: 1)
        )
    }

like image 28
Alvin Avatar answered Nov 17 '25 09:11

Alvin