🧩 はじめに
以前投稿した「ToDoListアプリで完了率をグラフ表示する」記事 https://qiita.com/kenshin_jp_13/items/52219325c7924075b039 で、
タスクにチェックを入れてもグラフの完了率が変わらないという不具合がありました。
今回はその原因と、正しく反映されるように修正したコードを紹介します。
💥 問題の症状
タスク一覧とグラフを TabView で切り替えるようにしていたのですが、
チェックを入れてもグラフ側が更新されないという状態でした。
⚠️ 問題のコード(修正前)
struct ChartView: View {
@ObservedObject var ListVM = ListViewModel()
var body: some View {
VStack {
Chart {
ForEach(ListVM.taskStats, id: \.category) { stat in
SectorMark(angle: .value("割合", stat.count))
}
}
Text("完了率:\(ListVM.completionRate, specifier: "%.1f")%")
}
}
}
ListView 側でも同じように
@StateObject var ListVM = ListViewModel()
を使っていました。
🎯 原因
SwiftUIの @StateObject / @ObservedObject は、
各ビューが独自に持つインスタンスになります。
つまり:
📋 ListView → 自分専用の ListViewModel()
📊 ChartView → 別の ListViewModel()
結果、タブごとに別々のデータを見ていたため、
リストで変更してもグラフには反映されなかった、というわけです。
✅ 解決方法:ViewModelを共有する
アプリ全体で同じデータを使いたいときは、
上位ビューで @StateObject を1つだけ作り、
下位ビューには .environmentObject() で渡すのが正解です。
🟢 修正版コード
🔹 HomeView.swift
struct HomeView: View {
@State var selectedTab = 1
@StateObject var ListVM = ListViewModel()
var body: some View {
TabView(selection: $selectedTab) {
NavigationStack {
ListView()
.environmentObject(ListVM)
}
.tabItem {
Label("リスト", systemImage: "list.bullet")
}
.tag(1)
NavigationStack {
ChartView()
.environmentObject(ListVM)
}
.tabItem {
Label("グラフ", systemImage: "chart.pie.fill")
}
.tag(2)
}
}
}
🔹 ListView.swiftと🔹 ChartView.swiftで
@EnvironmentObject var ListVM: ListViewModel
🎉 修正後の挙動
✅ タスクをチェックした瞬間にグラフも自動更新
✅ 削除や追加もリアルタイム反映
✅ TabView間でデータが完全に同期
SwiftUIの @EnvironmentObject を使うことで、
複数のビューで同じViewModelを安全に共有できるようになりました。