-
Notifications
You must be signed in to change notification settings - Fork 91
Expand file tree
/
Copy pathMessageTextView.swift
More file actions
141 lines (111 loc) · 3.72 KB
/
MessageTextView.swift
File metadata and controls
141 lines (111 loc) · 3.72 KB
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//
// MessageTextView.swift
// MessageViewController
//
// Created by Ryan Nystrom on 12/31/17.
//
import UIKit
public protocol MessageTextViewListener: class {
func didChange(textView: MessageTextView)
func didChangeSelection(textView: MessageTextView)
func willChangeRange(textView: MessageTextView, to range: NSRange)
}
open class MessageTextView: UITextView, UITextViewDelegate {
private let placeholderLabel = UILabel()
private var listeners: NSHashTable<AnyObject> = NSHashTable.weakObjects()
open override var delegate: UITextViewDelegate? {
get { return self }
set {}
}
open override var font: UIFont? {
didSet {
placeholderLabel.font = font
placeholderLayoutDidChange()
}
}
open override var textAlignment: NSTextAlignment {
didSet {
placeholderLabel.textAlignment = textAlignment
placeholderLayoutDidChange()
}
}
open override var text: String! {
didSet {
updatePlaceholderVisibility()
}
}
open override var attributedText: NSAttributedString! {
didSet {
updatePlaceholderVisibility()
}
}
public override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
commonInit()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
// MARK: Public API
public func add(listener: MessageTextViewListener) {
assert(Thread.isMainThread)
listeners.add(listener)
}
public var placeholderText: String {
get { return placeholderLabel.text ?? "" }
set {
placeholderLabel.text = newValue
placeholderLayoutDidChange()
}
}
public var placeholderTextColor: UIColor {
get { return placeholderLabel.textColor }
set { placeholderLabel.textColor = newValue }
}
// MARK: Overrides
open override func layoutSubviews() {
super.layoutSubviews()
let placeholderSize = placeholderLabel.bounds.size
placeholderLabel.frame = CGRect(
x: textContainerInset.left,
y: textContainerInset.top,
width: placeholderSize.width,
height: placeholderSize.height
)
}
// MARK: Private API
private func commonInit() {
placeholderLabel.backgroundColor = .clear
placeholderLabel.font = font
placeholderLabel.textColor = textColor
placeholderLabel.textAlignment = textAlignment
addSubview(placeholderLabel)
updatePlaceholderVisibility()
}
private func enumerateListeners(block: (MessageTextViewListener) -> Void) {
for listener in listeners.objectEnumerator() {
guard let listener = listener as? MessageTextViewListener else { continue }
block(listener)
}
}
private func placeholderLayoutDidChange() {
placeholderLabel.sizeToFit()
setNeedsLayout()
}
private func updatePlaceholderVisibility() {
placeholderLabel.isHidden = !text.isEmpty
}
// MARK: UITextViewDelegate
public func textViewDidChange(_ textView: UITextView) {
updatePlaceholderVisibility()
enumerateListeners { $0.didChange(textView: self) }
}
public func textViewDidChangeSelection(_ textView: UITextView) {
enumerateListeners { $0.didChangeSelection(textView: self) }
}
public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
enumerateListeners { $0.willChangeRange(textView: self, to: range) }
return true
}
}