SwiftUI初学者がチーム向けに勉強会資料を作った Part2

　この記事は[フリューAdvent Calendar 2024](https://qiita.com/advent-calendar/2024/furyu)の21日目の記事となります。

## はじめに
　私が所属するチーム向けにSwiftUI勉強会を開催することとなったため、資料を記事として投稿します。SwiftUI初学者なので間違いなどあれば、教えていただきたいです！Part1の記事は[こちら](https://qiita.com/uso_k_sk/items/e546eaad128eef8f5791)

## 目次
- [事前準備](#事前準備)
- [Part2のお品書き](#part2のお品書き)
    - [ユーザー入力で用いる部品](#ユーザー入力で用いる部品)
        - [ボタン](#ボタン)
        - [トグルスイッチ](#トグルスイッチ)
        - [テキストフィールド](#テキストフィールド)
        - [セキュアフィールド](#セキュアフィールド)
- [まとめ](#まとめ)

## 事前準備
　事前準備については[Part1](https://qiita.com/uso_k_sk/items/e546eaad128eef8f5791)をご確認ください。今回は省略させていただきます。主に、Xcodeでのプロジェクト作成の手順を記載しています。

## Part2のお品書き
- [ユーザー入力で用いる部品](#ユーザー入力で用いる部品)
    - [ボタン](#ボタン)
    - [トグルスイッチ](#トグルスイッチ)
    - [テキストフィールド](#テキストフィールド)
    - [セキュアフィールド](#セキュアフィールド)
    - [課題](#課題)
        - [Level1](#level1)
        - [Level2](#level2)
        - [Level3](#level3)

### ユーザー入力で用いる部品
　ボタンやトグルスイッチ、文字入力のテキストフィールドなど、ユーザー入力で使用される部品はたくさんあります。その中から一部をピックアップして紹介します。

#### ボタン
　ユーザーからの入力を受けてアクションする部品として、まずはボタンを使ってみましょう。早速ですが、以下のようにコードを書いてみてください。

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        Button("Button") {
            
        }
    }
}

#Preview {
    ContentView()
}
```

　プレビュー画面を確認すると、デザインは簡素ですが画面中央にボタンが表示されます。

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/080723c2-c173-c4f3-9a77-a503106715fc.png" width = 300>

　また、次のように、```Button```を日本語の```ボタン```に変更してみましょう。

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        Button("ボタン") {
            
        }
    }
}

#Preview {
    ContentView()
}
```

　プレビュー画面を確認すると、表示が日本語の```ボタン```に変更されたことが確認できます。

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/c3de4abf-be64-699b-ca86-e04dd36cfb54.png" width = 300>

　ボタンは押すことができ（当然）、ボタンを押すことで何かしらのアクション（別の画面に遷移するなど）が発生します。続いて、ボタンをタップした時のアクションの実装方法について説明します。
　先ほどの日本語の```ボタン```で実装したコードを見てみましょう。察しのいい方は以下のコードの中で気になる箇所があるのではないでしょうか？

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        Button("ボタン") {
            
        }
    }
}

#Preview {
    ContentView()
}
```

　```Button```の```{}```内にコードが何も記載されていませんね。ボタンを押した時のアクションを実装したい場合、```{}```内にコードを書いていくことで実現することができます。では、ボタンを押すと、Xcodeのデバックエリアにテキストを表示できるようにしてみましょう。デバックエリアに表示は```print```文で実現できます（知ってるわ！って方もいらっしゃると思いますが、念の為書いておきます）。

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        Button("ボタン") {
            print("タップされたよ")
        }
    }
}

#Preview {
    ContentView()
}
```
　次の画像の順に操作してデバックエリアを確認すると、画像のようにデバックエリアに```タップされたよ```と表示されることが確認できると思います。デバックエリアに表示するだけでは、ユーザーはボタンを押しても画面上では何も変化が起きないので、次の章からはボタンを押すと、画面が更新されるような処理を実装してみましょう。

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/002b89c1-ca94-acde-c025-61217d1f9ed1.png">

　ここからは、数字が1ずつ増えるカウントアップボタンを実装して、数字が1ずつ増える様子を見てみましょう。まずは、前述のコードに数字表示用のテキストを追加します。

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Text("1")
            Button("カウントアップ") {
                print("タップされたよ")
            }
        }
    }
}

#Preview {
    ContentView()
}
```

 この状態でプレビュー画面を確認すると、下記のようにボタンの上に数字が表示されるようになりました。

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/b723cb74-d45d-9cb8-311e-18d5ba2da4ab.png" width = 300>

　ただ、このままではボタンを押しても```タップされたよ```しか表示されない上に、数字をカウントアップさせることもできません。そこで、数字をカウントアップさせるために、変数を宣言しましょう。
　```Swift```で変数を宣言したい場合は```var 変数名: 型 = ~~```と宣言します。本来、その変数が何を表しているか理解できる命名にすべきですが、今回は特に問いません。ただ、型は数値を扱うので```Int```型で宣言しましょう（これもprint文の時と同じく、知ってるわ！かもしれませんが念の為です）。そして、宣言した変数をテキストで表示させるようにしましょう。テキストは```String```型で```Int```型の変数```num```をそのまま中に入れることはできないので、下記のように対応します。

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    var num: Int = 0
    var body: some View {
        VStack {
            Text("\(num)")
            Button("カウントアップ") {
                print("タップされたよ")
            }
        }
    }
}

#Preview {
    ContentView()
}
```

　この状態でプレビュー画面を確認すると下記のように表示されると思います。これで、カウントアップした数値を表示させるための変数```num```を用いて、テキストに表示させることができました。

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/aaaeef10-9f31-02f9-f4ee-4fec149eb854.png" width = 300>

　次はボタンをタップすると数字が1増える処理を記載します。具体的にはボタンをタップすると、変数```num```の値を1増やせば良いので以下のようにコードを記載すればよさそうですね。

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    var num: Int = 0
    var body: some View {
        VStack {
            Text("\(num)")
            Button("カウントアップ") {
                num += 1
            }
        }
    }
}

#Preview {
    ContentView()
}
```

　と、思うじゃないですか。上記の通りコードを実装すると、下記のようにXcode上ではエラーが表示されてしまいます。

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/a21dbf20-1f3e-5aac-03b9-c98eeb2fa84a.png">

**エラーメッセージ**
>```Left side of mutating operator isn't mutable: 'self' is immutable```
>直訳：変更演算子の左側は変更可能ではありません: 'self' は不変です

　Swiftでは変数・定数の宣言として```var```で宣言する方法と、```let```で宣言する方法が基本的です。```var```で宣言した変数は何度でも再代入が可能ですが、```let```の場合は一度代入したものを後から別のものを代入して変更することはできません。
　これを踏まえてもう一度エラーメッセージを確認してみましょう。

　おい！```num```は```var```で宣言しているのに変更できないってどういうことや！ってなるかもしれませんね。

　このようなエラーメッセージが表示される原因は構造体```struct```にあります。構造体```struct```内で宣言されたプロパティ（上記のサンプルコードでは```num```）は変更することができません。そのため、先述のエラーメッセージが表示されてしまいます。

　じゃあどうやって値を更新すればいいんだ？になりますが、これを解決する方法として```@State```をつけるという方法があります。```@State```はプロパティの値の変更を監視し、値が変更されたときにViewが再描画されます。```@State```を付与することで上記のエラーメッセージも表示されなくなります。
　では、変数```num```を```@State```を付与した変数に変更し、プレビュー画面のカウントアップボタンを押して値が1ずつ増加するか確認してみましょう。

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @State var num: Int = 0
    var body: some View {
        VStack {
            Text("\(num)")
            Button("カウントアップ") {
                num += 1
            }
        }
    }
}

#Preview {
    ContentView()
}
```

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/e2a34555-2f76-47cc-9241-c9e4e45c28b9.gif" width = 300>

　カウントアップボタンを押すと数字が1ずつ増えるようになりましたね。画面の更新処理の実装方法の説明は以上となります。


#### トグルスイッチ
　トグルスイッチはON/OFFを切り替えるボタンです。iPhoneの設定画面でよく見るアレです。

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/742d068a-9e44-81ad-8450-ad2aad96ec3e.jpeg" width = 300>

　では、早速ですがトグルスイッチの実装をしてみましょう。以下のようにコードを実装してみてください。

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @State var flag = true
    var body: some View {
        Toggle(isOn: flag) {

        }
    }
}

#Preview {
    ContentView()
}
```

　トグルスイッチはユーザーからの入力を受けてON/OFFが切り替わらないといけないため、```@State```をつけた変数```flag```を用意し、```Toggle()```の引数```isOn```に変数```flag```を指定してあげます。これでプレビュー画面を確認してみましょう。

　と、行きたいところなのですが（またかよ）、上記の通りコードを実装すると、下記のようにXcode上ではエラーが表示されてしまいます。

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/60e2fa14-a711-ed4c-9967-ab5bc870e251.png">


**エラーメッセージ**
>```Cannot convert value 'flag' of type 'Bool' to expected type 'Binding<Bool>', use wrapper instead```
>直訳：型 'Bool' の値 'flag' を予期された型 'Binding<Bool>' に変換できません。代わりにラッパーを使用してください`

　変数```flag```は```Bool```型であるのに対し、```Toggle()```の引数```isOn```に指定できる変数の型は```Binding<Bool>```型です。そのため、このエラーメッセージは「トグルが受け付ける引数のパラメータは```Bool```型ではなく、```Binding<Bool>```という型ですよー」という意味になります。
　では、どう修正すれば良いかなのですが、エラーメッセージの最後に```Insert '$'```とFixボタンが表示されていますね。一旦、Fixボタンを押してみましょう。

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/fd89a5da-8c1d-91e0-5c3a-f79bca3da847.png" width = 600>

　すると、flagの前に```$```が挿入され、エラーメッセージも表示されなくなりました。```Binding```についての説明は省きます（私も知らないので勉強します）が、```Binding<型>```の引数にパラメータを指定する場合は```$```をつけないといけないということみたいですね。

　では、これでプレビュー画面を確認してみましょう。

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/c9d3b8cb-01d0-4c8d-aff9-10d909a65ec8.png" width = 300>

　すると、画面右端にトグルスイッチが表示されていることが確認できます。続いて、iPhoneの設定画面のように表示させてみましょう。```Form```を用いて実装してみます。


```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @State var flag = true
    var body: some View {
        Form {
            Toggle(isOn: $flag) {
                Text("Wi-Fi")
            }
        }
    }
}

#Preview {
    ContentView()
}
```

　プレビュー画面を確認すると、iPhoneの設定画面のようにトグルスイッチとテキストが表示されていることが確認できると思います。

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/45076690-6812-2543-22bc-ba81c8b9c406.png" width = 300>

　では、続いてトグルスイッチのON/OFFでテキストの表示を切り替えられるようにしてみましょう。以下のようにコードを実装してください。

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @State var flag = true
    var body: some View {
        Form {
            Toggle(isOn: $flag) {
                Text(flag ? "ON" : "OFF")
            }
        }
    }
}

#Preview {
    ContentView()
}
```

　```flag ? "ON" : "OFF"```について少しだけ解説をします。```flag ? "ON" : "OFF"```の形は**三項演算子**と呼ばれ```論理式 ? 論理式がtrueの時の値 : 論理式がfalseの時の値```という形になっています。上記のコードの場合、変数```flag```が```true```の場合は文字列の```ON```が返却され、```false```の場合は文字列の```OFF```が返却されます。このように実装することでトグルスイッチのON/OFFに合わせてテキストを更新することができます。
 　それでは、プレビュー画面でトグルスイッチのON/OFFを切り替えて文字が切り替わるか確認してみましょう。

<p>
<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/fb40e838-e8b7-6505-e0d4-a694df903995.png" width = 300>
<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/5894bcc6-aa44-03b0-ee60-274eba03c376.png" width = 300>
</p>

　ON/OFFそれぞれで文字が切り替わっていることが確認できました。

#### テキストフィールド
　テキストフィールドはユーザーが自由に文字入力することができるUI部品です。テキストフィールドは文字列を扱うので入力で使用できるのは```String```型です。早速ですが、テキストフィールドを使ってみましょう。

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @State var text = ""
    var body: some View {
        TextField("入力してください", text: $text)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .frame(width: 200)
    }
}


#Preview {
    ContentView()
}
```

　TextFieldの引数として```@State```で宣言されたtextを指定していますが、この中にユーザーから入力された文字が入ります。また、この引数は```Binding<String>```という型であるため、```$text```と指定してください。前述のトグルスイッチの章で紹介したエラーと同様のエラーが表示されてしまいます。
　また、```.textFieldStyle()```というモディファイアがありますが、この引数に```RoundedBorderTextFieldStyle()```を指定してあげることでテキストフィールドに角丸の枠線が表示されます。
　では、プレビュー画面を確認してみましょう。

<p>
<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/e5c2eb4a-d051-022e-f979-02e87cc5e1a2.png" width = 300>
<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/9848e428-9aa4-b657-538b-5b27f28ccc75.png" width = 300>
</p>

　少し確認しづらいですが、テキストフィールド画面中央に表示され、文字入力ができるようになっていること確認できると思います。また、第一引数として指定した```"入力してください"```がプレースホルダ（未入力時、テキストフィールド内に表示されるテキスト）として表示されていることも確認できますね。

　続いて、入力した文字をテキストとして表示させてみましょう。表示させる用のテキストをテキストフィールドの上に実装します。

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @State var text = ""
    var body: some View {
        VStack {
            if !text.isEmpty {
                Text(text)
            }
            TextField("入力してください", text: $text)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .frame(width: 200)
        }
    }
}


#Preview {
    ContentView()
}
```

　テキストフィールドの上に```if```文を実装しています。```text.isEmpty```は```text```の中身が空である時にtrueを返す論理式で、その前に```!```をつけることで空でない時にtrueを返すようにします。こうすることで文字が入力されていないときにテキストは表示されず、文字が入力されると入力された文字が表示されるようになります。
 　では、シミュレータを起動してテキストフィールドに文字を入力してみましょう。

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/d67c124d-3b14-9610-1b62-fca087a094e5.gif" width = 300>

　文字を入力するとテキストフィールドの上部に入力された文字が表示されていますね。ちなみにですがシミュレータでキーボードを表示させて文字を入力したい場合、シミュレータメニューの**I/O** > **Keyboard** > **Toggle Software Keyboard**を選択してください。

　テキストフィールドには文字だけではなく数値を入力する場面もあります。そこで次は数値を入力できるようにしてみましょう。先ほどのコードにモディファイアを1つ追加してください。

```swift:ContentView.swift
import SwiftUI

struct ContentView3: View {
    @State var text = ""
    var body: some View {
        VStack {
            if !text.isEmpty {
                Text(text)
            }
            TextField("入力してください", text: $text)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .keyboardType(.numberPad) // 追加
                .frame(width: 200)
        }
    }
}


#Preview {
    ContentView3()
}
```

　追加したモディファイアは```.keyboardType(.numberPad)```です。キーボードの種類を指定するモディファイアで、.numberPadとすることで0〜9の数字のみのキーボードを表示させることができます。
　では、シミュレータを起動して数字を入力してみましょう。
 
<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/a0a239b1-7074-65ff-dcae-8455e5fbeffc.png" width = 300>

　これで数字の入力ができるようになりましたね。本来は数値以外の文字や指定範囲外の数値が入力された場合などに対応する必要がありますが、今回は省略します。


#### セキュアフィールド
　セキュアフィールドはテキストフィールドと似ていますが、テキストフィールドと異なるのは入力された文字が隠れる仕様になっている点です。よくアプリのアカウントログイン時のパスワード入力欄で使用されています。
　セキュアフィールドの実装方法はテキストフィールドとかなり似ています。早速ですが、セキュアフィールドを使ってパスワード入力欄を作成し、入力されたパスワードをデバックエリアに表示してみましょう。

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @State var text = ""
    var body: some View {
        SecureField("パスワード入力", text: $text)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .frame(width: 200)
            .onSubmit {
                print(text)
                text = ""
            }
    }
}

#Preview {
    ContentView()
}
```

　ここで、あまり見慣れないのが```.onSubmit{}```だと思います。これはキーボード上のreturnキーが押された後に```{}```内の処理を実行してくれます。セキュアフィールドで入力された文字を変数```text```で受け取り、returnキーが押されると```.onSubmit{}```内の```print```文でパスワードを表示し、セキュアフィールド内に入力されたテキストを空文字にしています。
　では、シミュレータを起動してパスワードを入力してみましょう。

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/9d1968d4-dad2-bde1-a8e6-594e86f76adc.png" width = 300>

　パスワードを入力してキーボードのreturnキーを押すと、

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/84ca7c7b-2941-64ec-e165-8579b8374f31.png">

　XCodeのデバックエリアに入力したパスワードを表示させることができました。

　続いて、パスワード入力時によく見る「パスワードは◯文字以上で入力してください」の注意文言をテキストとして表示させてみましょう。今回はパスワードを6文字以上入力してもらうように設定してみます。

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @State var text = ""
    var body: some View {
        VStack {
            SecureField("パスワードを入力", text: $text)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .frame(width: 200)
                .onSubmit {
                    print(text)
                    text = ""
                }
            Text(text.count < 6 ? "6文字以上で入力してください" : "")
                .font(.system(size: 14, weight: .bold, design: .default))
                .foregroundStyle(.red)
        }
    }
}

#Preview {
    ContentView()
}
```

