iosswiftswiftui

Use gradient for .toolbarBackground SwiftUI


I'm trying to set a custom gradient for the navigation .toolbarBackground but anytime I run its only using the first color from the LinearGradient.

.toolbar {
    ToolbarItem(placement: .navigationBarTrailing) {
        Menu {
            // MARK: 2 Actions
            // 1. logout
            // 2. Delete Account
            Button("Logout",action: logOutUser)
            
            Button("Delete Account",role: .destructive,action: deleteAccount)
        } label: {
            Image(systemName: "slider.horizontal.3")
                .rotationEffect(.init(degrees: 90))
                .tint(.white)
                .scaleEffect(0.8)
        }
    }
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("My Garden")
.toolbarColorScheme(.dark, for: .navigationBar)
.toolbarBackground(.visible, for: .navigationBar)
.toolbarBackground(LinearGradient(gradient: Gradient(colors: [Color("Green1"), Color("Green2")]),startPoint: .bottomLeading,endPoint: .topTrailing))

Solution

  • I came across the same issue lately and according to a blog post by Sarunw this is simply not possible as of iOS 16:

    In the current beta, we can't use other ShapeStyle like LinearGradient.

    I have filed a bug report and will update the post once I know it is the intended behavior or a bug.

    https://sarunw.com/posts/swiftui-navigation-bar-color/#basic-usage

    Alternative Solution

    If you don’t want to wait for iOS 17 to maybe fix this issue, you can fall back to UIKit and use the UINavigationBarAppearance instead and use a gradient image. I guess you could even create the gradient image in code, but I simply ended up putting the gradient image into the asset catalog (default, without slicing) and feed it to the NavBar as backgroundImage.

    There are quite a few tutorials out there showing this way, like this, this or this video. I the end they all end up with some form of this code:

    struct MainNavigationBar: ViewModifier {
        init() {
            let appearance = UINavigationBarAppearance()
            
            // Custom gradient background
            appearance.backgroundImage = UIImage(named: "NavigationBarBackground")
            
            // Apply custom styling to all bar states
            UINavigationBar.appearance().standardAppearance = appearance
            UINavigationBar.appearance().scrollEdgeAppearance = appearance
            UINavigationBar.appearance().compactAppearance = appearance
            
            // Custom button color tinting
            // (buggy and not working for me for some reason)
            UINavigationBar.appearance().tintColor = .white
        }
        
        func body(content: Content) -> some View {
            content
        }
    }
    

    Now you can use this modifier on a NavigationStack to attach this style to the view and all of the subviews.

    NavigationStack {
         // Your Content
    }
    .modifier(MainNavigationBar())