Fitness Activity

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))
    }
}

Similar Blogs

View All Articles
How to Translate Xcode String Catalogs with AI

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.

by dockui
May 4
How to optimize your SwiftUI app in 2026

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.

by dockui
Apr 29
SwiftUI: iOS Confirmation Alert (Confirm + Cancel)

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.

by dockui
Nov 11
SwiftUI: IOS Haptic Feedback Guide 2026

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.

by dockui
Nov 11

Code copied to clipboard!