// // SFSymbolPicker.swift // Workouts // // Copyright 2025 Rouslan Zenetl. All Rights Reserved. // import SwiftUI struct SFSymbolPicker: View { @Environment(\.dismiss) private var dismiss @Binding var selection: String @State private var searchText: String = "" private let columns = [ GridItem(.adaptive(minimum: 50, maximum: 60)) ] var body: some View { NavigationStack { ScrollView { LazyVGrid(columns: columns, spacing: 16) { ForEach(filteredSymbols, id: \.self) { symbol in Button { selection = symbol dismiss() } label: { VStack { Image(systemName: symbol) .font(.title2) .frame(width: 44, height: 44) .background( RoundedRectangle(cornerRadius: 8) .fill(selection == symbol ? Color.accentColor : Color.secondary.opacity(0.2)) ) .foregroundColor(selection == symbol ? .white : .primary) } } .buttonStyle(.plain) } } .padding() } .searchable(text: $searchText, prompt: "Search symbols") .navigationTitle("Choose Icon") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Cancel") { dismiss() } } } } } private var filteredSymbols: [String] { if searchText.isEmpty { return Self.workoutSymbols } return Self.workoutSymbols.filter { $0.localizedCaseInsensitiveContains(searchText) } } // Curated list of workout/fitness-related SF Symbols static let workoutSymbols: [String] = [ // Fitness & Exercise "dumbbell.fill", "dumbbell", "figure.strengthtraining.traditional", "figure.strengthtraining.functional", "figure.cross.training", "figure.core.training", "figure.cooldown", "figure.flexibility", "figure.pilates", "figure.yoga", "figure.highintensity.intervaltraining", "figure.mixed.cardio", "figure.rower", "figure.elliptical", "figure.stair.stepper", "figure.step.training", // Running & Walking "figure.run", "figure.run.circle", "figure.run.circle.fill", "figure.walk", "figure.walk.circle", "figure.walk.circle.fill", "figure.hiking", "figure.outdoor.cycle", "figure.indoor.cycle", // Sports "figure.boxing", "figure.kickboxing", "figure.martial.arts", "figure.wrestling", "figure.gymnastics", "figure.handball", "figure.basketball", "figure.tennis", "figure.badminton", "figure.racquetball", "figure.squash", "figure.volleyball", "figure.baseball", "figure.softball", "figure.golf", "figure.soccer", "figure.american.football", "figure.rugby", "figure.hockey", "figure.lacrosse", "figure.cricket", "figure.table.tennis", "figure.fencing", "figure.archery", "figure.bowling", "figure.disc.sports", // Water Sports "figure.pool.swim", "figure.open.water.swim", "figure.surfing", "figure.waterpolo", "figure.rowing", "figure.sailing", "figure.fishing", // Winter Sports "figure.skiing.downhill", "figure.skiing.crosscountry", "figure.snowboarding", "figure.skating", // Climbing & Adventure "figure.climbing", "figure.equestrian.sports", "figure.hunting", // Mind & Body "figure.mind.and.body", "figure.dance", "figure.barre", "figure.socialdance", "figure.australian.football", // General Activity "figure.stand", "figure.wave", "figure.roll", "figure.jumprope", "figure.play", "figure.child", // Health & Body "heart.fill", "heart", "heart.circle", "heart.circle.fill", "bolt.heart.fill", "bolt.heart", "waveform.path.ecg", "lungs.fill", "lungs", // Energy & Power "bolt.fill", "bolt", "bolt.circle", "bolt.circle.fill", "flame.fill", "flame", "flame.circle", "flame.circle.fill", // Timer & Tracking "stopwatch", "stopwatch.fill", "timer", "timer.circle", "timer.circle.fill", "clock", "clock.fill", // Progress & Goals "trophy.fill", "trophy", "trophy.circle", "trophy.circle.fill", "medal.fill", "medal", "star.fill", "star", "star.circle", "star.circle.fill", "target", "scope", "chart.bar.fill", "chart.line.uptrend.xyaxis", "arrow.up.circle.fill", // Misc "scalemass.fill", "scalemass", "bed.double.fill", "bed.double", "moon.fill", "moon", "sun.max.fill", "sun.max", "drop.fill", "drop", "leaf.fill", "leaf", "carrot.fill", "carrot", "fork.knife", "cup.and.saucer.fill", ] } #Preview { SFSymbolPicker(selection: .constant("dumbbell.fill")) }