Generally I like to stick with default color options for accessibility reasons, but I have a specific use case and I am unable to change an individual .toolbar ToolbarItem color.
I can override all ToolbarItem colors by using the commented out code at top of my snippet, but I want to color individual icons.
import SwiftUI
struct ContentView: View {
// init() {
// UIBarButtonItem.appearance(whenContainedInInstancesOf: [UIToolbar.self]).tintColor = .systemRed
// }
var body: some View {
NavigationView {
Text("Hello, world!")
.toolbar {
// To be colored RED
ToolbarItem(placement: .bottomBar) {
Button(action: {}, label: {Label("Icon One", systemImage: "stop.fill")})
}
// To be colored BLACK
ToolbarItem(placement: .bottomBar) {
Button(action: {}, label: {Label("Icon Two", systemImage: "play.fill")})
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Yep, I've been struggling with the same thing. It looks that Buttons
are using system tint color and its style overall.
Here is my take:
content
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
HStack {
StyledButton(image: .system("arrow.left")) { ... }
Spacer(minLength: 0) // important to not fell button to default styling
}
}
}
The idea is to not display Button only, but together with other UI elements. Looks like a SwiftUI bug to me. (Or just trying to out-smart you.)
The simplest solution I've found is to ditch Button
and use .onTapGesture
instead.
struct ContentView: View {
var body: some View {
NavigationView {
Text("Hello World!")
.toolbar {
// To be colored RED
ToolbarItem(placement: .bottomBar) {
Label("Icon One", systemImage: "stop.fill")
.foregroundColor(.red)
.onTapGesture {
// Respond to tap
}
}
// To be colored BLACK
ToolbarItem(placement: .bottomBar) {
Label("Icon One", systemImage: "play.fill")
.foregroundColor(.black)
.onTapGesture {
// Respond to tap
}
}
}
}
}
}
Output:
It looks like all standard types (button, image, text, etc) are intercepter by ToolbarItem and converted into appropriate internal representation. But custom view (eg. shape based)... is not. So see below a demo of possible approach.
Demo prepared & tested with Xcode 12 / iOS 14.
ToolbarItem(placement: .bottomBar) {
ShapeButton(shape: Rectangle(), color: .red) {
print(">> works")
}
}
and simple custom Shape-based button
struct ShapeButton<S:Shape>: View {
var shape: S
var color: Color
var action: () -> ()
@GestureState private var tapped = false
var body: some View {
shape
.fill(color).opacity(tapped ? 0.4 : 1)
.animation(.linear(duration: 0.15), value: tapped)
.frame(width: 18, height: 18)
.gesture(DragGesture(minimumDistance: 0)
.updating($tapped) { value, state, _ in
state = true
}
.onEnded { _ in
action()
})
}
}
backup
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With