title: SwiftUI technique to update UINavigationBarAppearance.backgroundImage dynamically?
body: <p>I am implementing a gradient background for the top navigation bar in SwiftUI using a standard <code>NavigationView</code>. For the gradient I am using a <code>UIGraphicsImageRenderer</code> to create a gradient image which I then assign to the <code>backgroundImage</code> of <code>UINavigationBarAppearance</code>. So far everything is behaving correctly in terms of drawing the gradient and having it render on the top bar.</p>
  <p><a href="https://i.stack.imgur.com/ZAlLe.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ZAlLe.jpg" alt="gradient" /></a> <a href="https://i.stack.imgur.com/KIS34.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/KIS34.jpg" alt="enter image description here" /></a></p>
  <p>However, I have not yet been able to make the <code>backgroundImage</code> change dynamically per view. Full code below that should work if you paste it into a playground.</p>
  <pre class="lang-swift prettyprint-override"><code>import SwiftUI
  
  struct NavBarGradient: ViewModifier {
      
      init(from: UIColor, to: UIColor) {
          let appearance = UINavigationBarAppearance()
          appearance.backgroundColor = .clear
          
          let imageRenderer = UIGraphicsImageRenderer(size: .init(width: 1, height: 1))
          let gradientImage : UIImage = imageRenderer.image { ctx in
              let gradientLayer = CAGradientLayer()
              gradientLayer.frame = imageRenderer.format.bounds
              gradientLayer.colors = [from.cgColor, to.cgColor]
              gradientLayer.locations = [0, 1]
              gradientLayer.startPoint = .init(x: 0.0, y: 0.0)
              gradientLayer.endPoint = .init(x: 0.5, y: 1.0)
              
              gradientLayer.render(in: ctx.cgContext)
          }
          appearance.backgroundImage = gradientImage
          
          UINavigationBar.appearance().standardAppearance = appearance
          UINavigationBar.appearance().compactAppearance = appearance
          UINavigationBar.appearance().scrollEdgeAppearance = appearance
      }
      
      func body(content: Content) -&gt; some View {
          content
      }
      
  }
  
  extension View {
      func navBarGradient(from: UIColor = .systemRed, to: UIColor = .systemYellow) -&gt; some View {
          modifier(NavBarGradient(from: from, to: to))
      }
  }
  
  struct SimpleView: View {
      var body: some View {
          Text(&quot;Gradient View&quot;).navigationTitle(&quot;Gradient Colors&quot;)
      }
  }
  
  struct ContentView: View {
      var body: some View {
          NavigationView {
              List {
                  NavigationLink(
                      &quot;Blue to Cyan&quot;,
                      destination: SimpleView()
                          .navBarGradient(from: .systemBlue, to: .systemCyan)
                  )
                  NavigationLink(
                      &quot;Green to Mint&quot;,
                      destination: SimpleView()
                          .navBarGradient(from: .systemGreen, to: .systemMint)
                  )
                  NavigationLink(
                      &quot;Red to Yellow&quot;,
                      destination: SimpleView()
                          .navBarGradient() // comment me out and previous modifier wins
                  )
              }
                  .navigationTitle(&quot;Main Menu&quot;)
                  .navigationBarTitleDisplayMode(.inline)
          }
      }
  }
  </code></pre>
  <p>Right now the <code>.navBarGradient()</code> view modifier seems to be &quot;last one wins&quot; which makes sense given how the view system works in SwiftUI. So what I am trying to understand is - what is the &quot;SwiftUI way&quot; of updating the appearance dynamically?</p>
  <p>I have been able to achieve a gradient background in the top bar by <a href="https://filipmolcik.com/navigationview-dynamic-background-color-in-swiftui/" rel="nofollow noreferrer">adapting solutions like this one</a>; it works exactly as desired. But it seems that it would be a useful thing to be able to update the top bar background image dynamically for things like server-side images.</p>
  <p>Thanks in advance!</p>