✏️
iOS | Placeholder つきの TextView を作る
作りたいもの
何も入力されていない場合は Placeholder が表示され、何かしら入力されると Placeholder が消える TextView を作ります。
Android だと EditText に hint を設定すれば一瞬で実装できるのですが、iOS は自分で実装する必要があるらしく。。どうにかなりませんか Apple さん。。
実装方法
- TextView を継承した HintTextView クラスを作る
- TextView の中に Label を置く
- TextView に文字が入力されているかどうかで Label の表示/非表示を切り替える
import UIKit
// 1. TextView を継承した HintTextView クラスを作る
@IBDesignable
class HintTextView: UITextView {
// Placeholder として表示するテキスト
var hintText = "" {
didSet {
hintLabel.text = hintText
}
}
private lazy var hintLabel: UILabel = {
let label = UILabel()
label.lineBreakMode = .byWordWrapping
label.backgroundColor = .clear
label.numberOfLines = 0
label.textColor = .systemGray
return label
}()
// 追記: テキストの初期値を入れるときは textViewDidChange が発火しないのでこちらのメソッドを使用する
// (もっと良い方法がありそうですが...)
func setText(_ text: String) {
self.text = text
changeVisibility()
}
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
private func configure() {
hintLabel.translatesAutoresizingMaskIntoConstraints = false
// 2. TextView の中に Label を置く
addSubview(hintLabel)
// Placeholder の表示位置を調整
NSLayoutConstraint.activate([
// hintLabe とその親である HintTextView のトップの余白
hintLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 5),
// hintLabe とその親である HintTextView で X 軸を一致させる
hintLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor),
// hintLabe はその親である HintTextView よりも幅を 10 小さくする(左右に 5 ずつ余白を設ける)
hintLabel.widthAnchor.constraint(equalTo: self.widthAnchor, constant: -10),
])
self.delegate = self
}
private func changeVisibility() {
if self.text.isEmpty {
hintLabel.isHidden = false
} else {
hintLabel.isHidden = true
}
}
}
extension HintTextView: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
// 3. TextView に文字が入力されているかどうかで Label の表示/非表示を切り替える
changeVisibility()
}
}
個人的に hintLabel の位置の調整でつまずきました。
制約を何もつけない場合、TextView のカーソルの位置と Placeholder の位置にズレが生じて違和感のある表示になるので、位置を調整する必要があります。また、Placeholder が複数行になっても対応できる必要があります。
最初は hintLabel と親(HintTextView)の間に 上、左、右それぞれ 5 ずつ余白を空ける制約をつけていたのですが、これだと Placeholder が長い場合に改行してくれませんでした。
テキストが長いと親 View を突き抜けて右に伸び続けてしまうようです。
最終的には X 軸を親 View と一致させ、幅を親 View より 10 小さくすることで意図どおりの挙動になってくれました。
使い方
TextView を適当な箇所に置きます。今回は画面全体が TextView になるように制約をつけています。
設置した TextView の Custom Class に HintTextView を指定します。
HintTextView と ViewController を @IBOutlet で繋ぎ込み、HintTextView.hintText
に任意のテキストを入力します。
hintTextView.hintText = "テキストを入力してください"
これで冒頭の GIF 画像のような TextView が表示できたと思います。
Discussion