Chapter 11無料公開

【Part10】Listを使ってToDoリストアプリを作ってみよう!

Rikuto Sato
Rikuto Sato
2022.11.06に更新


このPartでは、リストを使ってなんちゃってToDoリストを作ってみます。

では、実装していきましょう。ToDoListという名前でプロジェクトを作成してください。

ToDoリスト開発

レイアウト実装

まずは、レイアウトを作っていきましょう。

  1. ナビゲーションバーを作る
    まずは、ナビゲーションバーを作りましょう。以下のようにVStackNavigationStackで囲ってください。
    NavigationStack {
        VStack {
    	Image(systemName: "globe")
    	    .imageScale(.large)
    	    .foregroundColor(.accentColor)
    	Text("Hello, world!")
        }
        .padding()
    }
    
    追記する場所


  2. Listを作る
    次にリストを作りましょう。NavigationStackの中をListに変更してみましょう。
    NavigationStack {
        List {
    	Text("ジョギングする")
    	Text("お花に水をやる")
    	Text("部屋の掃除をする")
    	Text("本を読む")
        }
        .navigationTitle("ToDoリスト")
    }
    
    追記する場所

    実行するとこのようなレイアウトになります。


  3. 左側にアイコンをつける
    Text一つ一つをHStackで囲って、Imageをつけましょう。
    List {
        HStack {
    	Image(systemName: "circle")
    	Text("ジョギングする")
        }
        HStack {
    	Image(systemName: "checkmark.circle.fill")
    	Text("お花に水をやる")
        }
        HStack {
    	Image(systemName: "checkmark.circle.fill")
    	Text("部屋の掃除をする")
        }
        HStack {
    	Image(systemName: "circle")
    	Text("本を読む")
        }
    }
    
    追記する場所

    実行すると、テキストの左側にチェックマークのアイコンが表示されるはずです。

これで見た目はToDoリストっぽくなりました。

ForEachを使う

現時点では、Listの中に、4回もHStackで同じようにセルを記述しています。たくさんのセルを表示するとなると、その度にHStackを記述するので、かなりコードが長くなってしまいます。
Textの内容とImageの名前だけしか変わらないので、その数の分記述するのではなく、その数の分処理を繰り返し行うというふうにしましょう。
そうすると、少ないコードで実装できます。

  1. 配列を宣言

    let taskData = ["ジョギングする", "お花に水をやる", "部屋の掃除をする", "本を読む"]
    
    追記する場所


  2. Listの中を変更
    Listのなく以下のように変更してください。これでだいぶコードが短くなります。

    List(0..<taskData.count, id:\.self) { index in
        HStack {
    	Image(systemName: "circle")
    	Text(taskData[index])
        }
    }
    
    追記する場所

    実行して確認してください。一旦アイコンは⚪︎だけになってしまいますが、先ほどと同じようなレイアウトになるはずです。

機能をつける

では、ToDoリストっぽく、ボタンを押したらチェックマークがつくようにしましょう。

  1. ボタンを作る
    まずは、セルを押せるようにしましょう。以下のようにHStackButtonの中に入れましょう。
    Button {
    } label: {
        HStack {
    	Image(systemName: "circle")
    	Text(taskData[index])
        }
    }
    
    追記する場所

    このようにセルを押せるようになっているはずです。


  2. ボタンの文字色を戻す
    Buttonにしたら文字色が青になってしまったので、以下のモディファイアをつけて黒に戻しましょう。
    .foregroundColor(.black)
    
    追記する場所

    実行して確認してみると黒に戻っているはずです。


  3. データの構成を変更する
    現状のデータは、タスクが終了しているかどうかの値がないため、ボタンを押したらチェックマークがつくという実装ができません。なので、データの構成を変更します。
    クラス外に以下のモデルを追加してください。
    struct TaskData: Identifiable {
        var title: String
        var completed: Bool
        var id = UUID()
    }
    
    追記する場所


  4. taskDataを変更
    taskDataを以下のように変更してください。(現時点ではエラーになります。)
    @State var taskData = [
        TaskData(title: "ジョギングする", completed: false),
        TaskData(title: "お花に水をやる", completed: false),
        TaskData(title: "部屋の掃除をする", completed: false),
        TaskData(title: "本を読む", completed: false)
    ]
    
    追記する場所


  5. エラーの修正
    データの構成を変更したので、Text(taskData[index])のところでエラーになっています。以下のように変更してください。
    Text(taskData[index].title)
    
    追記する場所


  6. Imageを変更
    もし、completedfalseだったら◯で、trueだったら☑️にしましょう。Imageの部分を以下のように変更してください。
    if taskData[index].completed {
        Image(systemName: "checkmark.circle.fill")
    } else {
        Image(systemName: "circle")
    }
    
    追記する場所


  7. 三項演算子に変更
    このくらいのif文なら、三項演算子を使った方が良いです。Imageのif文を以下のように変更しましょう。
    Image(systemName: taskData[index].completed ? "checkmark.circle.fill" : "circle")
    
    追記する場所


  8. ボタンを押したらcompletedを変更
    ボタンを押したら、completedの値を変更するようにしましょう。
    Button {
        taskData[index].completed.toggle()
    } label: {
    
    追記する場所

    実行して確認しましょう。
    セルを押すとチェックマークが付いて、もう一度押すとチェックマークが外れるようになると思います。

バグ修正

シミュレーターをダークモードにしたら、黒背景に黒文字になってしまい、文字が読みづらくなってしまいました。
ダークモードにする方法としては、シミュレーターでcommand + shift + aを押すか、
ホーム画面に戻って設定 > デベロッパ > ダークの外観モードをONにするとダークモードになります。

ボタンに対して、.foregroundColor(.black)というモディファイアをつけたので、強制的に黒になってしまいます。
黒色に黒は見づらいので、ダークモードの時は白色で、ライトモードの時は黒色にしたいです。
なので、以下のように変更してみましょう。.blackではなく、以下のように、.primaryにしましょう。

.foregroundColor(.primary)
追記する場所

これで、ダークモードで実行すると白色になって、ライトモードで実行すると黒色になります。

これで、なんちゃってToDoが完成しました!
ここから、ToDoの入力フォームを作って、データを保存できるようにすれば本格的なToDoになると思います!

全てのコード

全てのコード
import SwiftUI

struct TaskData: Identifiable {
    var title: String
    var completed: Bool
    var id = UUID()
}

struct ContentView: View {
    @State var taskData = [
        TaskData(title: "ジョギングする", completed: false),
        TaskData(title: "お花に水をやる", completed: false),
        TaskData(title: "部屋の掃除をする", completed: false),
        TaskData(title: "本を読む", completed: false)
    ]
    
    var body: some View {
        NavigationStack {
            List(0..<taskData.count, id:\.self) { index in
                Button {
                    taskData[index].completed.toggle()
                } label: {
                    HStack {
                        Image(systemName: taskData[index].completed ? "checkmark.circle.fill" : "circle")
                        Text(taskData[index].title)
                    }
                }
                .foregroundColor(.primary)
            }
            .navigationTitle("ToDoリスト")
        }
    }
}