Widgets – Adapting content to rendering modes

How to support widgetRenderingMode on visionOS.

Overview

Users on visionOS can apply a tint color to their widgets. We can use widgetRenderingMode to provide alternative views as needed.

struct RenderWidgetsEntryView : View {
    @Environment(\.widgetRenderingMode) var renderingMode
    var body: some View {
        if renderingMode == .fullColor {
            // Provide a main version of the widget content
        } else if renderingMode == .accented {
            // Provide an alternative view in accented mode
        }
    }
}

The documentation includes three modes, but only two seem to be used on visionOS.

  • fullColor: default SwiftUI content
  • accented: applying a user selected tint color
  • vibrant: used on iOS Lock Screen (does not appear to be used in visionOS widgets)

We have a couple of ways we can adapt our content to support accented mode. Content in this mode is separate into two groups. The default group and the accent group. We can use widgetAccentable to mark views as part of the accent group.

VStack {
    Text("Default Group")
    Text("Accent Group")
        .widgetAccentable(true)
}

For images, we can use the widgetAccentedRenderingMode modifier to tell visionOS how to change the image. There are a handful of options.

  • accented will make the image part of the “accented” group. Most of the time, this turns the entire image to the accent color, making it essentially useless. For reasons, I don’t understand, this seems to be the default that visionOS applies to images if we don’t use this modifier to specify another value.
  • desaturated and accentedDesaturated will map the alpha channel to ether the default or accent group.
  • fullColor will display the original image.
Image(.jsWidget)
    .widgetAccentedRenderingMode(.accentedDesaturated)

Let’s see this in action.

A caveat: When using SwiftUI spaces and fills it can be tricky to get accented mode looking good. For example, the Clock Widget we made in the timeline post uses a lot of Circle views. As of Beta 5, visionOS doesn’t provide enough control over accented rendering mode to make this viable. Instead, we could swap out the detailed clock view with a much simpler view. Perhaps one with only the hours and clock hands.

What about widget background? WidgeKit has a system for defining background views and allowing the system to remove them. This is mostly applicable on other platforms, but on visionOS we may see background views removed when using accented mode. If we do this by using containerBackground.

EmojiWidgetEntryView(entry: entry)
  .containerBackground(.white.gradient, for: .widget)

Sometimes we may not want the background removed or tinted by accented mode. In those cases, we can remove the containerBackground and use .containerBackgroundRemovable(false).

AppIntentConfiguration(kind: kind, intent: EmojiConfigurationAppIntent.self, provider: EmojiProvider()) { entry in
  ClockWidgetEntryView(entry: entry)
    // .containerBackground(.white.gradient, for: .widget)
  }
.containerBackgroundRemovable(false)

When we use this on the Clock Widget, visionOS will stop accenting our SwiftUI shapes, allowing us to show the original clock view.

The clock widget with black accent color applied to the frame, but not the widget content

Documentation

Support our work so we can continue to bring you new examples and articles.

Questions or feedback?