From bb6de4e7bc41fd90e70b5cb8cabbb5720fd6d1f3 Mon Sep 17 00:00:00 2001 From: Nathan Tannar Date: Fri, 20 Oct 2017 22:10:23 -0700 Subject: [PATCH] InputTextView Comments --- Sources/Views/InputTextView.swift | 142 ++++++++++++++++++++---------- 1 file changed, 97 insertions(+), 45 deletions(-) diff --git a/Sources/Views/InputTextView.swift b/Sources/Views/InputTextView.swift index b57a5fc3..e9f664ff 100644 --- a/Sources/Views/InputTextView.swift +++ b/Sources/Views/InputTextView.swift @@ -24,10 +24,32 @@ import UIKit +/** + A UITextView that has a UILabel embedded for placeholder text + + ## Important Notes ## + 1. Changing the font, textAlignment or textContainerInset automatically performs the same modifications to the placeholderLabel + 2. Intended to be used in an `MessageInputBar` + 3. Default placeholder text is "New Message" + 4. Will pass a pasted image it's `MessageInputBar`'s `InputManager`s + */ open class InputTextView: UITextView { - + // MARK: - Properties - + + open override var text: String! { + didSet { + textViewTextDidChange() + } + } + + open override var attributedText: NSAttributedString! { + didSet { + textViewTextDidChange() + } + } + + /// A UILabel that holds the InputTextView's placeholder text open let placeholderLabel: UILabel = { let label = UILabel() label.numberOfLines = 0 @@ -38,47 +60,51 @@ open class InputTextView: UITextView { return label }() - open override var text: String! { - didSet { - placeholderLabel.isHidden = !text.isEmpty - } - } - - private var placeholderLabelConstraintSet: NSLayoutConstraintSet? - + /// The placeholder text that appears when there is no text open var placeholder: String? = "New Message" { didSet { placeholderLabel.text = placeholder } } - + + /// The placeholderLabel's textColor open var placeholderTextColor: UIColor? = .lightGray { didSet { placeholderLabel.textColor = placeholderTextColor } } - - open override var font: UIFont! { - didSet { - placeholderLabel.font = font - } - } - - open override var textAlignment: NSTextAlignment { - didSet { - placeholderLabel.textAlignment = textAlignment - } - } - + + /// The UIEdgeInsets the placeholderLabel has within the InputTextView open var placeholderLabelInsets: UIEdgeInsets = UIEdgeInsets(top: 4, left: 7, bottom: 4, right: 7) { didSet { updateConstraintsForPlaceholderLabel() } } + /// The font of the InputTextView. When set the placeholderLabel's font is also updated + open override var font: UIFont! { + didSet { + placeholderLabel.font = font + } + } + + /// The textAlignment of the InputTextView. When set the placeholderLabel's textAlignment is also updated + open override var textAlignment: NSTextAlignment { + didSet { + placeholderLabel.textAlignment = textAlignment + } + } + + /// The textContainerInset of the InputTextView. When set the placeholderLabelInsets is also updated + open override var textContainerInset: UIEdgeInsets { + didSet { + placeholderLabelInsets = textContainerInset + } + } + open override var scrollIndicatorInsets: UIEdgeInsets { didSet { - // When .zero a rendering issue can occur when the MessagesViewController is pushed onto a UINavigationController stack, popped and then pushed again + // When .zero a rendering issue can occur if scrollIndicatorInsets == .zero { scrollIndicatorInsets = UIEdgeInsets(top: .leastNonzeroMagnitude, left: .leastNonzeroMagnitude, @@ -87,27 +113,38 @@ open class InputTextView: UITextView { } } } - - public weak var messageInputBar: MessageInputBar? - + + /// A weak reference to the MessageInputBar that the InputTextView is contained within + open weak var messageInputBar: MessageInputBar? + + /// The constraints of the placeholderLabel + private var placeholderLabelConstraintSet: NSLayoutConstraintSet? + // MARK: - Initializers - + public convenience init() { self.init(frame: .zero) } - + public override init(frame: CGRect, textContainer: NSTextContainer?) { super.init(frame: frame, textContainer: textContainer) setup() } - + required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setup() } - + + deinit { + NotificationCenter.default.removeObserver(self) + } + + // MARK: - Setup + + /// Sets up the default properties open func setup() { - + font = UIFont.preferredFont(forTextStyle: .body) textContainerInset = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4) scrollIndicatorInsets = UIEdgeInsets(top: .leastNonzeroMagnitude, @@ -118,18 +155,14 @@ open class InputTextView: UITextView { layer.cornerRadius = 5.0 layer.borderWidth = 1.25 layer.borderColor = UIColor.lightGray.cgColor - - addSubviews() - addConstraints() + addObservers() + addPlaceholderLabel() } - - private func addSubviews() { - + + /// Adds the placeholderLabel to the view and sets up its initial constraints + private func addPlaceholderLabel() { + addSubview(placeholderLabel) - } - - private func addConstraints() { - placeholderLabelConstraintSet = NSLayoutConstraintSet( top: placeholderLabel.topAnchor.constraint(equalTo: topAnchor, constant: placeholderLabelInsets.top), bottom: placeholderLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -placeholderLabelInsets.bottom), @@ -137,12 +170,31 @@ open class InputTextView: UITextView { width: placeholderLabel.widthAnchor.constraint(equalTo: widthAnchor, constant: -(placeholderLabelInsets.left + placeholderLabelInsets.right)) ).activate() } - + + /// Updates the placeholderLabels constraint constants to match the placeholderLabelInsets private func updateConstraintsForPlaceholderLabel() { - + placeholderLabelConstraintSet?.top?.constant = placeholderLabelInsets.top placeholderLabelConstraintSet?.bottom?.constant = -placeholderLabelInsets.bottom placeholderLabelConstraintSet?.left?.constant = placeholderLabelInsets.left placeholderLabelConstraintSet?.width?.constant = -(placeholderLabelInsets.left + placeholderLabelInsets.right) } + + /// Adds a notification for .UITextViewTextDidChange to detect when the placeholderLabel + /// should be hidden or shown + private func addObservers() { + + NotificationCenter.default.addObserver(self, + selector: #selector(InputTextView.textViewTextDidChange), + name: Notification.Name.UITextViewTextDidChange, + object: nil) + } + + // MARK: - Notifications + + /// Updates the placeholderLabel's isHidden property based on the text being empty or not + @objc + open func textViewTextDidChange() { + placeholderLabel.isHidden = !text.isEmpty + } }