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.

Questions or feedback?