【SwiftUI】Buttonビューを基本から学ぶ

# SwiftUIのButton

SwiftUIのボタンは多用途で、高度にカスタマイズ可能です。
そして、シンプルかつ慣れ親しんだ方法で、アプリでタスクを実行する手段を提供します。
ボタンの機能をユーザーに明確に伝えるには、3つの属性を組み合わせます。

- **スタイル**（サイズや色、形状に基づいた視覚的な様式）
- **コンテンツ**（ボタンがその目的を伝えるために表示するシンボルやアイコン、テキストラベル、またはその両方）
- **役割**（ボタンを意味的に区別しその外観に影響を与えうる、システムで定義された役割）

万能ボタンに加えて、多くの一般的なボタンのスタイルは、システム全体で使い慣れたタスク（情報表示、閉じる、連絡先の追加など）を実行します。
外観と動作に明確な違いがある、特定の使い道に対応したボタンのコンポーネント（[トグル](https://developer.apple.com/design/human-interface-guidelines/toggles)、[ポップアップボタン](https://developer.apple.com/design/human-interface-guidelines/pop-up-buttons)、[セグメント・コントロール](https://developer.apple.com/design/human-interface-guidelines/segmented-controls)など）もあります。

> Swiftを基礎から学ぶには
自著、工学社より発売中の「[まるごと分かるSwiftプログラミング](https://amzn.asia/d/7YPWJwl)」をお勧めします。変数、関数、フロー制御構文、データ構造はもちろん、構造体からクロージャ、エクステンション、プロトコル、クロージャまでを基礎からわかりやすく解説しています。
> また、Swiftプログラミングを基礎から動画で学びたい方には、Udemyコース「[今日からはじめるプログラミング](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwii1qTuj_2BAxV4hlYBHbj5BqEQFnoECBYQAQ&url=https%3A%2F%2Fwww.udemy.com%2Fcourse%2Fstart-programming-today%2F&usg=AOvVaw2vs4Sccq-bpfA4ZD2rCyNm&opi=89978449)」をお勧めします。

### 理想的なボタン

直感的で上手に設計されているアプリでは、一目でボタンを理解できるはずです。

**ボタンを使いやすくする**
ユーザーが視覚的に周囲のコンポーネントやコンテンツとボタンを区別できるようにするには、ボタンの周りに十分なスペースを確保します。
ボタンの周囲にスペースを確保することは、ユーザーがそれを選択したりアクティブにしやすくするためにも重要です。
ユーザーの入力が指先、ポインタ、視線、またはリモコンかどうかにかかわらず、簡単に選択できるようにするには、原則として、少なくとも44x44ポイントのヒット領域がボタンには必要です。（なお、visionOSでは60x60ポイント）

**各ボタンが目的を明確に伝えていることを確実にする**
ボタンには必ずテキストラベルかシンボル（あるいはアイコン）を含めます。その両方の組み合わせることもあります。
そうすることで、人々は「そのボタンが何をするためのものか」を予測しやすくなります。

### スタイル

システムのボタンは、広範なカスタマイズ（組み込まれたインタラクション状態、アクセシビリティサポート、および外観適応など）をサポートするさまざまなスタイルを提供します。
定義されているスタイルはプラットフォーム毎に異なるので、アプリでのアクションの優先度がわかりやすくなります。

スタイルとサイズを自在に組み合わせて、システムボタンを設定できます。
デフォルトのシステムボタンは、サイズ固有のコーナー半径とアプリのアクセントカラーを使用します。
必要に応じて、コンテンツのレイアウトとアクティビティインジケーターの存在を制御する属性に加えて、これらの属性を変更できます。

**ビューで最も可能性の高いアクションには、ボタンの背景を見えるようにする。**
塗りつぶしや背景付きのボタンは視覚的に最も目立ちやすく、ユーザーが望む可能性の最も高いアクションをすばやく特定するのに役立ちます。

**視覚的に目立つボタンは、ビューあたり1つか2つまでにすべきです。**
派手なボタンが多すぎると認知負荷が増加し、意図する選択肢を選ぶのに時間がかかってしまいます。

**大きさに頼らず、視覚的に好ましいスタイルの選択肢を選んで、ボタンを区別します。**
いくつかの選択肢にまとまった意図があることを示すには、それらの選択肢について同じサイズのボタンを提供します。
一連の選択肢について優先度や頻度の高さを強調したい場合は、そのボタンにより目立つスタイルを使用して、それ以外には目立たないスタイルを使用してください。

### コンテンツ

**ボタンには「それが何をするのか」を理解しやすくするコンテンツを作成します。**
プラットフォームによっては、ボタンはアイコンやテキスト（あるいは、その両方）を含めることができます。

> Note
macOSとvisionOSでは、ユーザーがボタン上をホバリングすると、システムによってツールチップが表示されます。ツールチップは手短なボタンの説明です。
参考 [Offering help](https://developer.apple.com/design/human-interface-guidelines/offering-help).

**ユーザーが慣れ親しんだアクションを実行するボタンには、アイコンの使用を検討します。**
例えば、`square.and.arrow.up`シンボルのボタンなら、ユーザーは「共有アクティビティに役立つ」と予測できます。
ボタンにアイコンを使用することが理にかなっている場合は、既存または独自の[シンボル](https://developer.apple.com/design/human-interface-guidelines/sf-symbols)を使用できるか検討してください。

**アイコンよりも短いラベルのほうが明確な場合は、テキストの使用を検討します。**
テキストを使用するには、いくつかの単語で「何のボタン」かを簡潔に説明します。
[タイトル形式](https://help.apple.com/applestyleguide/#/apsgb744e4a3?sub=apdca93e113f1d64)で、ボタンの働きがわかりやすい動詞で始まるラベルにすることを検討してください。
例えば、アイテムを買い物かごに追加するボタンならば、「Add to Cart」にすると良いかもしれません。

### 役割

システムボタンは以下に挙げる役割のいずれかを担う。

- 通常 *Normal*
- 優先 *Primary*（ユーザーの選ぶ可能性が最も高い、デフォルトの操作）
- 取り消し *Cancel*（現在のアクションをキャンセルする）
- 破壊的 *Destructive*（データを破壊する可能性のあるアクション）

ボタンの役割は、設定した外観にいっそうの影響を与えます。
例えば、システムは優先のボタンラベルに太字テキストを使用します。
そして、破壊的役割のボタンには赤いテキストを使用します。

**役割「優先」は、選択される可能性の最も高いボタンに割り当てます。**
優先なボタンがリターンキーに応答すると、ユーザーが自分の選択をすばやく確認しやすくなります。

さらに、一時的なビュー（[シート](https://developer.apple.com/design/human-interface-guidelines/sheets)、編集可能なビュー、[アラート](https://developer.apple.com/design/human-interface-guidelines/alerts)など）のボタンに役割「優先」を割り当てると、リターンキーでビューを自動的に閉じることができます。

**破壊的なアクションを実行するボタンには、役割「優先」を割り当てないでください。（例え、そのアクションが最も可能性の高い選択であっても）**
その視覚的に目立ちすぎると、ユーザーは何も読まず真っ先に優先ボタンを選択することがあります。
非破壊的な操作ボタンの役割を優先とすることで、コンテンツの損失を避られるようにします。

## コード

![スクリーンショット 2024-06-01 15.16.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70476/c5ac31f3-407c-ebb3-01ef-8850ca82cc8e.png)

### イニシャライザ

ボタンはタップして、ユーザーがアプリと対話できるようにする。
ユーザーがボタンをタップすると、`action`クロージャ内にコードが実行されます。

```swift: Button型イニシャライザの宣言
Button(_ titleKey: LocalizedStringKey, action: () -> Void)
```

`action`パラメータの`() -> Void`は、最も単純な形式のクロージャです。
中括弧`{ }`のペアで作成して、そこに実行したいコードを含めることができます。

```swift: 末尾クロージャ形式の初期化コード
Button("Add One") {
    number += 1
}
```

上記の例は、イニシャライザの最終パラメータがクロージャである場合に使用できる、末尾クロージャ構文を示しています。
必要に応じて引数ラベルを含めることもできます。
末尾クロージャではない方法でボタンを初期化するコードは、次のとおりです。

```swift: 引数ラベル付きの初期化コード
Button("Add One", action: {
    number += 1
})
```

### アクセシビリティとスタイル

`buttonStyle`モディファイアはボタンに境界線を追加して、タップできる領域を明確にします。

```swift: buttonStyleモディファイア
Button("Add One") {
    number += 1
}
.buttonStyle(.bordered)
```

イニシャライザに`systemImage`引数を追加して、ボタンのラベルに「シンボルとテキストの組み合わせ」を使用します。
境界線がないボタンは小さいので、フォントサイズを調節してタップしやすくします。

```swift:テキストとシンボルのラベル
Button("Add One", systemImage: "plus.circle.fill") {
    number += 1
}
.font(.title)
```

「1を足す」ボタンは、シンボルだけで「それが何をするか」をユーザーは理解できます。
`.labelStyle`モディファイアは、ボタンのテキストラベルを非表示にします。
ただし、「音声案内などの機能に依存するユーザー」が利用するために、ボタンにテキストが表示されるかどうかにかかわらず、常にテキストラベルが必要です。

```swift:ラベルスタイルをシンボルだけにする
Button("Add One", systemImage: "plus.circle.fill") {
    number += 1
}
.font(.title)
.labelStyle(.iconOnly)
```

### ボタンを無効にする

ボタンのタップを防ぐために`disabled`モディファイアを使用すると、ボタンをグレーアウトして「タップしても応答しない」という視覚的な合図を与えることができます。
ここでは`number`変数が`10`の場合、ボタンを無効にします。

```swift: 条件に基づいて、ボタンを無効にするdisabledモディファイア
Button("Add One") {
    number += 1
}
.font(.title)
.labelStyle(.iconOnly)
.disabled(number == 10)
```

### アニメーション

古い画像から新しい画像へフェードするには、`withAnimation`メソッドを使用して変更をアニメーションします。
`withAnimation`は「コード内で発生する状態変更をアニメーション化する」ように、SwiftUIに指示します。

```swift: 画像の切り替えにアニメーションを導入する
Button("Add One") {
    withAnimation {
        number += 1
    }
}
.font(.title)
.labelStyle(.iconOnly)
.disabled(number == 10)
```

## 参考

[Develop in Swift Tutorials](https://developer.apple.com/tutorials/develop-in-swift/buttons-and-state-conclusion)
[Buttons in the Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/buttons)