@@ -12,6 +12,39 @@ public final class M3CTextView: UIView, M3CTextInput {
1212 }
1313 }
1414
15+ /// The text that is displayed when the text view is empty.
16+ @objc public var placeholder : String ? {
17+ didSet {
18+ placeholderLabel. text = placeholder
19+ updatePlaceholderVisibility ( )
20+ }
21+ }
22+
23+ /// The color of the placeholder text.
24+ @objc public var placeholderColor : UIColor ? {
25+ didSet {
26+ placeholderLabel. textColor = placeholderColor ?? . gray
27+ }
28+ }
29+
30+ /// The font of the placeholder text.
31+ @objc public var placeholderFont : UIFont ? {
32+ didSet {
33+ placeholderLabel. font = placeholderFont ?? textContainer. font
34+ }
35+ }
36+
37+ private lazy var placeholderLabel : UILabel = {
38+ let label = UILabel ( )
39+ label. translatesAutoresizingMaskIntoConstraints = false
40+ label. font = textContainer. font
41+ label. numberOfLines = 0
42+ label. lineBreakMode = . byWordWrapping
43+ label. isAccessibilityElement = false
44+ label. textColor = . gray
45+ return label
46+ } ( )
47+
1548 private var controlState : UIControl . State {
1649 if isInErrorState {
1750 return . error
@@ -79,6 +112,9 @@ public final class M3CTextView: UIView, M3CTextInput {
79112 super. init ( frame: . zero)
80113
81114 configureStackViews ( )
115+ addSubview ( placeholderLabel)
116+ configurePlaceholderConstraints ( )
117+ updatePlaceholderVisibility ( )
82118 textContainer. setContentHuggingPriority ( . defaultLow, for: . vertical)
83119 textContainer. setContentCompressionResistancePriority ( . required, for: . vertical)
84120 }
@@ -154,6 +190,36 @@ public final class M3CTextView: UIView, M3CTextInput {
154190 super. layoutSubviews ( )
155191 hideEmptyLabels ( )
156192 }
193+
194+ private func configurePlaceholderConstraints( ) {
195+ NSLayoutConstraint . activate ( [
196+ placeholderLabel. topAnchor. constraint ( equalTo: textContainer. topAnchor, constant: 10 ) ,
197+ placeholderLabel. leadingAnchor. constraint ( equalTo: textContainer. leadingAnchor, constant: 12 ) ,
198+ placeholderLabel. trailingAnchor. constraint (
199+ equalTo: textContainer. trailingAnchor, constant: - 12 ) ,
200+ placeholderLabel. bottomAnchor. constraint (
201+ lessThanOrEqualTo: textContainer. bottomAnchor,
202+ constant: - textContainer. textContainerInset. bottom) ,
203+ ] )
204+ }
205+
206+ private func updatePlaceholderVisibility( ) {
207+ if let placeholderText = placeholder {
208+ placeholderLabel. isHidden = !textContainer. text. isEmpty
209+ textContainer. accessibilityHint = textContainer. text. isEmpty ? placeholderText : nil
210+ } else {
211+ placeholderLabel. isHidden = true
212+ textContainer. accessibilityHint = nil
213+ }
214+ }
215+
216+ // MARK: - UITextViewDelegate
217+
218+ public func textViewDidChange( _ textView: UITextView ) {
219+ DispatchQueue . main. async { [ weak self] in
220+ self ? . updatePlaceholderVisibility ( )
221+ }
222+ }
157223}
158224
159225// MARK: M3CTextView Color Configuration
0 commit comments