0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【SwiftUI】前回のToDoListアプリに「完了率のグラフ」を追加してみた

Last updated at Posted at 2025-11-04

こんにちは!前回の記事(SwiftUIでToDoListアプリを作ってみた)https://qiita.com/kenshin_jp_13/items/9d6a8706828cdb856bfb では、タスクの追加・削除・チェック機能を実装しました。
今回はその続きとして、「タスク完了率をグラフで可視化する」機能を追加しました!📊
SwiftUIの Chartsフレームワーク を使って、完了タスクと未完了タスクの割合を円グラフで表示しています。

🎯 今回の目的
完了タスクと未完了タスクの割合を見える化する
モチベーションを上げるために達成感を「数値化」する
SwiftUI Charts の使い方を学ぶ

💡 完成イメージ
Simulator Screenshot - iPhone 16 - 2025-11-04 at 21.09.46.png

グラフの中央に完了率を表示して、下には完了数・未完了数などを表示するようにしました。

🧩 Chart.Swiftに構造体を追加

import Foundation

struct TaskStat: Identifiable {
    let id = UUID()
    let category: String
    let count: Int
}

🧩 ListViewModelに統計用プロパティを追加
既存の ListViewModel に、完了・未完了数や完了率を計算するプロパティを追加します。

    class ListViewModel: ObservableObject {
        var completedCount: Int {
            todoList.filter { $0.isChecked }.count
        }
        
        var incompletedCount: Int {
            todoList.filter { !$0.isChecked }.count
        }
        
        var completionRate: Double {
            guard !todoList.isEmpty else { return 0 }
            return (Double(completedCount) / Double(todoList.count)) * 100
        }
        
        var taskStats: [TaskStat] {
            [TaskStat(category: "未完了", count: incompletedCount), TaskStat(category: "完了", count: completedCount)]
        }
    }

これで、どこからでも ListVM.completionRate のように呼び出して統計情報を使えるようになります。

📊 統計画面(ChartView)
新しく ChartView を作成します。
ListViewModel をそのまま受け取ってグラフを描画します。

import SwiftUI
import Charts

struct ChartView: View {
    @ObservedObject var ListVM = ListViewModel()
    var body: some View {
        NavigationStack {
            VStack(spacing: 30) {
                Text("タスク統計")
                    .font(.title.bold())
                    .padding(.top, 20)
                
                Chart {
                    ForEach(ListVM.taskStats, id: \.category) { stat in
                        SectorMark(angle: .value("割合", stat.count),
                                   innerRadius: .ratio(0.6),
                                   outerRadius: .ratio(1.0)
                        )
                        .foregroundStyle(by: .value("カテゴリ", stat.category))
                    }
                }
                .frame(height: 250)
                .padding()
                
                VStack(spacing: 12) {
                    Text("総タスク数:\(ListVM.todoList.count)")
                    Text("完了タスク数:\(ListVM.completedCount)")
                    Text("未完了タスク数:\(ListVM.incompletedCount)")
                    Text("完了率:\(ListVM.completionRate, specifier: "%.1f")%")
                }
                .font(.headline)
                .padding(.bottom, 30)
            }
            .padding()
            .navigationTitle("統計")
            .navigationBarTitleDisplayMode(.inline)
        }
    }
}

🔗 HomeViewからTabViewで統計画面へ遷移
今回はTabViewを使ってListViewとChartViewで分けました。

import SwiftUI

struct HomeView: View {
    @State var selectedTab = 1
    var body: some View {
        TabView(selection: $selectedTab) {
            NavigationStack {
                ListView()
            }
            .tabItem {
                Label("リスト", systemImage: "list.bullet")
            }
            .tag(1)
            
            NavigationStack {
                ChartView()
            }
            .tabItem {
                Label("グラフ", systemImage: "chart.pie.fill")
            }
            .tag(2)
        }
    }
}

✅ 実行結果
タスクを完了にすると、グラフがリアルタイムで更新され、
完了率も自動で変化するようになりました。
「達成率が上がる→モチベーションも上がる」
という最高のループが完成です🔥

✨ まとめ
SwiftUIのChartsを使えば、たった数行でグラフ表示ができる
HomeViewModelを拡張するだけで統計画面がすぐ作れる
見た目と体験の両方が向上する

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?