在日常开发中时常会遇到为 TextView 设置placeholder 的需求。 基本的思想是这个样子的: 1.在 UITextView 的左上角上放一个 UILabel,让 UILabel 的 text 来作为 UITextView的 placeholder; 2.为 UITextView添加一个通知NotificationCenter,监听 UITextView 中值得变化; 3.UITextView 的值发生变化就把UILabel 隐藏或者显示。
来贴代码:(代码中有很详细的注释,如果使用的话可以直接新建一个文件把代码粘贴过去就可以了)
import UIKit// @IBDesignable 可以让自定义的这个 view 在 Xib 中使用@IBDesignablepublic class YHYPlaceholderTextView: UITextView { // 默认的占位字的颜色 private struct Constants { static let defaultiOSPlaceholderColor = UIColor(red: 179/255, green: 179/255, blue: 179/255, alpha: 0.2) } // 占位 label public let placeholderLabel: UILabel = UILabel() // 使用 AutoLayout 的约束集合 private var placeholderLabelConstraints = [NSLayoutConstraint]() // @IBInspectable xib 中可以设置这些属性 这个属性是 text @IBInspectable public var placeholder: String = "" { didSet { placeholderLabel.text = placeholder } } // placeholder 的颜色,有默认值 defaultiOSPlaceholderColor @IBInspectable public var placeholderColor: UIColor = YHYPlaceholderTextView.Constants.defaultiOSPlaceholderColor { didSet { placeholderLabel.textColor = placeholderColor } } // textView 的字体大小重写, 默认placeholder 的字体大小和 textView一样 override public var font: UIFont! { didSet { if placeholderFont == nil { placeholderLabel.font = font } } } // placeholder 的字体大小 public var placeholderFont: UIFont? { didSet { let font = (placeholderFont != nil) ? placeholderFont : self.font placeholderLabel.font = font } } // 对齐方式 override public var textAlignment: NSTextAlignment { didSet { placeholderLabel.textAlignment = textAlignment } } override public var text: String! { didSet { textDidChange() } } override public var attributedText: NSAttributedString! { didSet { textDidChange() } } // 设置 UIlabel 的位置约束 override public var textContainerInset: UIEdgeInsets { didSet { updateConstraintsForPlaceholderLabel() } } // 初始化的时候 添加通知,和作为placeholder的UILabel override public init(frame: CGRect, textContainer: NSTextContainer?) { super.init(frame: frame, textContainer: textContainer) commonInit() } // 初始化的时候 添加通知,和作为placeholder的UILabel required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) commonInit() } // 初始化的时候 添加通知,和作为placeholder的UILabel private func commonInit() { NotificationCenter.default.addObserver(self, selector: #selector(textDidChange), name: NSNotification.Name.UITextViewTextDidChange, object: nil) placeholderLabel.font = font placeholderLabel.textColor = placeholderColor placeholderLabel.textAlignment = textAlignment placeholderLabel.text = placeholder placeholderLabel.numberOfLines = 0 placeholderLabel.backgroundColor = UIColor.clear placeholderLabel.translatesAutoresizingMaskIntoConstraints = false addSubview(placeholderLabel) // 添加 UILabel 的约束 updateConstraintsForPlaceholderLabel() } // AutoLayout 添加约束 private func updateConstraintsForPlaceholderLabel() { // 水平方向上 placeholderLabel 距离 textView 的左边距离为 textContainerInset.left + textContainer.lineFragmentPadding var newConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-(\(textContainerInset.left + textContainer.lineFragmentPadding))-[placeholder]", options: [], metrics: nil, views: ["placeholder": placeholderLabel]) // 竖直方向上 placeholderLabel 距离 textView 的上边距离为textContainerInset.top newConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-(\(textContainerInset.top))-[placeholder]", options: [], metrics: nil, views: ["placeholder": placeholderLabel]) // placeholderLabel 的宽和 self 的宽的比例约束 注意是有 constant的 newConstraints.append(NSLayoutConstraint( item: placeholderLabel, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1.0, constant: -(textContainerInset.left + textContainerInset.right + textContainer.lineFragmentPadding * 2.0) )) removeConstraints(placeholderLabelConstraints) addConstraints(newConstraints) placeholderLabelConstraints = newConstraints } // 根据 textView 的编辑情况,隐藏或者显示作为placeholder的UILabel @objc private func textDidChange() { placeholderLabel.isHidden = !text.isEmpty } public override func layoutSubviews() { super.layoutSubviews() placeholderLabel.preferredMaxLayoutWidth = textContainer.size.width - textContainer.lineFragmentPadding * 2.0 } // 移除通知 deinit { NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UITextViewTextDidChange, object: nil) } }复制代码