Created
March 12, 2025 17:45
-
-
Save mireabot/a6bdc4cdd651c70782cd0438e29fdab2 to your computer and use it in GitHub Desktop.
WorkoutSetsCard
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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