functioning border

This commit is contained in:
Kevin G Christiano 2020-04-28 16:44:05 -04:00
parent 077d94edd1
commit 2a251649af
3 changed files with 303 additions and 55 deletions

View File

@ -51,7 +51,7 @@ public protocol TextInputDidDeleteProtocol: class {
public func initialSetup() { public func initialSetup() {
if !initialSetupPerformed { if !initialSetupPerformed {
tintColor = .black tintColor = .mvmBlack
initialSetupPerformed = true initialSetupPerformed = true
setupView() setupView()
} }
@ -87,7 +87,9 @@ extension TextField: MVMCoreViewProtocol {
/// MARK:- MoleculeViewProtocol /// MARK:- MoleculeViewProtocol
extension TextField: MoleculeViewProtocol { extension TextField: MoleculeViewProtocol {
open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
if let color = model.backgroundColor?.uiColor { if let color = model.backgroundColor?.uiColor {
backgroundColor = color backgroundColor = color
} }

View File

@ -9,7 +9,7 @@
import UIKit import UIKit
@objc open class TextView: UITextView, UITextViewDelegate { @objc open class TextView: UITextView, UITextViewDelegate, MVMCoreViewProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -28,6 +28,98 @@ import UIKit
return model as? TextViewModel return model as? TextViewModel
} }
//--------------------------------------------------
// MARK: - Drawing Properties
//--------------------------------------------------
/// Total control over the drawn top, bottom, left and right borders.
public var disableAllBorders = false
private(set) var fieldState: FieldState = .original {
didSet (oldState) {
// Will not update if new state is the same as old.
if fieldState != oldState {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.fieldState.setStateUI(for: self)
}
}
}
}
/// Determines if the top, left, and right borders should be drawn.
private var hideBorders = false
public var borderStrokeColor: UIColor = .mvmCoolGray3
public var bottomStrokeColor: UIColor = .mvmBlack
private var borderPath: UIBezierPath = UIBezierPath()
private var bottomPath: UIBezierPath = UIBezierPath()
//--------------------------------------------------
// MARK: - Property Observers
//--------------------------------------------------
private var _isEnabled: Bool = true
private var _showError: Bool = false
private var _isLocked: Bool = false
private var _isSelected: Bool = false
public var isEnabled: Bool {
get { return _isEnabled }
set (enabled) {
_isEnabled = enabled
_isLocked = false
_isSelected = false
_showError = false
fieldState = enabled ? .original : .disabled
}
}
public var showError: Bool {
get { return _showError }
set (error) {
_showError = error
_isEnabled = true
_isLocked = false
_isSelected = false
fieldState = error ? .error : .original
}
}
public var isLocked: Bool {
get { return _isLocked }
set (locked) {
_isLocked = locked
_isEnabled = true
_isSelected = false
_showError = false
fieldState = locked ? .locked : .original
}
}
public var isSelected: Bool {
get { return _isSelected }
set (selected) {
_isSelected = selected
_isLocked = false
_isEnabled = true
if _showError {
fieldState = selected ? .selectedError : .error
} else {
fieldState = selected ? .selected : .original
}
}
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Delegate // MARK: - Delegate
//-------------------------------------------------- //--------------------------------------------------
@ -47,6 +139,8 @@ import UIKit
} }
} }
var delegateObject: MVMCoreUIDelegateObject?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Constraint // MARK: - Constraint
//-------------------------------------------------- //--------------------------------------------------
@ -77,7 +171,7 @@ import UIKit
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Setup // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
public func initialSetup() { public func initialSetup() {
@ -89,6 +183,187 @@ import UIKit
} }
} }
open func updateView(_ size: CGFloat) {
refreshUI()
}
/// 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()
isEditable = true
isOpaque = false
}
open func reset() {
backgroundColor = .mvmWhite
text = ""
inputAccessoryView?.removeFromSuperview()
contentInset = UIEdgeInsets(top: Padding.Three, left: Padding.Three, bottom: Padding.Three, right: Padding.Three)
}
open override func layoutSubviews() {
super.layoutSubviews()
refreshUI(bottomBarSize: showError ? 4 : 1)
}
//--------------------------------------------------
// MARK: - Draw
//--------------------------------------------------
/// This handles the top, left, and right border lines.
open override func draw(_ rect: CGRect) {
super.draw(rect)
borderPath.removeAllPoints()
bottomPath.removeAllPoints()
if !disableAllBorders && !hideBorders {
// Brings the other half of the line inside the view to prevent cropping.
let origin = bounds.origin
let size = frame.size
let insetLean: CGFloat = 0.5
borderPath.lineWidth = 1
borderPath.move(to: CGPoint(x: origin.x + insetLean, y: origin.y + size.height))
borderPath.addLine(to: CGPoint(x: origin.x + insetLean, y: origin.y + insetLean))
borderPath.addLine(to: CGPoint(x: origin.x + size.width - insetLean, y: origin.y + insetLean))
borderPath.addLine(to: CGPoint(x: origin.x + size.width - insetLean, y: origin.y + size.height))
borderStrokeColor.setStroke()
borderPath.stroke()
bottomPath.lineWidth = 4
bottomPath.move(to: CGPoint(x: origin.x + size.width, y: origin.y + size.height - 2))
bottomPath.addLine(to: CGPoint(x: origin.x, y: origin.y + size.height - 2))
bottomStrokeColor.setStroke()
bottomPath.stroke()
}
}
//--------------------------------------------------
// MARK: - Draw States
//--------------------------------------------------
public enum FieldState {
case original
case error
case selectedError
case selected
case locked
case disabled
public func setStateUI(for formField: TextView) {
switch self {
case .original:
formField.originalUI()
case .error:
formField.errorUI()
case .selectedError:
formField.selectedErrorUI()
case .selected:
formField.selectedUI()
case .locked:
formField.lockedUI()
case .disabled:
formField.disabledUI()
}
}
}
open func originalUI() {
isEditable = true
hideBorders = false
borderStrokeColor = .mvmCoolGray3
bottomStrokeColor = .mvmBlack
refreshUI(bottomBarSize: 1)
}
open func errorUI() {
isEditable = true
hideBorders = false
borderStrokeColor = .mvmOrange
bottomStrokeColor = .mvmOrange
refreshUI(bottomBarSize: 4)
}
open func selectedErrorUI() {
isEditable = true
hideBorders = false
borderStrokeColor = .mvmBlack
bottomStrokeColor = .mvmOrange
refreshUI(bottomBarSize: 4)
}
open func selectedUI() {
isEditable = true
hideBorders = false
borderStrokeColor = .mvmBlack
bottomStrokeColor = .mvmBlack
refreshUI(bottomBarSize: 1)
}
open func lockedUI() {
isEditable = false
hideBorders = true
borderStrokeColor = .clear
bottomStrokeColor = .clear
refreshUI(bottomBarSize: 1)
}
open func disabledUI() {
isEditable = false
hideBorders = false
borderStrokeColor = .mvmCoolGray3
bottomStrokeColor = .mvmCoolGray3
refreshUI(bottomBarSize: 1)
}
open func refreshUI(bottomBarSize: CGFloat? = nil, updateMoleculeLayout: Bool = false) {
if !disableAllBorders {
// let size: CGFloat = bottomBarSize ?? (showError ? 4 : 1)
// var heightChanged = false
// if let bottomHeight = bottomBar?.bounds.height {
// heightChanged = size != bottomHeight
// }
if updateMoleculeLayout {//|| heightChanged {
delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self)
}
setNeedsDisplay()
layoutIfNeeded()
}
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Methods // MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
@ -177,36 +452,12 @@ import UIKit
} }
} }
/// MARK:- MVMCoreViewProtocol // MARK:- MoleculeViewProtocol
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 { extension TextView: MoleculeViewProtocol {
open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
self.model = model self.model = model
self.delegateObject = delegateObject
if let color = model.backgroundColor?.uiColor { if let color = model.backgroundColor?.uiColor {
backgroundColor = color backgroundColor = color
@ -223,8 +474,6 @@ extension TextView: MoleculeViewProtocol {
isEditable = model.isEditable isEditable = model.isEditable
textAlignment = model.textAlignment textAlignment = model.textAlignment
textColor = model.textColor.uiColor textColor = model.textColor.uiColor
layer.borderColor = model.borderColor?.cgColor
layer.borderWidth = model.borderWidth
text = model.text text = model.text
uiTextViewDelegate = delegateObject?.uiTextViewDelegate uiTextViewDelegate = delegateObject?.uiTextViewDelegate
isShowingPlaceholder = model.text!.isEmpty isShowingPlaceholder = model.text!.isEmpty
@ -237,17 +486,14 @@ extension TextView: MoleculeViewProtocol {
setPlaceholderIfAvailable() setPlaceholderIfAvailable()
if isEditable { if isEditable {
FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
MVMCoreUICommonViewsUtility.addDismissToolbar(to: self, delegate: delegateObject?.uiTextViewDelegate) MVMCoreUICommonViewsUtility.addDismissToolbar(to: self, delegate: delegateObject?.uiTextViewDelegate)
if model.selected ?? false {
isSelected = true
model.wasInitiallySelected = true
becomeFirstResponder()
}
} }
} }
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
}
} }

View File

@ -27,8 +27,8 @@ open class TextViewModel: TextEntryFieldModel {
public var placeholderFontStyle: Styler.Font = Styler.Font.RegularMicro public var placeholderFontStyle: Styler.Font = Styler.Font.RegularMicro
public var showsPlaceholder: Bool = true public var showsPlaceholder: Bool = true
public var isEditable: Bool = true public var isEditable: Bool = true
public var borderColor: Color? // public var borderColor: Color?
public var borderWidth: CGFloat = 0 // public var borderWidth: CGFloat = 0
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
@ -44,12 +44,12 @@ open class TextViewModel: TextEntryFieldModel {
case textAlignment case textAlignment
case attributes case attributes
case height case height
case borderColor // case borderColor
case borderWidth // case borderWidth
case placeholder // case placeholder
case placeholderFontStyle case placeholderFontStyle
case isEditable = "editable" case isEditable = "editable"
case hideBlinkingCaret // case hideBlinkingCaret
} }
//-------------------------------------------------- //--------------------------------------------------
@ -76,15 +76,15 @@ open class TextViewModel: TextEntryFieldModel {
self.isEditable = isEditable self.isEditable = isEditable
} }
if let borderWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .borderWidth) { // if let borderWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .borderWidth) {
self.borderWidth = borderWidth // self.borderWidth = borderWidth
} // }
if let fontStyle = try typeContainer.decodeIfPresent(Styler.Font.self, forKey: .fontStyle) { if let fontStyle = try typeContainer.decodeIfPresent(Styler.Font.self, forKey: .fontStyle) {
self.fontStyle = fontStyle self.fontStyle = fontStyle
} }
borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) // borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor)
accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
height = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .height) height = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .height)
} }
@ -95,13 +95,13 @@ open class TextViewModel: TextEntryFieldModel {
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText) try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
try container.encodeIfPresent(textColor, forKey: .textColor) try container.encodeIfPresent(textColor, forKey: .textColor)
try container.encodeIfPresent(fontStyle, forKey: .fontStyle) try container.encodeIfPresent(fontStyle, forKey: .fontStyle)
try container.encodeIfPresent(borderColor, forKey: .borderColor) // try container.encodeIfPresent(borderColor, forKey: .borderColor)
try container.encodeIfPresent(height, forKey: .height) try container.encodeIfPresent(height, forKey: .height)
try container.encode(text, forKey: .text) try container.encode(text, forKey: .text)
try container.encode(placeholder, forKey: .placeholder) // try container.encode(placeholder, forKey: .placeholder)
try container.encode(placeholderFontStyle, forKey: .placeholderFontStyle) try container.encode(placeholderFontStyle, forKey: .placeholderFontStyle)
try container.encode(textAlignment, forKey: .textAlignment) try container.encode(textAlignment, forKey: .textAlignment)
try container.encode(isEditable, forKey: .isEditable) try container.encode(isEditable, forKey: .isEditable)
try container.encode(borderWidth, forKey: .borderWidth) // try container.encode(borderWidth, forKey: .borderWidth)
} }
} }