What You Will Build
A custom dropdown selector that expands with spring animation, shows a scrollable list of options, and collapses on selection. This replaces Picker when you need full control over appearance.
The Complete Dropdown
struct AnimatedDropdown: View {
@State private var isExpanded = false
@State private var selected = "Select an option"
let options = ["Swift", "Kotlin", "Dart", "TypeScript", "Rust"]
var body: some View {
VStack(alignment: .leading, spacing: 0) {
// Header
HStack {
Text(selected)
.foregroundStyle(selected == "Select an option" ? .gray : .primary)
Spacer()
Image(systemName: "chevron.down")
.rotationEffect(.degrees(isExpanded ? -180 : 0))
}
.padding()
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 12))
.onTapGesture {
withAnimation(.spring(duration: 0.3)) { isExpanded.toggle() }
}
// Options
if isExpanded {
VStack(alignment: .leading, spacing: 0) {
ForEach(options, id: \.self) { option in
Text(option)
.padding(.horizontal).padding(.vertical, 12)
.frame(maxWidth: .infinity, alignment: .leading)
.background(selected == option ? Color.blue.opacity(0.1) : .clear)
.onTapGesture {
selected = option
withAnimation(.spring(duration: 0.3)) { isExpanded = false }
}
Divider()
}
}
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 12))
.transition(.opacity.combined(with: .move(edge: .top)))
}
}
.zIndex(1)
.padding()
}
}
Tips
- zIndex is critical so expanded options render above content below.
- Chevron rotation gives clear open/close feedback.
- Add outside-tap-to-close with a transparent overlay in production.
iOS Version: iOS 15+