　セキュアフィールドの下に注意文言用のテキストを追加しました。```Text```の中には三項演算子を記載しており、```text.count```は文字数の数を表しています。そのため、文字数が6未満の場合、```6文字以上で入力してください```を表示させ、6以上になった場合は注意文言を表示しない（空文字）にしています。　```.font(.system(size: 14, weight: .bold, design: .default))```でフォントの設定をしています。各種設定は以下のとおりです。

| 引数   | 説明                       | 今回の設定 | 
| ------ | -------------------------- | ---------- | 
| size   | フォントサイズ             | 14         | 
| weight | フォントの太さ             | .bold      | 
| design | フォントタイプ（明朝体等） | .default   | 

　では、シミュレータを確認してパスワードを入力してみましょう。

<p>
<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/2db90470-859b-c922-4365-33e773bed344.png" width = 300>
<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/1320c1f3-91a7-b31b-57c5-44c69a3b84ca.png" width = 300>
</p>

 パスワード未入力時と6文字未満のときは注意文言が表示され、

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/71a48b59-25a6-3d0d-6253-fbd2992e66af.png" width = 300>

　6文字以上入力すると注意文言が表示されなくなりました。次節から課題を掲載しているので取り組んでみてください。

#### 課題
　課題のサンプルコードを記載していますが、サンプルコードと完全一致していなくても他の方法でレイアウトを作成できていればOKです。
##### Level1
　次のような画面を作成してください。また、以下のように各要素を設定してください。

