What You Will Build

An interactive app icon designer that lets users pick an SF Symbol, choose a background color, and adjust corner radius and icon size with sliders. A live preview updates instantly as each parameter changes. This is a great tool for rapid prototyping app icons or teaching SwiftUI data binding.

Why This Pattern Matters

This component demonstrates real-time reactive UI in SwiftUI. Every slider drag and color change immediately reflects in the preview thanks to @State bindings. The same pattern applies to theme editors, avatar builders, and any configuration screen where users need instant visual feedback.

Key SwiftUI Concepts

  • @State properties for selectedIcon, bgColor, cornerRadius, and iconSize drive the entire UI.
  • ColorPicker provides a native color selection experience.
  • Slider with custom ranges for fine-grained control.
  • .gradient modifier on Color turns a flat fill into a subtle gradient automatically.

Step 1: Define the State

Four state properties control the icon preview. Changing any of them triggers an automatic redraw:

@State private var selectedIcon = "star.fill"
@State private var bgColor: Color = .blue
@State private var cornerRadius: CGFloat = 20
@State private var iconSize: CGFloat = 60

let icons = ["star.fill", "heart.fill", "bolt.fill", "moon.fill",
             "sun.max.fill", "leaf.fill", "camera.fill", "music.note",
             "paintbrush.fill", "globe", "airplane", "car.fill"]

Step 2: Build the Live Preview

The preview card uses a RoundedRectangle filled with the selected color's gradient, overlaid with the chosen SF Symbol:

RoundedRectangle(cornerRadius: cornerRadius)
    .fill(bgColor.gradient)
    .frame(width: 120, height: 120)
    .overlay {
        Image(systemName: selectedIcon)
            .font(.system(size: iconSize))
            .foregroundStyle(.white)
    }
    .shadow(color: bgColor.opacity(0.4), radius: 10, y: 5)
    .animation(.spring, value: selectedIcon)
    .animation(.spring, value: bgColor)

Step 3: Create the Icon Picker Grid

A LazyVGrid with 6 columns displays all available icons. The selected icon gets a tinted background:

LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 6), spacing: 10) {
    ForEach(icons, id: \.self) { icon in
        Image(systemName: icon)
            .font(.title2)
            .frame(width: 44, height: 44)
            .background(
                selectedIcon == icon ? bgColor.opacity(0.2) : .clear,
                in: RoundedRectangle(cornerRadius: 8)
            )
            .onTapGesture { selectedIcon = icon }
    }
}
.padding(.horizontal)

Step 4: Add the Adjustment Controls

A ColorPicker and two sliders give fine-grained control over the icon appearance:

ColorPicker("Background Color", selection: $bgColor)
    .padding(.horizontal)

VStack(spacing: 8) {
    HStack {
        Text("Corner Radius")
        Spacer()
        Text("\(Int(cornerRadius))")
    }
    .font(.caption)
    Slider(value: $cornerRadius, in: 0...60)
}
.padding(.horizontal)

VStack(spacing: 8) {
    HStack {
        Text("Icon Size")
        Spacer()
        Text("\(Int(iconSize))")
    }
    .font(.caption)
    Slider(value: $iconSize, in: 20...80)
}
.padding(.horizontal)

Tips and Pitfalls

  • Use .gradient on Color: Adding .gradient to any SwiftUI Color creates a subtle top-to-bottom gradient automatically, making flat colors look richer.
  • Spring animation on icon change gives a smooth bounce when switching symbols instead of an abrupt swap.
  • Shadow color matches background: Using bgColor.opacity(0.4) for the shadow creates a natural colored glow effect.
  • Export tip: To make this a real icon exporter, render the preview into a UIImage using ImageRenderer (iOS 16+).

iOS Version: iOS 16+ (for .gradient modifier and ColorPicker)

Get New Tutorials by Email

No spam. Just clear, practical breakdowns you can apply right away.

Enjoy this tutorial?

Get new practical tech tutorials in your inbox.