如何为 UITextView 增加 Placeholder ?

UITextView 居然不能设定 placeholder,一点都不 cool !那就来自己做一个呗,最好还能直接在 Storyboard 修改默认的属性,这样才真的 cool !

伸手党请拉到最后直接看完整代码!

Property

首先新建一个名为 UIPlaceholderTextView 的类,继承于 UITextView ,记得把它标记为 @IBDesignable 。它需要三个基本属性 placeholder, placeholderColor, placeHolderLabel 。其中 placeholder, placeholderColor 使用 @IBInspectable 修饰,可以用 Storyboard 直接设定。注意默认值的使用,可以让你减少很多 Optinal 及初始化的处理。 placeholderColor 使用了一个闭包来赋予默认值,注意它的 lazy 修饰。

Life Cycle

生命周期中需要一直监听 UITextViewTextDidChangeNotification 通知来获取输入状态的变化。

使用 didSet 来订阅 text 的变化并及时做出响应。

Full Code

完整的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import UIKit

@IBDesignable
public class UIPlaceholderTextView: UITextView {

// MARK: - Public Property

@IBInspectable public var placeholder: String = "placeholder"
@IBInspectable public var placeholderColor: UIColor = .lightGrayColor()

// MARK: - Private Property

private let kPlaceholderViewTag: Int = 999
private let kChangeAnimationDuration: NSTimeInterval = 0.25

private lazy var placeHolderLabel: UILabel = {
let frame = CGRect(x: 8, y: 8, width: self.bounds.size.width - 16, height: 0)
let label = UILabel(frame: frame)
label.lineBreakMode = NSLineBreakMode.ByWordWrapping
label.numberOfLines = 0
label.font = self.font
label.backgroundColor = .clearColor()
label.textColor = self.placeholderColor
label.alpha = 0
label.tag = self.kPlaceholderViewTag
self.addSubview(label)
return label
}()

// MARK: - Life Cycle

public override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(textChanged(_:)), name: UITextViewTextDidChangeNotification, object: nil)
}

public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(textChanged(_:)), name: UITextViewTextDidChangeNotification, object: nil)
}

public override func awakeFromNib() {
super.awakeFromNib()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(textChanged), name: UITextViewTextDidChangeNotification, object: nil)
}

deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}

override var text: String! {
didSet { textChanged(nil) }
}

override func drawRect(rect: CGRect) {
placeHolderLabel.text = placeholder
placeHolderLabel.sizeToFit()
sendSubviewToBack(placeHolderLabel)

if text.characters.isEmpty && !placeholder.characters.isEmpty {
viewWithTag(kPlaceholderViewTag)?.alpha = 1
}
super.drawRect(rect)
}

// MARK: - Private

@objc private func textChanged(notification: NSNotification?) {
if placeholder.characters.isEmpty { return }
UIView.animateWithDuration(kChangeAnimationDuration) {
if self.text.characters.isEmpty {
self.viewWithTag(self.kPlaceholderViewTag)?.alpha = 1
} else {
self.viewWithTag(self.kPlaceholderViewTag)?.alpha = 0
}
}
}
}