Skip to content

Instantly share code, notes, and snippets.

@mireabot
Created March 12, 2025 17:45
Show Gist options
  • Save mireabot/a6bdc4cdd651c70782cd0438e29fdab2 to your computer and use it in GitHub Desktop.
Save mireabot/a6bdc4cdd651c70782cd0438e29fdab2 to your computer and use it in GitHub Desktop.
WorkoutSetsCard
import SwiftUI
// MARK: - Exercise Data Model
struct ExerciseSet: Identifiable {
let id = UUID()
var weight: Double
var reps: Int
var isCompleted: Bool = false
}
struct Exercise {
var name: String
var sets: [ExerciseSet]
}
struct WorkoutSetsCard: View {
@State private var exercise: Exercise
@State private var selectedSetIndex: Int?
init(exerciseName: String) {
_exercise = State(initialValue: Exercise(
name: exerciseName,
sets: [
ExerciseSet(weight: 82.5, reps: 8),
ExerciseSet(weight: 82.5, reps: 8),
ExerciseSet(weight: 82.5, reps: 8),
ExerciseSet(weight: 82.5, reps: 8)
]
))
}
var body: some View {
VStack(alignment: .leading, spacing: 16) {
VStack(alignment: .leading, spacing: 4) {
Text("Titan Fitness Dumbbell")
.foregroundStyle(.secondary)
Text(exercise.name)
.foregroundStyle(.primary)
}
.font(.system(.title3, weight: .medium))
.padding(.horizontal, 16)
VStack(spacing: 0) {
ForEach(Array(exercise.sets.enumerated()), id: \.element.id) { index, set in
setCard(set: set, index: index)
}
}
HStack {
Button(action: {
withAnimation(.linear(duration: 0.1)) {
exercise.sets.append(ExerciseSet(weight: 82.5, reps: 8))
}
}) {
Text("Add set")
.font(.system(.subheadline, weight: .medium))
.foregroundColor(.secondary)
}
.buttonStyle(.bordered)
.clipShape(Capsule())
Spacer()
Button(action: {}) {
Image(systemName: "chart.xyaxis.line")
.font(.system(.subheadline, weight: .medium))
.foregroundColor(.secondary)
}
.buttonStyle(.bordered)
.clipShape(Circle())
Spacer()
Button(action: {}) {
Text("Previous")
.font(.system(.subheadline, weight: .medium))
.foregroundColor(.secondary)
}
.buttonStyle(.bordered)
.clipShape(Capsule())
}
.padding(.horizontal, 16)
}
.padding(.vertical, 16)
.background(.gray.opacity(0.2))
.cornerRadius(16)
}
// MARK: - Subviews
@ViewBuilder
private func setCard(set: ExerciseSet, index: Int) -> some View {
HStack(spacing: 12) {
Text("\(index + 1)")
.font(.system(.caption, weight: .medium))
.frame(width: 30, height: 30)
.background(set.isCompleted ? Color.green : .gray.opacity(0.3))
.clipShape(Circle())
Spacer()
HStack {
HStack(spacing: 4) {
Text(String(format: "%.1f", set.weight))
.fontWeight(.medium)
Text("kg")
.foregroundStyle(.secondary)
}
.padding(.vertical, 5)
.padding(.horizontal, 8)
.background(.gray.opacity(0.2))
.cornerRadius(8)
Spacer()
Text("×")
.foregroundStyle(.secondary)
Spacer()
HStack(spacing: 4) {
Text("\(set.reps)")
.fontWeight(.medium)
Text("reps")
.foregroundStyle(.secondary)
}
.padding(.vertical, 5)
.padding(.horizontal, 8)
.background(.gray.opacity(0.2))
.cornerRadius(8)
}
Spacer()
Button(action: {
withAnimation(.smooth) {
exercise.sets[index].isCompleted.toggle()
}
}, label: {
Image(systemName: "checkmark")
.foregroundStyle(set.isCompleted ? .white : .secondary)
.font(.system(.caption, weight: .medium))
.frame(width: 30, height: 30)
.background(set.isCompleted ? Color.green : .gray.opacity(0.3))
.clipShape(Circle())
})
.buttonStyle(PlainButtonStyle())
}
.padding(.horizontal, 16)
.padding(.vertical, 12)
.background(set.isCompleted ? .green.opacity(0.2) : .clear)
}
}
#Preview {
WorkoutSetsCard(exerciseName: "Incline Press")
.padding(16)
.preferredColorScheme(.dark)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment