xyk blog

最近は iOS 開発の記事が多めです。

UITextView にプレースホルダーを設定できるようにする

検証環境:
Xcode 11.3
Swift 5.1.3

UITextView にプレースホルダーを設定できるカスタムビューを作成した。

import UIKit

@IBDesignable
open class PlaceHolderTextView: UITextView {
    
    @IBInspectable
    open var placeHolderText: String = "" {
        didSet {
            placeHolderLabel.text = placeHolderText
            updatePlaceHolder()
        }
    }
    
    open var placeHolderLabel: UILabel = .init()
    
    open override func awakeFromNib() {
        super.awakeFromNib()
        configure()
    }

    open override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        configure()
    }
    
    // text プロパティに直接文字列をセットした場合、textViewDidChange は呼ばれないので override している
    open override var text: String! {
        didSet {
            updatePlaceHolder()
        }
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    private func configure() {
        
        placeHolderLabel.lineBreakMode = .byWordWrapping
        placeHolderLabel.numberOfLines = 0
        placeHolderLabel.font = font
        placeHolderLabel.textColor = .lightGray
        addSubview(placeHolderLabel)
        placeHolderLabel.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            placeHolderLabel.topAnchor.constraint(equalTo: topAnchor, constant: textContainerInset.top),
            placeHolderLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: textContainer.lineFragmentPadding),
            placeHolderLabel.widthAnchor.constraint(equalTo: widthAnchor)
        ])
        
        updatePlaceHolder()
        
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(textViewDidChange(_:)),
            name: UITextView.textDidChangeNotification,
            object: nil)
    }
    
    private func updatePlaceHolder() {
        placeHolderLabel.isHidden = placeHolderText.isEmpty || !text.isEmpty
    }
    
    @objc private func textViewDidChange(_ notification: Notification) {
        updatePlaceHolder()
    }
}