Chapters

Hide chapters

SwiftUI Cookbook

Live Edition · iOS 16.4 · Swift 5.8.1 · Xcode 14.3.1

Create Consistent SwiftUI Designs
Written by Team Kodeco

When developing your SwiftUI application, you might want to ensure a consistent look and feel across different views and UI elements. One common element that can disrupt this consistency is an image or icon that stands out due to its color. Thankfully, SwiftUI provides an elegant solution to this problem: the template rendering mode.

The renderingMode view modifier in SwiftUI determines how an image should be displayed in your views. There are two rendering modes available: original and template. original keeps the image’s original color, while template discards color information and draws only the shape of the image. The color of a template image is then determined by the tint color in its environment.

Let’s look at an example. Suppose you’ve created a custom icon that you want to change color based on whether it’s selected. You could use the .template rendering mode to achieve this.

import SwiftUI

struct ContentView: View {
  @State private var isSelected = false

  var body: some View {
    Button(action: {
      isSelected.toggle()
    }) {
      Image("TransparentHedgy")
        .renderingMode(.template)
        .resizable()
        .aspectRatio(contentMode: .fit)
        .frame(width: 200, height: 200)
        .foregroundColor(isSelected ? .brown : .gray)
    }
  }
}

Note: If you want to try out the examples, you can download an archive of all the images used in this section here.

The preview should look as follows:

Use template rendering mode to change colors on custom images in SwiftUI.
Use template rendering mode to change colors on custom images in SwiftUI.

In this code, you define a Button that displays your custom image. The isSelected state property determines the color of the image: brown if selected, and gray otherwise. The magic happens with .renderingMode(.template), which tells SwiftUI to treat the image as a shape and apply the color set with foregroundColor.

Another advantage of using template rendering mode is that it adapts to the color scheme of the device. This could be useful when you want your UI to seamlessly transition between light and dark appearance.

Using Template Rendering Mode With Animation

Animations offer another fun way to use template rendering mode. By using .template mode, you can animate the color of an image over time, creating interesting effects.

In the following example, you use animation to change the color of your custom image between purple and gray:

struct ContentView: View {
  @State private var changeColor = false

  var body: some View {
    Image("TransparentHedgy")
      .renderingMode(.template)
      .resizable()
      .aspectRatio(contentMode: .fit)
      .frame(width: 200, height: 200)
      .foregroundColor(changeColor ? .purple : .gray)
      .onAppear {
        withAnimation(.easeInOut(duration: 2).repeatForever(autoreverses: true)) {
          changeColor.toggle()
        }
      }
  }
}

The preview should look as follows:

Template rendering mode also works well with animation in SwiftUI.
Template rendering mode also works well with animation in SwiftUI.

Here’s what this code does:

  • @State private var changeColor = false declares a property called changeColor as a @State property wrapper. The @State wrapper allows the view to have mutable state. The changeColor property is initially set to false.

  • Image("TransparentHedgy") displays an image in the view. It assumes that there is an image named TransparentHedgy included in the asset catalog of your project.

  • .renderingMode(.template) sets the rendering mode of the image to .template. This mode allows you to change the color of the image using the foregroundColor modifier.

  • .resizable() makes the image resizable, allowing it to scale up or down based on the frame you set.

  • .aspectRatio(contentMode: .fit): This modifier sets the aspect ratio of the image and ensures that it fits within the available space. The .fit content mode maintains the image’s aspect ratio while fitting it within the frame, without cropping or distorting the image.

  • .frame(width: 200, height: 200) sets the size of the image’s frame. In this example, the image is given a width and height of 200 points.

  • .foregroundColor(changeColor ? .purple : .gray) sets the color of the image. When changeColor is true, the image color is set to .purple, and when changeColor is false, the color is set to .gray. The changeColor property is toggled in the following onAppear block.

  • .onAppear { } executes a closure when the view appears on the screen.

  • withAnimation(.easeInOut(duration: 2).repeatForever(autoreverses: true)) wraps the animation settings for the following closure. It specifies an animation with ease-in and ease-out timing, a duration of 2 seconds and autoreversing behavior.

  • changeColor.toggle() switches the value of changeColor back and forth. When the view appears, the closure is executed in an animated loop, causing changeColor to alternate between true and false. This toggling of changeColor triggers the change in the image’s color, animating it between purple and gray.

By combining these modifiers and the @State property, the code displays an image that is resizable, has a fixed width and height of 200 points and alternates its color between purple and gray in an animated loop.

Remember, the template rendering mode can be very useful, but it is not always the correct choice. If you have a colorful image that should always be displayed in its original colors, then you should use original.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.