Selectable Tile
Interactive card with active/selected border highlight for option grids.
SwiftUI Implementation
Pure Vanilla Swift • No DependenciesselectableTile.swift
import SwiftUI
struct UseSelectableTile: View {
@State private var selection: SelectableOption = .option1
private static func playHaptic() {
#if os(iOS)
let impact = UIImpactFeedbackGenerator(style: .soft)
impact.impactOccurred()
#endif
}
private let tileWidth: CGFloat = 160
private let tileHeight: CGFloat = 140
private let horizontalSpacing: CGFloat = 12
private let carouselHorizontalPadding: CGFloat = 16
var body: some View {
VStack {
ScrollViewReader { scrollViewProxy in
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: horizontalSpacing) {
ForEach(SelectableOption.allCases, id: \\.self) { option in
SelectableTile(
selectableOption: option,
isSelected: option == selection
)
.frame(width: tileWidth, height: tileHeight)
.contentShape(Rectangle())
.onTapGesture {
UseSelectableTile.playHaptic()
withAnimation(.snappy(duration: 0.25, extraBounce: 0.05)) {
selection = option
}
}
.id(option)
}
}
.padding(.horizontal, carouselHorizontalPadding)
.padding(.vertical, 8)
}
.ignoresSafeArea(.container, edges: .horizontal)
.onChange(of: selection) {
withAnimation(.snappy(duration: 0.35, extraBounce: 0.08)) {
scrollViewProxy.scrollTo(selection, anchor: .leading)
}
}
.onAppear {
DispatchQueue.main.async {
withAnimation(.snappy(duration: 0.35)) {
scrollViewProxy.scrollTo(selection, anchor: .center)
}
}
}
}
}
}
}
enum SelectableOption: Identifiable, Hashable, CaseIterable, Codable {
case option1
case option2
case option3
var id: SelectableOption { self }
var displayName: String {
switch self {
case .option1:
"Nice Option"
case .option2:
"Better Option"
case .option3:
"Great Option"
}
}
var description: String {
switch self {
case .option1:
"This is a nice option to go for."
case .option2:
"This is a better option to go for."
case .option3:
"But this option is great!"
}
}
}
struct SelectableTile: View {
let selectableOption: SelectableOption
let isSelected: Bool
private let cornerRadius: CGFloat = 12
private let selectedStrokeWidth: CGFloat = 2.5
private let checkmarkPadding: CGFloat = 6
var body: some View {
ZStack(alignment: .topTrailing) {
VStack(alignment: .leading, spacing: 5) {
Text(selectableOption.displayName)
.font(.system(size: 16))
.foregroundColor(.primary)
.lineLimit(2)
.minimumScaleFactor(0.8)
Text(selectableOption.description)
.font(.system(size: 13))
.foregroundColor(.secondary)
.lineLimit(5)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
Spacer(minLength: 0)
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.thinMaterial, in: RoundedRectangle(cornerRadius: cornerRadius, style: .continuous))
.overlay {
if isSelected {
RoundedRectangle(cornerRadius: cornerRadius - (selectedStrokeWidth / 2))
.stroke(Color.accentColor, lineWidth: selectedStrokeWidth)
.padding(selectedStrokeWidth / 2)
}
}
if isSelected {
Image(systemName: "checkmark.circle.fill")
.font(.title3)
.foregroundColor(Color.accentColor)
.background(Circle().fill(.background.opacity(0.6)).blur(radius: 1.0))
.padding(checkmarkPadding)
.transition(.scale.combined(with: .opacity))
}
}
.animation(.easeInOut(duration: 0.2), value: isSelected)
}
}Ready to copy and paste directly into any Xcode project running iOS 16.0+.
Recommended Use Cases
- Dashboard overviews, stats tracking, and metric highlights
Component Details
- Category
- Tiles & Selectables
- iOS Compatibility
- iOS 16.0+
- Xcode Version
- Xcode 15+
- Platform
- iOS
- License Tier
- Pro
- Themes & Contexts
- dashboard
- Keywords
- selection carousel
C
Compot for iOS
Native SwiftUI playground app
Preview all 130+ components interactively on your device and copy code straight to Xcode with 1 tap.
Get Compot on the App StoreRelated SwiftUI Components
More components in Tiles & Selectables and related app architectures.
ButtonsiOS 16.0+
Bouncy Button Icon
Interactive button featuring fluid spring animations and smooth scale feedback upon tap.
View Component & Code
ButtonsiOS 16.0+
Button with Repeat Behavior
Stepper-like button with continuous press-and-hold repeating action for counters and volume adjustment.
View Component & Code
ButtonsiOS 16.0+
Button for Copying
Smart copy button that temporarily switches icon and text to "Copied!" with tactile haptic feedback.
View Component & Code