【SwiftUI入門講座Part9】Listを使ってToDoリストアプリを作ってみよう!

作成中です。

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

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

レイアウト実装

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

STEP.1
ナビゲーションバーを作る

まずは、ナビゲーションバーを作りましょう。以下のように変更してください。

var body: some View {
    NavigationView {
        Text("Hello, world!")
            .padding()
            .navigationTitle("ToDoリスト")
    }
}

STEP.2
Listをつくる

次にリストを作りましょう。

NavigationView {
    List {
        Text("ジョギングする")
        Text("お花に水をやる")
        Text("部屋の掃除をする")
        Text("本を読む")
    }
    .navigationTitle("ToDoリスト")
}

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

STEP.3
左側にアイコンをつける

一つ一つHStackで囲んで、Imageをつけましょう。

NavigationView {
    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("本を読む")
        }
    }
    .navigationTitle("ToDoリスト")
}

このような見た目になるはずです。

これで見た目はいい感じになりました。

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView {
            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("本を読む")
                }
            }
            .navigationTitle("ToDoリスト")
        }
    }
}

ForEachで書く

現時点では、Listの中に、4回目もHStackを書いています。ですが、なるべく同じような処理はひとまとまりにした方が綺麗です。

ということで、何回も書いているHStackをまとめましょう。

STEP.1
配列を宣言

まずは、データを配列で持ちましょう。

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

STEP.2
Llistの中を変更

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

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

実行して確認してみてください。アイコンは◯だけになっちゃいますが、先ほどと同じレイアウトになるはずです。

コード解説
List(0..<taskData.count, id:\.self) { index in

List()の中に、0..<5と書くと、中の処理が5回繰り返されセルが5つ表示されます。

今回は、taskDataの数だけ表示したいので、0..<taskData.countというふうにしています。taskDataは、4個あるので、実質0..<4と書いているのと同じです。

indexはなんなのかというと、繰り返している回数が入ります。繰り返し1回目はindexが1で実行されます。2回目はindexが2で実行されます。というふに現時点の繰り返しの回数が入ります。

Text(taskData[index])

taskData[0]"ジョギングする"が取得できます。taskData[1]だと、"お花に水をやる"が取得できます。というふうに、taskData[]の中に番号を入れることで、値を取得できます。

今回は、そこにindexを入れることで、すべてのデータを表示させています。

これで、コードが少なくなり、わかりやすいコードになりました。

import SwiftUI

struct ContentView: View {
    let taskData = ["ジョギングする", "お花に水をやる", "部屋の掃除をする", "本を読む"]
    
    var body: some View {
        NavigationView {
            List(0..<taskData.count, id:\.self) { index in
                HStack {
                    Image(systemName: "circle")
                    Text(taskData[index])
                }
            }
            .navigationTitle("ToDoリスト")
        }
    }
}

機能をつける

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

STEP.1
ボタンを作る

まずは、セルを押せるようにしましょう。以下のようにHStackButtonの中に入れましょう。

Button(action: {
    print("セルが押されたよ")
}) {
    HStack {
        Image(systemName: "circle")
        Text(taskData[index])
    }
}

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

STEP.2
ボタンの文字色を戻す

Buttonにしたら文字色が青になってしまったので、以下のモディファイアをつけて黒に戻します。

Button(action: {
    print("セルが押されたよ")
}) {
    HStack {
        Image(systemName: "circle")
        Text(taskData[index])
    }
}
.foregroundColor(.black)

黒に戻りました。

STEP.3
データの構成を変更する

現状のデータは、タスクが終了しているかどうかの値がないため、ボタンを押したらチェックマークがつくという実装ができません。なので、データの構成を変更します。

クラス外に以下のモデルを追加してください。

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

STEP.4
taskDataを変更

taskDataを以下のように変更してください。(現時点ではエラーになります。)

@State var taskData = [
    TaskData(title: "ジョギングする", completed: false),
    TaskData(title: "お花に水をやる", completed: false),
    TaskData(title: "部屋の掃除をする", completed: false),
    TaskData(title: "本を読む", completed: false)
]

STEP.5
エラーの修正

データを変更したので、Text(taskData[index])のところでエラーになっています。以下のように変更してください。

HStack {
    Image(systemName: "circle")
    Text(taskData[index].title)
}

STEP.6
Imageを変更

もし、completedfalseだったら◯で、trueだったら☑️にしましょう。Imageの部分を以下のように変更してください。

if taskData[index].completed {
    Image(systemName: "checkmark.circle.fill")
} else {
    Image(systemName: "circle")
}

STEP.7
三項演算子に変更

このくらいのif文なら、三項演算子を使った方が良いです。Imageのif文を以下のように変更しましょう。

Image(systemName: taskData[index].completed ? "checkmark.circle.fill" : "circle")

STEP.8
ボタンを押したらcompletedを変更

ボタンを押したら、completedの値を変更するようにしましょう。

Button(action: {
    print("セルが押されたよ")
    taskData[index].completed.toggle()
}) {

セルを押すとチェックマークがついてもう一度押すとチェクマークが外れるようになると思います。

コード解説

Bool型に.toggle()とつけると、値が逆になります。つまり、falseだったらtrueになり、trueだったらfalseになります。

これで、なんちゃって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 {
        NavigationView {
            List(0..<taskData.count, id:\.self) { index in
                Button(action: {
                    print("セルが押されたよ")
                    taskData[index].completed.toggle()
                }) {
                    HStack {
                        Image(systemName: taskData[index].completed ? "checkmark.circle.fill" : "circle")
                        Text(taskData[index].title)
                    }
                }
                .foregroundColor(.black)
            }
            .navigationTitle("ToDoリスト")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
次回の講座:Part10

coming soon…