改めてSwiftUIのViewの基礎についておさらいするメモ

# 本記事について

本記事は、以下のセッションを見て改めて重要だと思った内容を復習しながらメモしたものです。

https://youtu.be/HyQgpxX__-A?si=7FQSBrAhTBRlYmN4

# イントロ

SwiftUIは新しいアプリや機能を構築する際に最適なツールと述べています。

その理由は、下記の通りです。

**Rich Feature Set**

SwiftUIは豊富な機能セットを提供しています。開発者はさまざまな機能を簡単に実装することができます。

**Less Code**

これらの豊富な機能を実装するために必要なコード量が少なくて済みます。

**Incremental Adoption**

アプリ全体をSwiftUIで書く必要はありません。
既存のアプリに徐々にSwiftUIを取り入れることができ、必要な部分だけを更新することが可能です。

# Viewの基礎

SwiftUIのViewには3つの特徴があります。

1. Declarative（宣言的）
2. Compositional（構成型）
3. State-Driven（状態駆動）


## 1. Declarative（宣言的）

UIKitのような命令的な実装では、ゴールに到達するまでの各ステップが必要ですが、宣言的な実装では、期待する結果に焦点を当てます。SwiftUIは命令的、宣言的の2つの要素を持っています。

下記に具体的な実装例を示します。

### 実現したいUIを宣言

```swift
Text("test")

Image(systemName: "gear")

Button("Tap") {
  // ボタン押した時の処理
}
```

<img width="150" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3368292/16bf50fb-7c89-84a3-bbde-9e6f38fd2c91.png"><img width="150" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3368292/788ddf19-22cd-7ea4-7667-d510cfb021e5.png"><img width="150" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3368292/2c1c74b9-fa53-64ce-3263-d5e4e58a4aaa.png">

### HStackやVStackなどを使えば、それぞれのパーツの組み合わせを作る

```swift
HStack {
    Image(systemName: "globe")
    Text("Hello")
}
```

<img width="150" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3368292/25948401-6e6b-3acf-4dbe-a5fa4dc25e9c.png">


### Listなどの他のコンテナにも適用

```swift
List(0..<5) {_ in
    HStack {
        Image(systemName: "globe")
        Text("Hello")
    }
}
```

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3368292/fd6ac531-aaba-b756-aa44-6e4688d20092.png">

### 宣言的にUIを配置して、命令的に状態の変更を加える

```swift
Button("Add money") {
    let money = Money(100)
    moneyList.append(money)
}
```

### なぜStructなのか？

Viewは、UIの現在の状態を示し、時間とともに命令を受け取って更新されます。長期間使うインスタンスではないため、classではなくstructを使用しています。

## 2. Compositional（構成型）

### コンテナビューの再配置が簡単

```swift
HStack {
    Label("hello", systemImage: "gear")
    
    Spacer()
    
    Text("walking")
}
```

↓

```swift
HStack {
    Image("pork")
        .resizable()
        .frame(width: 50, height: 50)

    VStack(alignment: .leading) {
        Label("hello", systemImage: "gear")
        Text("walking")
    }

    Spacer()
}
```

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3368292/9f41eb2b-8c36-b0d3-302d-1e4d30b0b38d.png">

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3368292/78372c1f-339b-1adc-03a9-f07b6ff26a7c.png">

### View Modifierは順序に基づいて定義される

```swift
Image("dog")
    .clipShape(.circle) // 2つ目
    .shadow(radius: 20) // 3つ目
    .overlay {
        Circle().stroke(.green, lineWidth: 5) // 4つ目
    }
```

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3368292/654d8e9d-b971-3911-351f-9abfb34bcada.png">


## 3. State-Driven（状態駆動）

状態が変化すると自動的に最新の状態に保つため、更新のバグを防ぐことができます。

### `@State`を用いた状態管理

ボタンが押された後、内部状態でcountがインクリメントされます。
countの変化がViewに通知され、bodyを呼び出すことで値が変更されます。

```swift
struct CounterView: View {
    @State private var count: Int = 0
    
    var body: some View {
        VStack {
            Text("\(count)")
            Button("Add Count") {
                count += 1
            }
        }
    }
}
```

### `@Binding`を用いた状態管理

親子関係のViewを扱う際には、子View（CounterView）がBindingを入力として受け取るように更新します。これにより、双方向で状態変化を参照でき、親Viewの更新が同期されます。

```swift
struct CounterView: View {
    @Binding var count: Float
    var body: some View {
        VStack {
            Text("\(count)")
            Button("Add Count") {
                count += 0.1
            }
        }
    }
}

struct ContentView: View {
    @State private var count: Float = 0.0
    var body: some View {
        VStack {
            Gauge(value: count, in: 0...1) {
                Text("Counter")
            }
            
            CounterView(count: $count)
        }
    }
}
```

# 最後に

WWDC24で基礎的なセッションが公開されましたが、改めて見ると実は知らなかった要素や、感覚的には理解していたものの言語化できていなかった要素があり、良い復習になりました。