
Fitness Activity
by dockui
Modern fitness goal tracker UI template, featuring progress tracking, and statistics summaries. User-friendly interface designed for mobile
- Platform
- SwiftUI
- Code
- Native Swift
- Format
- Screen
- Access
- Free
Code Snippet
import SwiftUI
import Charts
struct Activity: Identifiable {
let id = UUID()
let type: ActivityType
let duration: TimeInterval
let caloriesBurned: Int
let date: Date
let heartRate: [Int] // BPM readings
let distance: Double? // In kilometers
}
enum ActivityType: String, CaseIterable {
case running = "Running"
case cycling = "Cycling"
case swimming = "Swimming"
case yoga = "Yoga"
case hiking = "Hiking"
var icon: String {
switch self {
case .running: "figure.run"
case .cycling: "figure.outdoor.cycle"
case .swimming: "figure.pool.swim"
case .yoga: "figure.mind.and.body"
case .hiking: "figure.hiking"
}
}
var color: Color {
switch self {
case .running: .blue
case .cycling: .green
case .swimming: .cyan
case .yoga: .purple
case .hiking: .orange
}
}
}
struct DailyGoal {
let calories: Int
let minutes: Int
let progress: Double
}
struct FitnessActivityView: View {
@State private var selectedTimeRange: TimeRange = .week
@State private var showingActivityDetail: Activity?
enum TimeRange: String, CaseIterable {
case day = "Day"
case week = "Week"
case month = "Month"
case year = "Year"
}
// Dummy Data
let dailyGoal = DailyGoal(calories: 500, minutes: 30, progress: 0.75)
let activities: [Activity] = [
Activity(
type: .running,
duration: 1800, // 30 minutes
caloriesBurned: 320,
date: Date().addingTimeInterval(-3600),
heartRate: Array(120...150),
distance: 5.2
),
Activity(
type: .cycling,
duration: 3600, // 1 hour
caloriesBurned: 450,
date: Date().addingTimeInterval(-86400),
heartRate: Array(110...140),
distance: 15.0
),
Activity(
type: .yoga,
duration: 2700, // 45 minutes
caloriesBurned: 180,
date: Date().addingTimeInterval(-172800),
heartRate: Array(90...120),
distance: nil
)
]
var body: some View {
NavigationStack {
ScrollView {
VStack(spacing: 24) {
activityRings
timeRangeSelector
statsOverview
recentActivities
}
.padding()
}
.navigationTitle("Activity")
.sheet(item: $showingActivityDetail) { activity in
ActivityDetailSheet(activity: activity)
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button(action: {}) {
Image(systemName: "plus.circle.fill")
.foregroundStyle(.blue)
.font(.title3)
}
}
}
}
}
private var activityRings: some View {
VStack(spacing: 16) {
ZStack {
// Activity Ring
Circle()
.stroke(Color.gray.opacity(0.2), lineWidth: 20)
.frame(width: 200, height: 200)
Circle()
.trim(from: 0, to: dailyGoal.progress)
.stroke(
AngularGradient(
gradient: Gradient(colors: [.blue, .green]),
center: .center,
startAngle: .degrees(0),
endAngle: .degrees(360)
),
style: StrokeStyle(lineWidth: 20, lineCap: .round)
)
.frame(width: 200, height: 200)
.rotationEffect(.degrees(-90))
VStack {
Text("\(Int(dailyGoal.progress * 100))%")
.font(.system(size: 44, weight: .bold))
Text("of daily goal")
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
HStack(spacing: 24) {
GoalMetric(value: dailyGoal.calories, unit: "cal", icon: "flame.fill", color: .orange)
GoalMetric(value: dailyGoal.minutes, unit: "min", icon: "clock.fill", color: .green)
}
}
.padding()
.background(Color(.systemBackground))
.clipShape(RoundedRectangle(cornerRadius: 20))
.shadow(color: .black.opacity(0.1), radius: 10)
}
private var timeRangeSelector: some View {
HStack {
ForEach(TimeRange.allCases, id: \.self) { range in
Button(action: { selectedTimeRange = range }) {
Text(range.rawValue)
.fontWeight(.medium)
.padding(.horizontal, 16)
.padding(.vertical, 8)
.background(
selectedTimeRange == range ?
Color.blue : Color(.systemGray6)
)
.foregroundStyle(selectedTimeRange == range ? .white : .primary)
.clipShape(Capsule())
}
}
}
}
private var statsOverview: some View {
VStack(alignment: .leading, spacing: 16) {
Text("Statistics")
.font(.title3)
.fontWeight(.semibold)
LazyVGrid(columns: [
GridItem(.flexible()),
GridItem(.flexible())
], spacing: 16) {
StatCard(
title: "Total Distance",
value: String(format: "%.1f km", activities.compactMap { $0.distance }.reduce(0, +)),
icon: "figure.walk",
color: .blue
)
StatCard(
title: "Calories",
value: "\(activities.map { $0.caloriesBurned }.reduce(0, +)) cal",
icon: "flame.fill",
color: .orange
)
StatCard(
title: "Time",
value: "\(Int(activities.map { $0.duration }.reduce(0, +) / 60)) min",
icon: "clock.fill",
color: .green
)
StatCard(
title: "Activities",
value: "\(activities.count)",
icon: "figure.run",
color: .purple
)
}
}
}
private var recentActivities: some View {
VStack(alignment: .leading, spacing: 16) {
Text("Recent Activities")
.font(.title3)
.fontWeight(.semibold)
ForEach(activities) { activity in
ActivityRow(activity: activity)
.onTapGesture {
showingActivityDetail = activity
}
}
}
}
}
struct GoalMetric: View {
let value: Int
let unit: String
let icon: String
let color: Color
var body: some View {
VStack(spacing: 4) {
Image(systemName: icon)
.font(.title2)
.foregroundStyle(color)
Text("\(value)")
.font(.title3)
.fontWeight(.semibold)
Text(unit)
.font(.caption)
.foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity)
}
}
struct StatCard: View {
let title: String
let value: String
let icon: String
let color: Color
var body: some View {
VStack(alignment: .leading, spacing: 12) {
HStack {
Image(systemName: icon)
.foregroundStyle(color)
Text(title)
.foregroundStyle(.secondary)
}
.font(.subheadline)
Text(value)
.font(.title2)
.fontWeight(.bold)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(.systemBackground))
.clipShape(RoundedRectangle(cornerRadius: 15))
.shadow(color: .black.opacity(0.1), radius: 5)
}
}
struct ActivityRow: View {
let activity: Activity
var body: some View {
HStack(spacing: 16) {
Image(systemName: activity.type.icon)
.font(.title2)
.frame(width: 40, height: 40)
.background(activity.type.color.opacity(0.1))
.clipShape(Circle())
.foregroundStyle(activity.type.color)
VStack(alignment: .leading, spacing: 4) {
Text(activity.type.rawValue)
.fontWeight(.semibold)
Text(activity.date, style: .relative)
.font(.caption)
.foregroundStyle(.secondary)
}
Spacer()
VStack(alignment: .trailing, spacing: 4) {
Text("\(Int(activity.duration / 60)) min")
.fontWeight(.medium)
Text("\(activity.caloriesBurned) cal")
.font(.caption)
.foregroundStyle(.secondary)
}
}
.padding()
.background(Color(.systemBackground))
.clipShape(RoundedRectangle(cornerRadius: 15))
.shadow(color: .black.opacity(0.1), radius: 5)
}
}
struct ActivityDetailSheet: View {
let activity: Activity
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationStack {
ScrollView {
VStack(spacing: 24) {
// Activity Summary
HStack(spacing: 20) {
DetailMetric(title: "Duration", value: "\(Int(activity.duration / 60))", unit: "min", icon: "clock.fill", color: .blue)
if let distance = activity.distance {
DetailMetric(title: "Distance", value: String(format: "%.1f", distance), unit: "km", icon: "figure.walk", color: .green)
}
DetailMetric(title: "Calories", value: "\(activity.caloriesBurned)", unit: "cal", icon: "flame.fill", color: .orange)
}
.padding()
// Heart Rate Chart
VStack(alignment: .leading, spacing: 8) {
Text("Heart Rate")
.font(.headline)
Chart {
ForEach(Array(activity.heartRate.enumerated()), id: \.offset) { index, bpm in
LineMark(
x: .value("Time", index),
y: .value("BPM", bpm)
)
.foregroundStyle(Color.red.gradient)
}
}
.frame(height: 200)
}
.padding()
}
}
.navigationTitle(activity.type.rawValue)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Done") { dismiss() }
}
}
}
}
}
struct DetailMetric: View {
let title: String
let value: String
let unit: String
let icon: String
let color: Color
var body: some View {
VStack(spacing: 8) {
Image(systemName: icon)
.font(.title2)
.foregroundStyle(color)
VStack(spacing: 4) {
Text(value)
.font(.title2)
.fontWeight(.bold)
Text(unit)
.font(.caption)
.foregroundStyle(.secondary)
}
Text(title)
.font(.caption)
.foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity)
.padding()
.background(Color(.systemGray6))
.clipShape(RoundedRectangle(cornerRadius: 15))
}
}
More Templates
View All TemplatesSimilar Blogs
View All Articles
How to Translate Xcode String Catalogs with AI
Learn how to efficiently translate Xcode string catalogs using AI fast, streamlining localization for your iOS and SwiftUI applications.

How to optimize your SwiftUI app in 2026
Discover top strategies to optimize your SwiftUI app in 2026, enhancing performance, responsiveness, and maintainability with modern concurrency.

SwiftUI: iOS Confirmation Alert (Confirm + Cancel)
SwiftUI iOS confirmation alert with Confirm and Cancel buttons. Beginners guide to building safe actions, destructive deletes, and commit dialogs.

SwiftUI: IOS Haptic Feedback Guide 2026
Add click to vibrate haptic feedback in SwiftUI. Full guide to UIImpactFeedbackgenerator, success, error, selection, and tap vibration patterns with code.
Code copied to clipboard!