- **各種設定**
- **トグルスイッチ**
    - 最初はOFF
    - 幅：```150```
    - 高さ：指定なし
    - ```マイク```と表示されるテキストを追加する（ラベルが追加できるトグルで実装）
        - フォント設定
            - size：```25```
            - weight：```bold```
            - design：```default```
- **マイクの画像**
    - ON/OFFで画像を切り替えられるようにする
        - 画像は```Image()```で表示できる
        - ONの画像：```mic.fill```
        - OFFの画像：```mic.slash.fill```
    - 画像設定
        - サイズをフレームサイズに合わせる（モディファイア）
        - 縦横比を維持（モディファイア）
        - サイズ：```60x100```

<p></p>

<p>
<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/9d390983-c5db-9296-2747-99154d869e6b.png" width = 300>

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/418fa204-1d03-076a-0f18-57d867374c22.png" width = 300>
</p>

<details><summary>サンプルコード</summary>

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @State var flag = false

    var body: some View {
        VStack {
            Toggle(isOn: $flag, label: {
                Text("マイク")
                    .font(.system(size: 25, weight: .bold, design: .default))
            })
            .frame(width: 150)
            Image(systemName: flag ? "mic.fill" : "mic.slash.fill")
                .resizable()
                .scaledToFit()
                .frame(width: 60, height: 100)
        }
    }
}

