244 lines
8.0 KiB
Swift
244 lines
8.0 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
|
|
|
|
/// 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 = ""
|
|
}
|
|
}
|