I have this List
which I have customised to fit my app design, one of the things I added is .listRowBackground()
, this gives me the ability to change a row's background color.
With this change comes an issue, though. The default animation for tapping a row in a list, which is a subtle highlight, doesn't show anymore.
Is there any way to re-enable this animation, or another way to customize the color of the list's background which keeps the animation?
Here's some code for reference:
List {
Button(action: {
// do something
}) {
HStack {
Text("Title 1")
}
}
.listRowBackground(StyleManager.Colors.secondary)
NavigationLink(value: "Title 2") {
Text("Title 2")
}
.listRowBackground(StyleManager.Colors.secondary)
}
.someOtherStyles(likeShadows, fonts, etc)
You could try using a custom ButtonStyle
for the buttons. The background color can then depend on whether the button is pressed. This information is provided by the configuration
received by the button style.
struct CustomListButton: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(.horizontal, 20)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.contentShape(Rectangle())
.background(configuration.isPressed ? .yellow : .clear)
}
}
Example use:
struct ContentView: View {
var body: some View {
List(1...10, id: \.self) { n in
Button("Row \(n)") {
print("Row \(n) tapped")
}
.buttonStyle(CustomListButton())
.listRowBackground(Rectangle().fill(.background))
.listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
}
.listStyle(.insetGrouped)
.scrollContentBackground(.hidden)
.background(Color(white: 0.8))
}
}
Here is how the same button style can be used in connection with the solution I was suggesting in answer to your previous question:
List(1...10, id: \.self) { n in
Button("Row \(n)") {
print("Row \(n) tapped")
}
.buttonStyle(CustomListButton())
.modifier(CustomListRowStyle(
foregroundColor: .green,
shadowColor: .yellow,
shadowOffsetY: 3,
position: n == 1 ? .first : (n == 10 ? .last : .middle)
))
}
.listStyle(.grouped)
.scrollContentBackground(.hidden)
.background(Color(white: 0.8))
EDIT Following up on your comment, it seems that in the context of a List
, tap gestures do not trigger a change to configuration.isPressed
. When the button is not in a List
, the flag does get toggled on tap.
As a workaround, you could create a wrapper for the Button
and then set your own flag when the button is tapped. The flag could be reset again after a short delay.
Here is an updated version of the code above that works this way.
CustomListButton
from above has been renamed to CustomListButtonStyle
.CustomListButton
is now a wrapper for a Button
. It applies the custom style and performs tap detection.struct CustomListButtonStyle: ButtonStyle {
let tapped: Bool
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(.horizontal, 20)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.contentShape(Rectangle())
.background(configuration.isPressed || tapped ? .yellow : .clear)
}
}
struct CustomListButton: View {
let label: String
let action: () -> Void
@State private var tapped = false
var body: some View {
Button(label) {
tapped = true
action()
Task { @MainActor in
try? await Task.sleep(for: .milliseconds(50))
tapped = false
}
}
.buttonStyle(CustomListButtonStyle(tapped: tapped))
.transaction { trans in
trans.disablesAnimations = true
}
.listRowBackground(Rectangle().fill(.background))
.listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
}
}
struct ContentView: View {
var body: some View {
List(1...10, id: \.self) { n in
CustomListButton(label: "Row \(n)") {
print("Row \(n) tapped")
}
}
.listStyle(.insetGrouped)
.scrollContentBackground(.hidden)
.background(Color(white: 0.8))
}
}