mvm_core_ui/MVMCoreUI/BaseClasses/TextView.swift
2020-04-28 13:49:57 -04:00

254 lines
8.1 KiB
Swift

//
// 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
/// If true then text textView is currently displaying the stored placeholder text as there is not content to display.
public var isShowingPlaceholder = true
/// Set to true to hide the blinking textField cursor.
public var hideBlinkingCaret = false
public var textViewModel: TextViewModel? {
return model as? TextViewModel
}
//--------------------------------------------------
// MARK: - Delegate
//--------------------------------------------------
/// Holds a reference to the delegating class so this class can internally influence the TextField behavior as well.
public weak var didDeleteDelegate: TextInputDidDeleteProtocol?
/// 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(delegate: UITextViewDelegate) {
self.init(frame: .zero, textContainer: nil)
self.delegate = delegate
}
//--------------------------------------------------
// MARK: - Setup
//--------------------------------------------------
public func initialSetup() {
if !initialSetupPerformed {
tintColor = .mvmBlack
initialSetupPerformed = true
setupView()
}
}
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
/// Alters the blinking caret line as per design standards.
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()
}
public func setTextAppearance() {
if isShowingPlaceholder {
setTextContentTraits()
}
}
public func setPlaceholderIfAvailable() {
if let placeholder = textViewModel?.placeholder, !placeholder.isEmpty && text.isEmpty {
setPlaceholderContentTraits()
}
}
public func setTextContentTraits() {
isShowingPlaceholder = false
text = ""
font = textViewModel?.fontStyle.getFont()
textColor = textViewModel?.textColor.uiColor
}
public func setPlaceholderContentTraits() {
isShowingPlaceholder = true
textColor = textViewModel?.placeholderTextColor.uiColor
font = textViewModel?.placeholderFontStyle.getFont()
text = textViewModel?.placeholder
}
//--------------------------------------------------
// MARK: - UITextViewDelegate
//--------------------------------------------------
@objc public func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
return proprietorTextDelegate?.textViewShouldBeginEditing?(textView) ?? true
}
@objc public func textViewDidBeginEditing(_ textView: UITextView) {
setTextAppearance()
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) {
textViewModel?.text = textView.text
proprietorTextDelegate?.textViewDidChange?(textView)
}
@objc public func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
return proprietorTextDelegate?.textViewShouldEndEditing?(textView) ?? true
}
@objc public func textViewDidEndEditing(_ textView: UITextView) {
setPlaceholderIfAvailable()
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
contentInset = UIEdgeInsets(top: Padding.Three, left: Padding.Three, bottom: Padding.Three, right: Padding.Three)
backgroundColor = .mvmWhite
clipsToBounds = true
smartQuotesType = .no
smartDashesType = .no
smartInsertDeleteType = .no
font = textViewModel?.fontStyle.getFont()
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 }
heightConstraint?.isActive = false
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
text = model.text
uiTextViewDelegate = delegateObject?.uiTextViewDelegate
isShowingPlaceholder = model.text!.isEmpty
if let accessibilityText = model.accessibilityText {
accessibilityLabel = accessibilityText
}
font = model.fontStyle.getFont()
setPlaceholderIfAvailable()
if isEditable {
MVMCoreUICommonViewsUtility.addDismissToolbar(to: self, delegate: delegateObject?.uiTextViewDelegate)
}
}
open func reset() {
backgroundColor = .mvmWhite
text = ""
inputAccessoryView?.removeFromSuperview()
layer.borderColor = UIColor.mvmBlack.cgColor
contentInset = UIEdgeInsets(top: Padding.Three, left: Padding.Three, bottom: Padding.Three, right: Padding.Three)
layer.borderWidth = 0
}
}