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
.gradientto 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
UIImageusingImageRenderer(iOS 16+).
iOS Version: iOS 16+ (for .gradient modifier and ColorPicker)