#Preview {
    ContentView()
}
```

</details>

##### Level2
　次のようなログイン画面を作成してください。画面レイアウトの各種設定に加え、下記条件を満たしてください。

- **各種設定**
    - 縦に「ログイン画面」のテキスト、ID入力欄（テキストフィールド）、Password入力欄（セキュアフィールド）、ログインボタンを並べる
    - 各UI部品同士は縦方向に```20```のスペースを設けること
<p></p>

- **「ログイン画面」**
    - サイズ：指定なし
    - フォント設定
        - size：```30```
        - weight：```bold```
        - design：```default```
<p></p>

- **ID入力欄（テキストフィールド）**
    - サイズ：```300x50```
    - 角丸の枠線をつける
    - プレースホルダは```ID```

<p></p>

- **Password入力欄（セキュアフィールド）**
    - サイズ：```300x50```
    - 角丸の枠線をつける
    - プレースホルダは```パスワード```

<p></p>

- **ログインボタン**
    - ラベルを指定する（ラベルを指定できるボタンで実装する）
        - ラベル設定
            - 「ログイン」テキスト
            - サイズ：```150x50```
            - 文字色：白
            - 背景色：青
            - 形は```カプセル```
    - ボタンをタップすると「ログイン成功」か「ログイン失敗」が表示される
        - IDとPasswordを確認して成功か失敗を表示させる関数を実装すると良いかも
<p></p>

- **条件**
    - 「ログイン成功」と「ログイン失敗」をデバックエリアに表示させること
        - 成功：下記IDとPasswordでログインボタンをタップ
            - ID：```hoge```
            - Password：```hoge1234```
        - 失敗：上記ID、Password以外、もしくは未入力でログインボタンをタップ

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/cdfdcd49-560b-2c27-34f0-53c48904b9b1.png" width = 300>

<details><summary>サンプルコード</summary>

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @State var id = ""
    @State var password = ""
    private let myId = "hoge"
    private let myPassword = "hoge1234"
    
    var body: some View {
        VStack(spacing: 20) {
            Text("ログイン画面")
                .font(.system(size: 30, weight: .bold, design: .default))
            TextField("ID", text: $id)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .frame(width: 300, height: 50)
            SecureField("パスワード", text: $password)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .frame(width: 300, height: 50)
            Button(action: {
                canLogin(id: id, password: password)
            }, label: {
                Text("ログイン")
                    .frame(width: 150, height: 50)
                    .foregroundStyle(.white)
                    .background(.blue)
                    .clipShape(Capsule())
            })
        }
    }
    
    private func canLogin(id: String, password: String) {
        if id == myId && password == myPassword {
            print("ログイン成功")
        } else {
            print("ログイン失敗")
        }
    }
}

#Preview {
    ContentView()
}
```

