'How to set custom highlighted state of SwiftUI Button
Solution 1:[1]
As far as I can tell, theres no officially supported way to do this as of yet. Here is a little workaround that you can use. This produces the same behavior as in UIKit where tapping a button and dragging your finger off of it will keep the button highlighted.
struct HoverButton<Label>: View where Label: View {
private let action: () -> ()
private let label: () -> Label
init(action: @escaping () -> (), label: @escaping () -> Label) {
self.action = action
self.label = label
}
@State private var pressed: Bool = false
var body: some View {
Button(action: action) {
label()
.foregroundColor(pressed ? .red : .blue)
.gesture(DragGesture(minimumDistance: 0.0)
.onChanged { _ in self.pressed = true }
.onEnded { _ in self.pressed = false })
}
}
}
Solution 2:[2]
I was looking for a similar functionality and I did it in the following way.
I created a special View struct returning a Button in the style I need, in this struct I added a State property selected. I have a variable named 'table' which is an Int since my buttons a round buttons with numbers on it
struct TableButton: View {
@State private var selected = false
var table: Int
var body: some View {
Button("\(table)") {
self.selected.toggle()
}
.frame(width: 50, height: 50)
.background(selected ? Color.blue : Color.red)
.foregroundColor(.white)
.clipShape(Circle())
}
}
Then I use in my content View the code
HStack(spacing: 10) {
ForEach((1...6), id: \.self) { table in
TableButton(table: table)
}
}
This creates an horizontal stack with 6 buttons which color blue when selected and red when deselected.
I am not a experienced developer but just tried all possible ways until I found that this is working for me, hopefully it is useful for others as well.
Solution 3:[3]
This is for the people who are not satisfied with the above solutions, as they raise other problems such as overlapping gestures(for example, it's quite hard to use this solution in scrollview now). Another crutch is to create a custom button style like this
struct CustomButtonStyle<Content>: ButtonStyle where Content: View {
var change: (Bool) -> Content
func makeBody(configuration: Self.Configuration) -> some View {
return change(configuration.isPressed)
}
}
So, we should just transfer the closure which will return the state of the button and create the button based on this parameter. It will be used like this:
struct CustomButton<Content>: View where Content: View {
var content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
Button(action: { }, label: {
EmptyView()
})
.buttonStyle(CustomButtonStyle(change: { bool in
Text("\(bool ? "yo" : "yo2")")
}))
}
}
Solution 4:[4]
Okey let me clear everything again. Here is the exact solution
- Create the below button modifier.
struct StateableButton<Content>: ButtonStyle where Content: View {
var change: (Bool) -> Content
func makeBody(configuration: Configuration) -> some View {
return change(configuration.isPressed)
}
}
- Then use it like below one
Button(action: {
print("Do something")
}, label: {
// Don't create your button view in here
EmptyView()
})
.buttonStyle(StateableButton(change: { state in
// Create your button view in here
return HStack {
Image(systemName: "clock.arrow.circlepath")
Text(item)
Spacer()
Image(systemName: "arrow.up.backward")
}
.padding(.horizontal)
.frame(height: 50)
.background(state ? Color.black : Color.clear)
}))
Solution 5:[5]
You need to define a custom style that can be used to provide the two backgrounds for normal and highlighted states:
Button(action: {
print("action")
}, label: {
Text("My Button").padding()
})
.buttonStyle(HighlightableButtonStyle(normal: { Color.red },
highlighted: { Color.green }))
// Custom button style
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
struct HighlightableButtonStyle<N, H>: ButtonStyle where N: View, H: View {
private let alignment: Alignment
private let normal: () -> N
private let highlighted: () -> H
init(alignment: Alignment = .center, @ViewBuilder normal: @escaping () -> N, @ViewBuilder highlighted: @escaping () -> H) {
self.alignment = alignment
self.normal = normal
self.highlighted = highlighted
}
func makeBody(configuration: Configuration) -> some View {
return ZStack {
if configuration.isPressed {
configuration.label.background(alignment: alignment, content: normal)
}
else {
configuration.label.background(alignment: alignment, content: highlighted)
}
}
}
}
Solution 6:[6]
Thanks @Chris:
use lambda-parameters initialized:
dct[key]=lambda x,y,xx=params[key][0],yy=params[key][1]: myfun(xx*x,yy*y)
this resolved my question!
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 | Marcela |
| Solution 2 | Nico van der Linden |
| Solution 3 | |
| Solution 4 | Ozan Honamlioglu |
| Solution 5 | Alexander Volkov |
| Solution 6 | Friedrich Ka |

