// // TextView.swift // MVMCoreUI // // Created by Kevin Christiano on 4/1/20. // Copyright © 2020 Verizon Wireless. All rights reserved. // import UIKit @objc open class TextView: UITextView, UITextViewDelegate { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- open var model: MoleculeModelProtocol? private var initialSetupPerformed = false /// Set this property if you want updateView to update the font based on this standard and the size passed in. public var standardFontSize: CGFloat = 0.0 public var isShowingPlaceholder = true /// Set to true to hide the blinking textField cursor. public var hideBlinkingCaret = false private var textTraits: (color: UIColor, font: UIFont) = (color: .mvmBlack, font: MFStyler.fontRegularBodySmall()) private var placeholderTraits: (text: String, color: UIColor, font: UIFont) = (text: "", color: .mvmCoolGray3, font: MFStyler.fontRegularMicro() ) //-------------------------------------------------- // MARK: - Delegate //-------------------------------------------------- /// Holds a reference to the delegating class so this class can internally influence the TextField behavior as well. public weak var didDeleteDelegate: TextFieldDidDeleteProtocol? /// Holds a reference to the delegating class so this class can internally influence the TextField behavior as well. private weak var proprietorTextDelegate: UITextViewDelegate? /// If you're using a ViewController, you must set this to it public weak var uiTextViewDelegate: UITextViewDelegate? { get { return delegate } set { delegate = self proprietorTextDelegate = newValue } } //-------------------------------------------------- // MARK: - Constraint //-------------------------------------------------- public var heightConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Initialization //-------------------------------------------------- public override init(frame: CGRect, textContainer: NSTextContainer?) { super.init(frame: .zero, textContainer: nil) initialSetup() } public convenience init() { self.init(frame: .zero, textContainer: nil) } public required init?(coder: NSCoder) { super.init(coder: coder) initialSetup() } convenience init(placeholderText: String, delegate: UITextViewDelegate?) { self.init(frame: .zero, textContainer: nil) self.delegate = delegate self.placeholderTraits.text = placeholderText } public func initialSetup() { if !initialSetupPerformed { tintColor = .mvmBlack initialSetupPerformed = true setupView() } } open override func caretRect(for position: UITextPosition) -> CGRect { if hideBlinkingCaret { return .zero } let caretRect = super.caretRect(for: position) return CGRect(origin: caretRect.origin, size: CGSize(width: 1, height: caretRect.height)) } open override func deleteBackward() { super.deleteBackward() didDeleteDelegate?.textFieldDidDelete() } open func setTextViewState() { if isShowingPlaceholder { text = "" font = textTraits.font textColor = textTraits.color isShowingPlaceholder = false } else if text.isEmpty { isShowingPlaceholder = true textColor = placeholderTraits.color text = placeholderTraits.text font = placeholderTraits.font } } //-------------------------------------------------- // MARK: - UITextViewDelegate //-------------------------------------------------- //#pragma Mark UITextView Delegate methods forwarding @objc public func textViewShouldBeginEditing(_ textView: UITextView) -> Bool { return proprietorTextDelegate?.textViewShouldBeginEditing?(textView) ?? true } @objc public func textViewDidBeginEditing(_ textView: UITextView) { proprietorTextDelegate?.textViewDidBeginEditing?(textView) } @objc public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { return proprietorTextDelegate?.textView?(textView, shouldChangeTextIn: range, replacementText: text) ?? true } @objc public func textViewDidChange(_ textView: UITextView) { setTextViewState() proprietorTextDelegate?.textViewDidChange?(textView) } @objc public func textViewShouldEndEditing(_ textView: UITextView) -> Bool { return proprietorTextDelegate?.textViewShouldEndEditing?(textView) ?? true } @objc public func textViewDidEndEditing(_ textView: UITextView) { proprietorTextDelegate?.textViewDidEndEditing?(textView) } } /// MARK:- MVMCoreViewProtocol extension TextView: MVMCoreViewProtocol { open func updateView(_ size: CGFloat) { } /// Will be called only once. open func setupView() { translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false showsVerticalScrollIndicator = false showsHorizontalScrollIndicator = false backgroundColor = .clear clipsToBounds = true smartQuotesType = .no smartDashesType = .no smartInsertDeleteType = .no font = textTraits.font layer.borderWidth = 1 layer.borderColor = UIColor.mvmBlack.cgColor isEditable = true } } /// MARK:- MoleculeViewProtocol extension TextView: MoleculeViewProtocol { open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { self.model = model if let color = model.backgroundColor?.uiColor { backgroundColor = color } guard let model = model as? TextViewModel else { return } if let height = model.height { heightConstraint = heightAnchor.constraint(equalToConstant: height) heightConstraint?.isActive = true } isEditable = model.isEditable textAlignment = model.textAlignment textColor = model.textColor.uiColor layer.borderColor = model.borderColor?.cgColor layer.borderWidth = model.borderWidth // placeholderFont = model.placeholderFont text = model.text isShowingPlaceholder = model.text.isEmpty placeholderTraits.text = model.placeholder uiTextViewDelegate = delegateObject?.uiTextViewDelegate if let accessibilityText = model.accessibilityText { accessibilityLabel = accessibilityText } if let fontStyle = model.fontStyle?.rawValue { MFStyler.styleTextView(self, withStyle: fontStyle, genericScaling: false) standardFontSize = font?.pointSize ?? 0 if let font = font { textTraits.font = font } } else { let fontSize = model.fontSize if let fontSize = fontSize { standardFontSize = fontSize } if let fontName = model.fontName { font = MFFonts.mfFont(withName: fontName, size: fontSize ?? standardFontSize) } else if let fontSize = fontSize { font = font?.updateSize(fontSize) } } if isEditable { MVMCoreUICommonViewsUtility.addDismissToolbar(to: self, delegate: delegateObject?.uiTextViewDelegate) } } open func reset() { setNeedsDisplay() backgroundColor = .clear text = "" } }