LoginSignup
8
4

More than 3 years have passed since last update.

UITextViewにPlaceHolderを折り返し有りで追加してみる

Last updated at Posted at 2020-02-16

概要

UITextViewにプレースホルダーを追加してみました。
かつ、プレースホルダーには折り返しをしてくれるようにしてみました。

はじめに

UITextViewにプレースホルダーを追加するだけのやり方であれば、
パイセン等が分かりやすくて素晴らしい記事を公開されてるので、そちらを参考にしてみてください。

UITextViewにプレースホルダーを設定できるようにする(Swift4)
[Swift 4.2] UITextViewにプレースホルダーを追加する[iOS 12]

私自身も大変参考にさせて頂きました🙇‍♂️
ありがとうございます🙇‍♂️🙇‍♂️🙇‍♂️
というか、ほぼほぼベースはパクらせてもらっ

こんなの

まずはじめに、単純にプレースホルダーをUILabelで追加しただけであれば、
こんな感じに折り返しが効かずに見切れてしまっています。

※『UITextViewのPlaceHolderをここに表示してます。』という文言を設定
Simulator Screen Shot - iPhone 8 - 2020-02-16 at 20.23.00.png

それがこんな感じに!
Simulator Screen Shot - iPhone 8 - 2020-02-16 at 20.22.42.png

もちろんxib上でも折り返して表示してくれます
スクリーンショット 2020-02-16 19.42.06.png

ソースコード

プレースホルダーの表示用として、UILabelをaddSubViewしており、
その際に、superViewであるUITextViewに対してAutolayoutの設定を入れることにより、折り返して表示してくれるようになってます。

PlaceHolderTextView.swift
import UIKit

@IBDesignable
class PlaceHolderTextView: UITextView {

    private let placeHolderLabelEdgeInsets = UIEdgeInsets(top: 8, left: 5, bottom: 8, right: 5)

    private var placeHolderLabelWidthAConstraint = NSLayoutConstraint()
    private var placeHolderLabelWidth: CGFloat {
        return frame.width - (placeHolderLabelEdgeInsets.left + placeHolderLabelEdgeInsets.right)
    }

    lazy private var placeHolderLabel: UILabel = {
        let label = UILabel()
        label.numberOfLines = 0
        label.font = font
        label.backgroundColor = .clear
        label.translatesAutoresizingMaskIntoConstraints = false
        addSubview(label)

        placeHolderLabelWidthAConstraint = label.widthAnchor.constraint(equalToConstant: placeHolderLabelWidth)
        NSLayoutConstraint.activate([
            label.topAnchor.constraint(equalTo: topAnchor, constant: placeHolderLabelEdgeInsets.top),
            label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: placeHolderLabelEdgeInsets.left)
        ])

        return label
    }()

    @IBInspectable var placeHolder: String = "" {
        didSet { reloadView() }
    }
    @IBInspectable var placeHolderColor: UIColor = .lightGray {
        didSet { reloadView() }
    }

    override var text: String! {
        didSet { changeVisiblePlaceHolder() }
    }

    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        commonInit()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }

    private func commonInit() {
        changeVisiblePlaceHolder()
        NotificationCenter.default.addObserver(self, selector: #selector(textChanged), name: UITextView.textDidChangeNotification, object: nil)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        placeHolderLabelWidthAConstraint.constant = placeHolderLabelWidth
        placeHolderLabelWidthAConstraint.isActive = true
    }
}

extension PlaceHolderTextView {

    private func reloadView() {
        placeHolderLabel.text = placeHolder
        placeHolderLabel.textColor = placeHolderColor
        changeVisiblePlaceHolder()
    }

    private func changeVisiblePlaceHolder() {
        placeHolderLabel.isHidden = placeHolder.isEmpty || !text.isEmpty
    }

    @objc private func textChanged(notification: NSNotification?) {
        changeVisiblePlaceHolder()
    }

}

おわりに

そもそも、プレースホルダーに折り返しが必要になるほどの長文を書くシチュエーションに遭遇するのでしょうか・・・🤔

追記

@am10 さんからのご指摘&ご提案から、コードベースでテキスト代入時にもプレースホルダーの表示切替が可能になりました!
@yo1106 さんからのご指摘により、コードから生成時の不具合(そもそも考慮してなかった🙈)が発覚し、修正致しました。

8
4
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4