Widgets – Adding content and options for configuration

Layout some content and add parameters.

Overview

We already learned the basics of creating widgets. We also learned a handful of key concepts that make widgets special on visionOS. Let’s add some content and learn how to add options.

The template we started from had a parameter to pass in an emoji.

struct ConfigurationAppIntent: WidgetConfigurationIntent {
    ...
    @Parameter(title: "Emoji", default: "🌸")
    var emoji: String
}

When a user opens the configuration menu on the widget, this parameter is presented as a text field. They can use the keyboard to select any emoji they want. We’ll build on this. Let’s mock up a quick view to repeat this emoji in a grid. We’ll offset every other row to get a staggered pattern.

struct EmojiBackground: View {
    var emoji: String

    var body: some View {
        GeometryReader { geometry in
            let emojiSize: CGFloat = 24
            let spacing: CGFloat = 4

            let patternWidth = geometry.size.width * 3
            let patternHeight = geometry.size.height * 3

            let rows = Int(patternHeight / (emojiSize + spacing))
            let cols = Int(patternWidth / (emojiSize + spacing))

            VStack(spacing: spacing) {
                ForEach(0..<rows, id: \.self) { row in
                    HStack(spacing: spacing) {
                        ForEach(0..<cols, id: \.self) { col in
                            Text(emoji)
                                .font(.system(size: emojiSize))
                        }
                    }
                    .offset(x: row % 2 == 0 ? 0 : emojiSize / 2 + spacing / 2)
                }
            }
            .frame(width: patternWidth, height: patternHeight)
            .offset(x: -patternWidth / 3, y: -patternHeight / 3) 
            .clipped()
        }
    }
}

When we use this we can set the position to start just slightly off screen from the top left. We can also use opacity to fade the images out just a bit. I used 0.3 for .simplified LOD and 0.6 for default.

EmojiBackground(emoji: entry.configuration.emoji)
  .position(x: -50, y: -50)
  .opacity(0.3)

What if we want users to pick a value from a list? We’ll need to create a custom type DisplayRepresentation. Let’s make one with three cringy values.

enum DisplayOption: String, AppEnum {
    case live = "Live"
    case laugh = "Laugh"
    case love = "Love"

    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Title Options"

    static var caseDisplayRepresentations: [Self: DisplayRepresentation] = [
        .live: DisplayRepresentation(title: "Live"),
        .laugh: DisplayRepresentation(title: "Laugh"),
        .love: DisplayRepresentation(title: "Love")
    ]
}

Then use in a parameter.

struct ConfigurationAppIntent: WidgetConfigurationIntent {
    ...
    @Parameter(title: "Title", default: .live)
    var display: DisplayOption
    ...
}

That gives us a basic UI for a picker.

Picker for Live, Laugh, and Love

We’ll show the raw value in a Text View. We’ll also adjust the layout based on which value is selected.

struct DisplayLayout: View {
    let display: DisplayOption
    let text: String

    var body: some View {
        GeometryReader { geometry in

            switch display {
            case .live:
                VStack {
                    HStack {
                        DisplayTitle(text: text)
                        Spacer()
                    }
                    Spacer()
                }

            case .laugh:
                VStack {
                    Spacer()
                    DisplayTitle(text: text)
                    Spacer()
                }

            case .love:
                VStack {
                    Spacer()
                    HStack {
                        Spacer()
                        DisplayTitle(text: text)
                    }
                }
            }
        }
    }
}

With all the pieces put together we are left with this.

Video Demo

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

Download the Xcode project with this and many more examples from Step Into Vision.
Some examples are provided as standalone Xcode projects. You can find those here.

Questions or feedback?