Last active
May 24, 2022 09:48
-
-
Save KrisRJack/81df8cf224722ce222f1914751397fd6 to your computer and use it in GitHub Desktop.
TextView with Placeholder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Custom text view containing a placeholder | |
when text is `nil` (currently not supported by | |
UITextView). Placeholder can be modified both | |
programmatically and in the interface builder. | |
Created by Kristopher Jackson on 5/23/22. | |
*/ | |
import UIKit | |
@IBDesignable | |
open class TextView: UITextView { | |
@IBInspectable | |
public var placeholder: String { | |
get { placeholderTextView.text } | |
set { placeholderTextView.text = newValue } | |
} | |
open override var text: String? { | |
willSet { showPlaceholder = (newValue == nil) } | |
} | |
open override var font: UIFont? { | |
willSet { placeholderTextView.font = newValue } | |
} | |
open override var textAlignment: NSTextAlignment { | |
willSet { placeholderTextView.textAlignment = newValue } | |
} | |
open override var isScrollEnabled: Bool { | |
willSet { placeholderTextView.isScrollEnabled = newValue } | |
} | |
open override var showsVerticalScrollIndicator: Bool { | |
willSet { placeholderTextView.showsVerticalScrollIndicator = newValue } | |
} | |
open override var showsHorizontalScrollIndicator: Bool { | |
willSet { placeholderTextView.showsHorizontalScrollIndicator = newValue } | |
} | |
private var showPlaceholder: Bool = true { | |
willSet { placeholderTextView.isHidden = !newValue } | |
} | |
private lazy var placeholderTextView: UITextView = { | |
let textView = UITextView() | |
textView.font = font | |
textView.isHidden = false | |
textView.isEditable = false | |
textView.isSelectable = false | |
textView.backgroundColor = .clear | |
textView.text = "Add text here..." | |
textView.textColor = .placeholderText | |
textView.isUserInteractionEnabled = false | |
textView.translatesAutoresizingMaskIntoConstraints = false | |
return textView | |
}() | |
override public init(frame: CGRect, textContainer: NSTextContainer?) { | |
super.init(frame: frame, textContainer: textContainer) | |
setUp() | |
} | |
init(frame: CGRect) { | |
super.init(frame: frame, textContainer: nil) | |
setUp() | |
} | |
convenience public init() { | |
self.init(frame: .zero, textContainer: nil) | |
} | |
required public init?(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder) | |
setUp() | |
} | |
private func setUp() { | |
configureConstraints() | |
hidePlaceholderIfNeeded() | |
} | |
private func configureConstraints() { | |
addSubview(placeholderTextView) | |
bringSubviewToFront(placeholderTextView) | |
NSLayoutConstraint.activate([ | |
placeholderTextView.widthAnchor.constraint(equalTo: widthAnchor), | |
placeholderTextView.heightAnchor.constraint(equalTo: heightAnchor), | |
placeholderTextView.centerXAnchor.constraint(equalTo: centerXAnchor), | |
placeholderTextView.centerYAnchor.constraint(equalTo: centerYAnchor), | |
]) | |
} | |
func hidePlaceholderIfNeeded() { | |
NotificationCenter.default.addObserver( | |
forName: UITextView.textDidChangeNotification, | |
object: self, | |
queue: nil | |
) { [weak self] notification in | |
guard let self = self else { return } | |
guard let text = self.text else { | |
self.showPlaceholder = true | |
return | |
} | |
self.showPlaceholder = text.isEmpty | |
} | |
} | |
deinit { | |
NotificationCenter.default.removeObserver(self) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment