This commit is contained in:
Kevin G Christiano 2020-04-03 10:59:38 -04:00
parent daa37c5add
commit 174d26edc4
2 changed files with 365 additions and 0 deletions

View File

@ -0,0 +1,275 @@
//
// TextView.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 4/1/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import UIKit
@objc protocol MFTextViewDelegate {
// Dismisses the keyboard.
@objc optional func dismissFieldInput(_ sender: Any?)
}
@objc open class TextView: UITextView, UITextViewDelegate {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
open var model: MoleculeModelProtocol?
private var initialSetupPerformed = false
/// Set to true to hide the blinking textField cursor.
public var hideBlinkingCaret = false
public var hideBorder = true
public var borderPath: UIBezierPath?
public var errorShowing = false
weak var bottomLine: SeparatorView?
public var placeholderFont: UIFont = MFStyler.fontB3()! {
didSet {
if text.isEmpty {
font = placeholderFont
}
}
}
public var placeholder: String = "" {
didSet {
if text.isEmpty {
text = placeholder
}
}
}
public var placeholderTextColor: UIColor = .mvmCoolGray3
public var displaysPlaceholder = false
private(set) var textStore: String = ""
public override var text: String! {
didSet {
if displaysPlaceholder && text.isEmpty {
text = placeholder
textColor = placeholderTextColor
} else {
textColor = .mvmBlack
}
}
}
private(set) var textFont: UIFont = MFStyler.fontB2()!
public override var font: UIFont? {
didSet {
if displaysPlaceholder && text.isEmpty {
font = placeholderFont
} else {
textColor = .mvmBlack
}
}
}
//--------------------------------------------------
// 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?
/// If you're using a MFViewController, you must set this to it
public weak var uiTextViewDelegate: UITextViewDelegate? {
get { return delegate }
set { delegate = 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
displaysPlaceholder = true
self.placeholder = placeholderText
}
public func initialSetup() {
if !initialSetupPerformed {
tintColor = .black
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()
}
//--------------------------------------------------
// MARK: - Drawing
//--------------------------------------------------
open override func draw(_ rect: CGRect) {
super.draw(rect)
borderPath?.removeAllPoints()
if !hideBorder {
layer.cornerRadius = 0
borderPath = UIBezierPath()
let width = frame.origin.x + frame.size.width
let height = frame.origin.y + frame.size.height
borderPath?.move(to: CGPoint(x: frame.origin.x, y: height))
borderPath?.addLine(to: CGPoint(x: frame.origin.x, y: frame.origin.y))
borderPath?.addLine(to: CGPoint(x: width, y: frame.origin.y))
borderPath?.addLine(to: CGPoint(x: width, y: height))
borderPath?.lineWidth = 1
var strokeColor: UIColor?
if errorShowing {
strokeColor = .mvmOrangeAA
bottomLine?.backgroundColor = .mvmOrangeAA
} else {
strokeColor = .mvmCoolGray3
bottomLine?.backgroundColor = .mvmBlack
}
strokeColor?.setStroke()
borderPath?.stroke()
}
}
//--------------------------------------------------
// MARK: - Observing Methods
//--------------------------------------------------
/// Executes on UITextView.textDidEndEditingNotification
@objc open func dismissFieldInput() {
resignFirstResponder()
}
//#pragma Mark UITextView Delegate methods forwarding
@objc public func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
return delegate?.textViewShouldBeginEditing?(textView) ?? true
}
@objc public func textViewDidBeginEditing(_ textView: UITextView) {
delegate?.textViewDidBeginEditing?(textView)
}
@objc public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
return delegate?.textView?(textView, shouldChangeTextIn: range, replacementText: text) ?? true
}
@objc public func textViewDidChange(_ textView: UITextView) {
delegate?.textViewDidChange?(textView)
}
@objc public func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
return delegate?.textViewShouldEndEditing?(textView) ?? true
}
@objc public func textViewDidEndEditing(_ textView: UITextView) {
delegate?.textViewDidEndEditing?(textView)
}
}
/// MARK:- MVMCoreViewProtocol
extension TextView: MVMCoreViewProtocol {
open func updateView(_ size: CGFloat) { }
/// Will be called only once.
open func setupView() {
translatesAutoresizingMaskIntoConstraints = false
insetsLayoutMarginsFromSafeArea = false
clipsToBounds = true
errorShowing = false
hideBorder = false
layer.cornerRadius = CGFloat(CornerRadiusLarge)
// Disable SmartQuotes
smartQuotesType = .no
smartDashesType = .no
smartInsertDeleteType = .no
font = textFont
MVMCoreUICommonViewsUtility.addDismissToolbar(to: self, delegate: delegate)
textContainerInset = UIEdgeInsets(top: PaddingTwo, left: PaddingTwo, bottom: PaddingTwo, right: PaddingOne)
}
}
/// 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
}
text = model.text
placeholder = model.placeholder ?? ""
MVMCoreUICommonViewsUtility.addDismissToolbar(to: self, delegate: delegate)
}
open func reset() {
backgroundColor = .clear
text = ""
}
}

View File

@ -0,0 +1,90 @@
//
// TextViewModel.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 4/2/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
open class TextViewModel: MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "textView"
public var backgroundColor: Color?
public var text: String = ""
public var accessibilityText: String?
public var textColor: Color?
public var fontStyle: LabelModel.FontStyle?
public var textAlignment: NSTextAlignment?
public var height: CGFloat?
public var placeholder: String?
public var isEditable: Bool = true
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case moleculeName
case text
case accessibilityText
case textColor
case backgroundColor
case fontStyle
case fontName
case fontSize
case textAlignment
case attributes
case height
case placeholder
case isEditable
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(text: String) {
self.text = text
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
if let text = try typeContainer.decodeIfPresent(String.self, forKey: .text) {
self.text = text
}
placeholder = try typeContainer.decodeIfPresent(String.self, forKey: .text)
accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
textColor = try typeContainer.decodeIfPresent(Color.self, forKey: .textColor)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
fontStyle = try typeContainer.decodeIfPresent(LabelModel.FontStyle.self, forKey: .fontStyle)
textAlignment = try typeContainer.decodeIfPresent(NSTextAlignment.self, forKey: .textAlignment)
height = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .height)
if let isEditable = try typeContainer.decodeIfPresent(Bool.self, forKey: .isEditable) {
self.isEditable = isEditable
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(moleculeName, forKey: .moleculeName)
try container.encode(text, forKey: .text)
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
try container.encodeIfPresent(textColor, forKey: .textColor)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(fontStyle, forKey: .fontStyle)
try container.encodeIfPresent(textAlignment, forKey: .textAlignment)
try container.encodeIfPresent(height, forKey: .height)
try container.encodeIfPresent(isEditable, forKey: .isEditable)
}
}