</details>


##### Level3
　次のような計算アプリを作成してください。

- **各種設定**
- 「計算アプリ」、計算式作成欄、演算子入力ボタン一覧、「計算する」ボタンはそれぞれのUI部品同士の間に縦方向```30```のスペースを設けること
<p></p>

- **「計算アプリ」**
    - サイズ：指定なし
    - フォント設定
        - size：```30```
        - weight：```bold```
        - design：```default```
<p></p>

- **計算式作成欄**
    - 入力欄、演算子表示用テキスト、入力欄、計算結果表示用テキストの順に横方向へ並べる（部品同士のスペースは横方向に```30```）
    - 入力欄（テキストフィールド）×2
        - サイズ：```70x50```
        - キーボードは数字を入力できるようにする
        - 角丸の枠線をつける
        - プレースホルダは```左辺```と```右辺```
    - 演算子表示用テキスト
        - サイズ：指定なし
        - 演算子（足し算、引き算、掛け算のみ）を表示する
    - 計算結果表示用テキスト
        - サイズ：```70x50```
        - 表示させるテキストは```=計算結果```
<p></p>

- **演算子入力ボタン一覧**
    - 足し算、引き算、掛け算ができるようにする
    - ボタンを押すと演算子表示用テキストに演算子が表示される
    - サイズやモディファイアでの設定は特になし

