ナトーアプリ工房

iOS開発やApple製品の情報発信ブログです

【SwiftUI】UdemyLessonサンプルアプリ解説

Udemy SwiftUIオンラインレッスンのサンプルTODOアプリ解説

  • レッスンでの構造体やクラス、変数の名称が抽象的で値の移り変わりが分かりにくいため、日本語名にしました。
  • 全体が見える様、別ファイルを作らないでContentView.swiftに全て記述しました。

アプリの仕様

・+ボタンでテキストフィールドが表示されてTODOを追加

・TODO項目をクリックすると□が☑︎になりTODO内容に取り消し線が入る

・Deleteボタンをクリックするとチェック済みTODOが消える

 

f:id:nato99001:20210529140107p:plain

  • 構造体を配列にする場合idという変数を持った「Identifiableプロトコル」が必要になります。今回はUUIDメソッドにてユニークなidを生成していますが、手動で生成しても大丈夫です。ユニークなidがないとSwiftUIがそのViewで利用されているデータがどれなのかを特定しておかないと、データが更新されたときにどのViewを再構築すればいいのかわからなくなってしまうからです。
  • タップしたリスト行を検知するのにfirstIndex(of:)メソッドを使っています。その場合「Equatabelプロトコル」が必要になります。
  • テキストフィールドで使うプロパティーラッパー@stateを付けた変数に日本語は使えませんでした。

プログラムソースをgithubにアップしています。

github.com

import SwiftUI

 

struct 構造体:Identifiable,Equatable {

    var id = UUID()

    var 構造体check:Bool

    var 構造体todo:String

    init(イニシャライザ変数check:Bool,イニシャライザ変数todo:String) {

        self.構造体check = イニシャライザ変数check

        self.構造体todo = イニシャライザ変数todo

    }

}

 

class クラス:ObservableObject {

    @Published var 構造体インスタンス配列 = [

        構造体(イニシャライザ変数check: true, イニシャライザ変数todo: "勉強"),

        構造体(イニシャライザ変数check: false, イニシャライザ変数todo: "散歩")

    ]

    @Published var 入力状態チェック変数:Bool = false

}

 

struct ContentView: View {

    @EnvironmentObject var クラスインスタンス:クラス

    var body: some View {

        NavigationView {

            List {

                ForEach(クラスインスタンス.構造体インスタンス配列) { ForEach変数 in

                    Button(action:{

                        guard let 要素番号 = クラスインスタンス.構造体インスタンス配列.firstIndex(of: ForEach変数) else {

                            return

                        }

                        self.クラスインスタンス.構造体インスタンス配列[要素番号].構造体check.toggle()

                    })

                    {

                        リスト行表示ビュー(リスト行表示ビュー変数check: ForEach変数.構造体check, リスト行表示ビュー変数todo: ForEach変数.構造体todo)

                    }

                }

                if self.クラスインスタンス.入力状態チェック変数 {

                    行追加表示ビュー()

                } else {

                    Button(action: {

                        self.クラスインスタンス.入力状態チェック変数 = true

                    })

                    {

                        Text("+")

                        .font(.title)

                    }

                }

            }

            .navigationBarTitle(Text("ToDo"))

            .navigationBarItems(trailing: Button(action:{

                削除関数()

            })

            {

                Text("Delete")

            }

            )

        }

    }

    func 削除関数(){

        let 削除関数変数 = クラスインスタンス.構造体インスタンス配列.filter({!$0.構造体check })

        self.クラスインスタンス.構造体インスタンス配列 = 削除関数変数

    }

}

 

struct リスト行表示ビュー: View {

    var リスト行表示ビュー変数check:Bool

    var リスト行表示ビュー変数todo:String

    var body: some View {

        HStack {

            if リスト行表示ビュー変数check {

                Text("☑︎")

                Text(リスト行表示ビュー変数todo)

                    .strikethrough()

                    .fontWeight(.ultraLight)

            } else {

                Text("□")

                Text(リスト行表示ビュー変数todo)

            }

        }

    }

}

 

struct 行追加表示ビュー: View {

    @State var addListViewTodo = "" //現在propertyWrapperでは変数名に日本語を使えません

    @EnvironmentObject var クラスインスタンス: クラス

    var body: some View {

        TextField("タスクを入力してください", text:$addListViewTodo,

        onCommit: {

            self.todo作成関数()

            self.クラスインスタンス.入力状態チェック変数 = false

        })

    }

    func todo作成関数() {

        let 新しいtodo = 構造体(イニシャライザ変数check: false, イニシャライザ変数todo: addListViewTodo)

        self.クラスインスタンス.構造体インスタンス配列.insert(新しいtodo, at: 0)

        self.addListViewTodo = ""

    }

    

}

 

struct ContentView_Previews: PreviewProvider {

    static var previews: some View {

        ContentView()

            .environmentObject(クラス())

    }

}