在日常开发中时常会遇到为 TextView 设置placeholder 的需求。 基本的思想是这个样子的: 1.在 UITextView 的左上角上放一个 UILabel,让 UILabel 的 text 来作为 UITextView的 placeholder; 2.为 UITextView添加一个通知NotificationCenter,监听 UITextView 中值得变化; 3.UITextView 的值发生变化就把UILabel 隐藏或者显示。
来贴代码:(代码中有很详细的注释,如果使用的话可以直接新建一个文件把代码粘贴过去就可以了) Demo 在此
import UIKit
// @IBDesignable 可以让自定义的这个 view 在 Xib 中使用
@IBDesignable
public 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)
}
}
复制代码