【iOS】UIButtonの状態をコントールする

　UIButtonの[setTitle(_:for:)](https://developer.apple.com/documentation/uikit/uibutton/1624018-settitle)や[setImage(_:for:)](https://developer.apple.com/documentation/uikit/uibutton/1623997-setimage)メソッドを使用する際に、第2引数のforでUIControl.Stateを指定する必要があります。

## 1. UIControl.Stateとは
![スクリーンショット 2021-12-17 10.49.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/773915/a2156809-ce1c-46e6-820e-6cf646460084.png)
　[公式ドキュメント](https://developer.apple.com/documentation/uikit/uicontrol/state)では以下のように定義されています。
> Constants describing the state of a control. 
>
> 【日本語訳】
> コントロールの状態を表す定数。  
> 　　　　　　　　　　　　　　(Apple Developer Documentationより)

また、概要としては以下のように記載されています。
> A control can have more than one state at a time. Controls can be configured differently based on their state. For example, a UIButton object can be configured to display one image when it is in its normal state and a different image when it is highlighted.
>
> 【日本語訳】
> コントロールは、一度に複数の状態を持つことができます。コントロールは、その状態に応じて異なる設定をすることができます。例えば、UIButton オブジェクトは、通常の状態では 1 つの画像を表示し、ハイライトされた状態では異なる画像を表示するように設定することができます。
> 　　　　　　　　　　　　　　　　　　　　　(Apple Developer Documentationより)

## 2. 「コントロール状態を示す定数」のパターン
　UIControl.Stateの「コントロール状態を示す定数」は、以下の7種類があります。

```swift:コントロール状態
// ① normal：コントロールの通常の状態、つまりデフォルトの状態。つまり、有効になっているが、選択も強調表示もされていない。
static var normal: UIControl.State

// ② highlighted：コントロールの強調表示された状態。
static var highlighted: UIControl.State

// ③ disabled：コントロールの無効状態。
static var disabled: UIControl.State

// ④ selected：コントロールの選択された状態。
static var selected: UIControl.State

// ⑤ focused：コントロールのフォーカスされた状態。
static var focused: UIControl.State

// ⑥ application：アプリケーションで使用できる追加の制御状態フラグ。
static var application: UIControl.State

// ⑦ reserved：内部フレームワークで使用するために予約されている制御状態フラグ。
static var reserved: UIControl.State

```

　サンプルアプリを用いて、それぞれどのような状態を示すのかを確認していきます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/773915/8320d2a3-5a78-b2cf-fff0-5a140330f6a5.png" width="300"> 

```swift:ViewController
import UIKit

// cornerRadiusをstoryboard上で指定するためのカスタムクラス
@IBDesignable
class CustomButton: UIButton {
    @IBInspectable var cornerRadius: CGFloat = 0 {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }
}

class ViewController: UIViewController {
    
    @IBOutlet weak var normalButton: CustomButton!
    @IBOutlet weak var highlightedButton: CustomButton!
    @IBOutlet weak var disabledButton: CustomButton!
    @IBOutlet weak var selectedButton: CustomButton!
    @IBOutlet weak var focusedButton: CustomButton!
    @IBOutlet weak var applicationButton: CustomButton!
    @IBOutlet weak var reservedButton: CustomButton!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
    
    private func setTitle(to button: CustomButton, state: UIControl.State) {
        let title: String = "Hello!!"
        button.setTitle(title, for: state)
    }
}
```
※ @IBDesignableと@IBInspectableに関しては以下の記事が詳細に書かれている
→ [@IBDesignableと@IBInspectableでストーリーボード上に表示されるビューをつくる](http://safx-dev.blogspot.com/2014/11/ibdesignableibinspectable.html)

###① normal
>normal：コントロールの通常の状態、つまりデフォルトの状態。つまり、有効になっているが、選択も強調表示もされていない。

つまり、何も操作していない、そのままのボタンのことを指している。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/773915/0e0d2dcb-2a4f-4826-e2f3-2625b8de9865.png" width="300"> 

```swift:NormalボタンにHello!!を表示
class ViewController: UIViewController {
    
    @IBOutlet weak var normalButton: CustomButton!

    ...
    
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // NormalボタンにHello!!の文字をセットしている
        setTitle(to: normalButton, state: .normal)
    }
    
    private func setTitle(to button: CustomButton, state: UIControl.State) {
        let title: String = "Hello!!"
        button.setTitle(title, for: state)
    }
}
```

###② highlighted
>highlighted：コントロールの強調表示された状態。

つまり、ボタンを長押しタップをしたときの状態を指している。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/773915/2f1798e1-f276-9146-36e6-1f2eb0ae069c.gif" width="300"> 

```swift:Highlightedボタンを長押しタップ時、Hello!!を表示
class ViewController: UIViewController {
    ...

    @IBOutlet weak var highlightedButton: CustomButton!

    ...
    
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Highlightedボタンを長押しタップ時にHello!!の文字をセットしている
        setTitle(to: highlightedButton, state: .highlighted)
    }
    
    private func setTitle(to button: CustomButton, state: UIControl.State) {
        let title: String = "Hello!!"
        button.setTitle(title, for: state)
    }
}
```

###③ disabled
>disabled：コントロールの無効状態。

つまり、ボタンのisEnableがfalseのときの状態のことを指している。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/773915/91fa2fc3-da13-5869-bff7-2e508c78e468.png" width="300"> 

```swift:Disabledボタンがタップできない時Hello!!を表示
class ViewController: UIViewController {
    ...

    @IBOutlet weak var disabledButton: CustomButton!

    ...
    
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // DisabledボタンがタップできないときHello!!の文字をセットする
        disabledButton.isEnabled = false
        setTitle(to: disabledButton, state: .disabled)
    }
    
    private func setTitle(to button: CustomButton, state: UIControl.State) {
        let title: String = "Hello!!"
        button.setTitle(title, for: state)
    }
}
```

###④ selected
>selected：コントロールの選択された状態。

つまり、ボタンのisSelectedがtrueのときの状態のことを指している。
たとえば、写真アプリで写真をお気に入り登録するためのハート型のボタンがあり、このボタンをタップし、お気に入りとして選択すると青色で表示され、選択を解除すると枠線のみで表示される。このようなときに使用されている。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/773915/46264737-e178-e02b-7517-f83d77af03ad.png" width="300"> 

```swift:Selectedボタンが選択状態の時Hello!!を表示
class ViewController: UIViewController {
    ...

    @IBOutlet weak var selectedButton: CustomButton!

    ...
    
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Selectedボタンが選択状態の時Hello!!の文字をセットする
        selectedButton.isSelected = true
        setTitle(to: selectedButton, state: .selected)
    }
    
    private func setTitle(to button: CustomButton, state: UIControl.State) {
        let title: String = "Hello!!"
        button.setTitle(title, for: state)
    }
}
```

###⑤ focused
>focused：コントロールのフォーカスされた状態。

これは主にiOSやiPadOS向けではなく、tvOS向けのものである。
[tvOSのHuman Interface Guideline](https://developer.apple.com/design/human-interface-guidelines/tvos/app-architecture/focus-and-selection/)によると、Focusedという状態は以下のように説明されています。
>The viewer is currently interacting with the item. The item visually stands out from the other content on screen through elevation to the foreground, illumination, and animation in response to subtle finger movements on the remote's touch surface.
>
>【日本語訳】
>ビューアは現在、アイテムを操作しています。アイテムは、リモコンのタッチ面での微妙な指の動きに応じて、前景への仰角、照明、アニメーションを通じて、画面上の他のコンテンツから視覚的に目立ちます。
> 　　　　　　　　　　　　　　　　　　　　　(Human Interface Guidelinesより)

他の状態と比較したものが以下の図になります。
![スクリーンショット 2021-12-18 1.06.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/773915/a1665a6e-6af3-a72e-1447-fa0c86ff9afc.png)
iOS及びiPadOS向けのアプリを開発する場合にはあまり気にしなくてもよいと思います。

###⑥ application
>application：アプリケーションで使用できる追加の制御状態フラグ。

アプリケーション内で新たにコントロールの状態を追加したいときに使用する。
Objective-C時代にはヘッダーファイルで以下のように定義されていた。

```objc:UIControlStateApplicationの値
UIControlStateApplication = 0x00FF0000
```
これは、16進数で定義されている。(0xは16進数であることを意味し、実際の値は00FF0000である)。
これはC言語由来のポインタという概念からきているものだが、つまり、メモリに8bit分のユーザー側が自由に使用することできる容量を確保してくれているというものである。

参考：[UIControlにOptionSetTypeで状態を追加する](https://cockscomb.hatenablog.com/entry/define-my-own-uicontrolstate)

###⑦ reserved
>reserved：内部フレームワークで使用するために予約されている制御状態フラグ。

こちらに関してはあまりよくわかりませんでした笑
もし、こちらに詳しい方がおられましたら、reservedがどういったものなのか、教えていただけると嬉しいです！！

## 3. まとめ
UIControlの中で、UIButtonに使用する可能性が高いものは、.normal、.highlighted、.disabled、.selectedの4つになると思います。これら4つの違いを理解できればほとんどのパターンで困ることはないのではないでしょうか...たぶん...

※今回記載した内容で、「ここがおかしい！！」、「間違っているぞ！！」っていうポイントがあれば教えて頂けると幸いです！！