【Swift】UITextViewにプレースホルダーをつける

#はじめに
テキストビューにもテキストフィールドのようにプレースホルダーをつけたい時があるかと思います。
なので、今回はテキストビューにプレースホルダーをつけるにはどうすればいいのかを解説します。

#完成するもの

![ezgif.com-gif-maker (5).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954451/5d8b0a93-b3f6-c2db-30dd-f8089456a2cf.gif)


#GitHub

https://github.com/0429oonishi/TextView-placeholder

#実装
まずは、PlaceTextViewというカスタムクラスを作るためのswiftファイルとxibファイルを作成してください。
![ScreenShot 2021-06-12 11.44.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954451/04244f8e-25dc-0d11-056f-f51c2678a287.png)

コードを書いていきます。

```swift
final class PlaceTextView: UITextView {
    
    var placeHolder: String = "" {
        willSet {
            self.placeHolderLabel.text = newValue
            self.placeHolderLabel.sizeToFit()
        }
    }
    
    private lazy var placeHolderLabel: UILabel = {
        let label = UILabel()
        label.lineBreakMode = .byWordWrapping
        label.numberOfLines = 0
        label.font = self.font
        label.textColor = .gray
        label.backgroundColor = .clear
        label.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(label)
        return label
    }()
    
    override func awakeFromNib() {
        super.awakeFromNib()
        
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(textDidChanged),
                                               name: UITextView.textDidChangeNotification,
                                               object: nil)
        
        NSLayoutConstraint.activate([
            placeHolderLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 7),
            placeHolderLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 7),
            placeHolderLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 5),
            placeHolderLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 5)
        ])
        
    }
    
    @objc private func textDidChanged() {
        let shouldHidden = self.placeHolder.isEmpty || !self.text.isEmpty
        self.placeHolderLabel.alpha = shouldHidden ? 0 : 1
    }
    
}
```

Main.storyboardでUITextViewを配置します。

![ScreenShot 2021-06-12 11.46.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954451/cbe31aac-798f-7357-405a-d37993ea149c.png)

配置したUITextViewのカスタムクラスに先ほど書いたカスタムクラスを継承させます。n

![ScreenShot 2021-06-12 11.46.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954451/99d20930-958c-64b5-92ac-98aa7997d6e5.png)

ViewControllerではどのようなプレースホルダーを表示させるのかを設定します。

```swift
final class ViewController: UIViewController {
    
    @IBOutlet private weak var textView: PlaceTextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        textView.placeHolder = "入力してください。"
        
    }
    
}
```

#解説

表示させるプレースホルダーのテキストをこちらで指定し、入ってきた値をラベルのテキストにします。

```swift
    var placeHolder: String = "" {
        willSet {
            self.placeHolderLabel.text = newValue
            self.placeHolderLabel.sizeToFit()
        }
    }
```

ラベルの設定をします。

```swift
    private lazy var placeHolderLabel: UILabel = {
        let label = UILabel()
        label.lineBreakMode = .byWordWrapping
        label.numberOfLines = 0
        label.font = self.font
        label.textColor = .gray
        label.backgroundColor = .clear
        label.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(label)
        return label
    }()
```
オブザーバーを追加します。`UITextView.textDidChangeNotification`なので、このオブザーバーはテキストが入力または消去された時に通知されます。

```swift
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(textDidChanged),
                                           name: UITextView.textDidChangeNotification,
                                           object: nil)
```
先ほど追加したオブザーバーで通知されるたびに呼ばれるメソッドです。
初回で入力されたまたは全てテキストが消去されたときにalphaを変更することで表示非表示を切り替えます。

```swift
    @objc private func textDidChanged() {
        let shouldHidden = self.placeHolder.isEmpty || !self.text.isEmpty
        self.placeHolderLabel.alpha = shouldHidden ? 0 : 1
    }
```
オートレイアウトの指定です。これを指定しないと左上つめつめになってしまうので余白として制約を貼りました。

```swift
    NSLayoutConstraint.activate([
        placeHolderLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 7),
        placeHolderLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 7),
        placeHolderLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 5),
        placeHolderLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 5)
    ])
```

あとはViewControllerでIBOutlet接続をしてプレースホルダーを設定してあげましょう。

```swift
final class ViewController: UIViewController {
    
    @IBOutlet private weak var textView: PlaceTextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        textView.placeHolder = "入力してください。"
        
    }
    
}
```


#おわりに
公式でサポートしてほしいですね、、、