<p></p>

- **「計算する」ボタン**
    - ボタンを押すと計算結果表示用テキストに計算結果が表示される
    - このボタンを押した時に計算用の関数を呼び出すと良いかも
        - 入力された演算子によって計算を変える必要がありそう
        - ```switch```文を使って計算を切り替えると良さそう
    - サイズやモディファイアでの設定は特になし

<p>
<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/dd6c838a-50ae-3617-f120-1933c8bbaee9.png" width = 300>


<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/0f378a8c-9deb-ba90-255c-6798a4cb5033.png" width = 300>

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/d04572df-edd3-e14d-8b2a-b76359244a49.png" width = 300>

<img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939098/379d9aef-9d29-f68d-a963-d07535f2e22f.png" width = 300>
</p>


<details><summary>サンプルコード</summary>

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @State var num1 = 0
    @State var num2 = 0
    @State var operation = ""
    @State var result = 0
    
    var body: some View {
        VStack(spacing: 30) {
            Text("計算アプリ")
                .font(.system(size: 30, weight: .black, design: .default))
            HStack(spacing: 30) {
                TextField("左辺", value: $num1, format: .number)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .frame(width: 70, height: 50)
                    .keyboardType(.numberPad)
                Text(operation)
                TextField("右辺", value: $num2, format: .number)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .frame(width: 70, height: 50)
                    .keyboardType(.numberPad)
                Text("= \(result)")
                    .frame(width: 70, height: 50)
                    .multilineTextAlignment(.center)
            }
            HStack(spacing: 30) {
                Button("+") {
                    operation = "+"
                }
                Button("-"){
                    operation = "-"
                }
                Button("×") {
                    operation = "×"
                }
            }
            Button("計算する") {
                result = calculation(num1: num1, num2: num2, operation: operation)
            }
            
            
        }
    }
    private func calculation(num1: Int, num2: Int, operation: String) -> Int {
        switch operation {
            case "+":
                return num1 + num2
            case "-":
                return num1 - num2
            case "×":
                return num1 * num2
            default:
                return 0
        }
    }
}

#Preview {
    ContentView()
}
```

</details>




## まとめ
　Part2の勉強会資料は以上となります。Part1の記事を作成した時期と比較して、SwiftUIの理解はより深まってきたと思います。この勉強会資料作成を通して、SwiftUI初学者を脱却したいなと思っています。次回作（Part3）をお楽しみに！