Unityでパスワードのマスキングのオン・オフを切り替える

# 記事の目的
　アカウント登録画面において、パスワード入力フォームに加えてパスワード再入力フォームを設けるアプリは多いですが、これはユーザーの離脱率を上げやすい仕様です。一方で、パスワード再入力フォームをなくすだけでは、ユーザーがパスワードをミスタイプしたままアカウント登録してしまう可能性があります。パスワード入力フォームの中でミスタイプがないか、ユーザー自身が確認できる必要があります。
　そこでパスワード入力フォームの中で、パスワードのマスキングのオン・オフを切り替えられる、すなわちパスワードを「***」等の文字で隠すか中身を表示するかを切り替えられる機能を実装します。
# 要約
1. Unityのプロジェクトで、以下のオブジェクトを用意する
    * パスワードのInputField
    * パスワードのマスキングをオフにするButton
    * オンにするButton

2. Buttonを押すと実行される関数の中で、
    * Buttonの表示・非表示を切り替える
    * InputFieldのcontentTypeを切り替える
    * 3で作るコルーチンを開始する

3. コルーチンの中で、
    * InputFieldをアクティブにする
    * InputField内のカーソルを最後尾に移動する

# コード全体
```csharp:PasswordController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PasswordController : MonoBehaviour
{
    public InputField passField;            // パスワードのInputField
    public GameObject maskingOffButton;     // マスキングをオフにするButton
    public GameObject maskingOnButton;      // マスキングをオンにするButton

    public void OnClickMaskingOffButton()   // maskingOffButtonを押すと実行される関数
    {
        maskingOffButton.SetActive(false);
        maskingOnButton.SetActive(true);        
        passField.contentType = InputField.ContentType.Standard;
        StartCoroutine(ReloadInputField());
    }

    public void OnClickMaskingOnButton()    // maskingOnButtonを押すと実行される関数
    {
        maskingOffButton.SetActive(true);
        maskingOnButton.SetActive(false);        
        passField.contentType = InputField.ContentType.Password;
        StartCoroutine(ReloadInputField());
    }

    private IEnumerator ReloadInputField()
    {
        passField.ActivateInputField();
        yield return null;
        passField.MoveTextEnd(true);
    }
}
```

# 1. Unityのプロジェクトでオブジェクトを用意する
　Canvas上で、パスワード入力用のInput Fieldの右横に、マスキングをオフにするButton（目のマーク）を配置します。また、Input Fieldのインスペクターで、Content TypeをPasswordに設定しておきます。
![スクリーンショット 2022-11-29 0.52.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2988723/f8e46b91-c151-7c3a-9462-fd4b0d0ee6a2.png)
　さらに、オフにするButtonと同じ場所に、オンにするButton（目のマークに斜線が重なっている）を配置しますがこちらは最初非表示にしておきます。
![スクリーンショット 2022-11-29 0.55.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2988723/af123fee-b5ed-4c78-87f6-5018aaa90c30.png)

# 2. Buttonを押すと実行される関数を作る
　　オフにするButtonとオンにするButtonでやることはほぼ同じなので、まずオフにするButtonを押すと実行される関数について説明します。

:::note warn
この関数が書かれたスクリプトは、Button以外のオブジェクト（空のゲームオブジェクトなど）にアタッチしてください。Buttonにアタッチしてしまうと、Buttonが途中で非アクティブになるため関数の実行がその時点で止まってしまいます。
:::

```csharp
    public void OnClickMaskingOffButton()
    {
        maskingOffButton.SetActive(false);
        maskingOnButton.SetActive(true);        
        passField.contentType = InputField.ContentType.Standard;
        StartCoroutine(ReloadInputField());
    }
```
最初に、オフにするButtonを非表示にして、オンにするButtonを表示させます。
```csharp
        maskingOffButton.SetActive(false);
        maskingOnButton.SetActive(true);
```
次に、Input FieldのContent TypeをStandardに設定します。
```csharp
        passField.contentType = InputField.ContentType.Standard;
```
　この段階でパスワードを入力してみます。
![スクリーンショット 2022-11-29 1.21.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2988723/6b129274-87c4-384b-f7c2-fa87f09407e5.png)
Buttonを押すと...
![スクリーンショット 2022-11-29 1.19.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2988723/bd44e216-8c7a-cce1-f460-15f5edff5fc8.png)
...Buttonは入れ替わりましたが、パスワードの表示に変化がありません。Content Typeの設定は変更されていますが、その変更を表示に反映させるにはInput Fieldに再度フォーカスする必要があります。そのために、次の3章で作るコルーチンを開始させます。
```csharp
        StartCoroutine(ReloadInputField());
```

# 3. Input Fieldにフォーカスするコルーチンを作る
```csharp
    private IEnumerator ReloadInputField()
    {
        passField.ActivateInputField();
        yield return null;
        passField.MoveTextEnd(true);
    }
```
　コルーチンの中で、まずInput Filedにフォーカスします。
```csharp
        passField.ActivateInputField();
```
ただ、フォーカスするだけだと既に入力した文字が全選択された状態になるので、そのまま新しい文字を入力しようとすると既に入力した文字が消えてしまいます。
![スクリーンショット 2022-11-29 1.47.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2988723/98b41e89-dba3-371d-906c-1ea518bde79c.png)
　そこで、**次のフレームで**Input Field内のカーソルを最後尾に移動する処理をします。こうすると全選択が解除され、既に入力した文字の続きから新しい文字を入力することができます。
```csharp
        yield return null;
        passField.MoveTextEnd(true);
```
次のフレームで処理する訳は、`ActivateInputField()`と`MoveTextEnd(true)`を同一フレームで実行しようとすると、後者が実行されないためです（その理由は[こちらの記事](https://gomafrontier.com/unity/1102 "【Unity】InputFieldにフォーカスした際の全選択を無効化する方法")で触れられています）。そのため、`ActivateInputField()`を実行した後`yield return null`で1フレーム分処理を中断してから`MoveTextEnd(true)`を実行します。これらの処理をコルーチンの中で行っているのも、処理を途中で1フレーム分中断させたいためです。

　これで、パスワードのマスキングをオフにする機能が実装できました！
![スクリーンショット 2022-11-29 2.12.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2988723/c8f2684c-a82c-2e02-ed21-766d3ee2801e.png)

　マスキングをオンにする関数の中身もほぼ同じです。
```csharp
    public void OnClickMaskingOnButton()
    {
        maskingOffButton.SetActive(true);
        maskingOnButton.SetActive(false);        
        passField.contentType = InputField.ContentType.Password;
        StartCoroutine(ReloadInputField());
    }
```
オンにするボタンを非表示にし、オフにするボタンを表示させます。そして、Input FieldのContent TypeをPasswordに設定します。