Merge branch 'develop' into feature/list_progressbar_thin

This commit is contained in:
Lekshmi S 2020-05-14 12:57:08 +05:30
commit e1a5ac8faf
18 changed files with 420 additions and 410 deletions

View File

@ -68,6 +68,8 @@
0A21DB83235DFBC500C160A2 /* MdnEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A21DB82235DFBC500C160A2 /* MdnEntryField.swift */; }; 0A21DB83235DFBC500C160A2 /* MdnEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A21DB82235DFBC500C160A2 /* MdnEntryField.swift */; };
0A21DB91235E0EDB00C160A2 /* DigitBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A8321AE2355FE9500CB7F00 /* DigitBox.swift */; }; 0A21DB91235E0EDB00C160A2 /* DigitBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A8321AE2355FE9500CB7F00 /* DigitBox.swift */; };
0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */; }; 0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */; };
0A25209624645AFD000FA9F6 /* TextViewEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A25209524645AFD000FA9F6 /* TextViewEntryField.swift */; };
0A25209824645B76000FA9F6 /* TextViewEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A25209724645B76000FA9F6 /* TextViewEntryFieldModel.swift */; };
0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; }; 0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; };
0A41BA7F23453A6400D4C0BC /* TextEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */; }; 0A41BA7F23453A6400D4C0BC /* TextEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */; };
0A5D59C223AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */; }; 0A5D59C223AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */; };
@ -76,7 +78,6 @@
0A6682AA2435125F00AD3CA1 /* Styler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682A92435125F00AD3CA1 /* Styler.swift */; }; 0A6682AA2435125F00AD3CA1 /* Styler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682A92435125F00AD3CA1 /* Styler.swift */; };
0A6682AC243531C300AD3CA1 /* Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682AB243531C300AD3CA1 /* Padding.swift */; }; 0A6682AC243531C300AD3CA1 /* Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682AB243531C300AD3CA1 /* Padding.swift */; };
0A6682B5243769C700AD3CA1 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682B3243769C700AD3CA1 /* TextView.swift */; }; 0A6682B5243769C700AD3CA1 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682B3243769C700AD3CA1 /* TextView.swift */; };
0A6682B6243769C700AD3CA1 /* TextViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682B4243769C700AD3CA1 /* TextViewModel.swift */; };
0A69F611241BDEA700F7231B /* RuleAnyRequiredModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A69F610241BDEA700F7231B /* RuleAnyRequiredModel.swift */; }; 0A69F611241BDEA700F7231B /* RuleAnyRequiredModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A69F610241BDEA700F7231B /* RuleAnyRequiredModel.swift */; };
0A6BF4722360C56C0028F841 /* BaseDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6BF4712360C56C0028F841 /* BaseDropdownEntryField.swift */; }; 0A6BF4722360C56C0028F841 /* BaseDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6BF4712360C56C0028F841 /* BaseDropdownEntryField.swift */; };
0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */; }; 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */; };
@ -479,6 +480,8 @@
0A21DB7E235DECC500C160A2 /* EntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryField.swift; sourceTree = "<group>"; }; 0A21DB7E235DECC500C160A2 /* EntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryField.swift; sourceTree = "<group>"; };
0A21DB82235DFBC500C160A2 /* MdnEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MdnEntryField.swift; sourceTree = "<group>"; }; 0A21DB82235DFBC500C160A2 /* MdnEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MdnEntryField.swift; sourceTree = "<group>"; };
0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigitEntryField.swift; sourceTree = "<group>"; }; 0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigitEntryField.swift; sourceTree = "<group>"; };
0A25209524645AFD000FA9F6 /* TextViewEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextViewEntryField.swift; sourceTree = "<group>"; };
0A25209724645B76000FA9F6 /* TextViewEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextViewEntryFieldModel.swift; sourceTree = "<group>"; };
0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = "<group>"; }; 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = "<group>"; };
0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = "<group>"; }; 0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = "<group>"; };
0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleGuidelinesProtocol.swift; sourceTree = "<group>"; }; 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleGuidelinesProtocol.swift; sourceTree = "<group>"; };
@ -487,7 +490,6 @@
0A6682A92435125F00AD3CA1 /* Styler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Styler.swift; sourceTree = "<group>"; }; 0A6682A92435125F00AD3CA1 /* Styler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Styler.swift; sourceTree = "<group>"; };
0A6682AB243531C300AD3CA1 /* Padding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Padding.swift; sourceTree = "<group>"; }; 0A6682AB243531C300AD3CA1 /* Padding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Padding.swift; sourceTree = "<group>"; };
0A6682B3243769C700AD3CA1 /* TextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = "<group>"; }; 0A6682B3243769C700AD3CA1 /* TextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = "<group>"; };
0A6682B4243769C700AD3CA1 /* TextViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextViewModel.swift; sourceTree = "<group>"; };
0A69F610241BDEA700F7231B /* RuleAnyRequiredModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleAnyRequiredModel.swift; sourceTree = "<group>"; }; 0A69F610241BDEA700F7231B /* RuleAnyRequiredModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleAnyRequiredModel.swift; sourceTree = "<group>"; };
0A6BF4712360C56C0028F841 /* BaseDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDropdownEntryField.swift; sourceTree = "<group>"; }; 0A6BF4712360C56C0028F841 /* BaseDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDropdownEntryField.swift; sourceTree = "<group>"; };
0A7918F423F5E7EA00772FF4 /* ImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = "<group>"; }; 0A7918F423F5E7EA00772FF4 /* ImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = "<group>"; };
@ -1650,6 +1652,8 @@
0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */, 0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */,
0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */, 0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */,
0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */, 0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */,
0A25209724645B76000FA9F6 /* TextViewEntryFieldModel.swift */,
0A25209524645AFD000FA9F6 /* TextViewEntryField.swift */,
); );
path = TextFields; path = TextFields;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1760,7 +1764,6 @@
D2B18B7D236090D500A9AEDC /* BaseClasses */ = { D2B18B7D236090D500A9AEDC /* BaseClasses */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0A6682B4243769C700AD3CA1 /* TextViewModel.swift */,
0A6682B3243769C700AD3CA1 /* TextView.swift */, 0A6682B3243769C700AD3CA1 /* TextView.swift */,
C003506023AA94CD00B6AC29 /* Button.swift */, C003506023AA94CD00B6AC29 /* Button.swift */,
D2B18B7E2360913400A9AEDC /* Control.swift */, D2B18B7E2360913400A9AEDC /* Control.swift */,
@ -2038,6 +2041,7 @@
943820842432382400B43AF3 /* WebView.swift in Sources */, 943820842432382400B43AF3 /* WebView.swift in Sources */,
0103B84E23D7E33A009C315C /* HeadlineBodyToggleModel.swift in Sources */, 0103B84E23D7E33A009C315C /* HeadlineBodyToggleModel.swift in Sources */,
D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */, D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */,
0A25209624645AFD000FA9F6 /* TextViewEntryField.swift in Sources */,
014AA72623C501E2006F3E93 /* ContainerModelProtocol.swift in Sources */, 014AA72623C501E2006F3E93 /* ContainerModelProtocol.swift in Sources */,
AA11A42123F15D7000D7962F /* ListRightVariablePaymentsModel.swift in Sources */, AA11A42123F15D7000D7962F /* ListRightVariablePaymentsModel.swift in Sources */,
011D9626240EBB16000E3791 /* RadioButtonLabelModel.swift in Sources */, 011D9626240EBB16000E3791 /* RadioButtonLabelModel.swift in Sources */,
@ -2150,7 +2154,6 @@
012A88B1238C880100FE3DA1 /* CarouselPagingModelProtocol.swift in Sources */, 012A88B1238C880100FE3DA1 /* CarouselPagingModelProtocol.swift in Sources */,
0A9D091E2433796500D2E6C0 /* NumericCarouselIndicatorModel.swift in Sources */, 0A9D091E2433796500D2E6C0 /* NumericCarouselIndicatorModel.swift in Sources */,
D29DF2C921E7BFC6003B2FB9 /* MFSizeObject.m in Sources */, D29DF2C921E7BFC6003B2FB9 /* MFSizeObject.m in Sources */,
0A6682B6243769C700AD3CA1 /* TextViewModel.swift in Sources */,
9445890E2385C3F800DE9FD4 /* MultiProgressModel.swift in Sources */, 9445890E2385C3F800DE9FD4 /* MultiProgressModel.swift in Sources */,
011D95A5240455DC000E3791 /* FormGroupRule.swift in Sources */, 011D95A5240455DC000E3791 /* FormGroupRule.swift in Sources */,
D2A6390522CBCE160052ED1F /* MoleculeCollectionViewCell.swift in Sources */, D2A6390522CBCE160052ED1F /* MoleculeCollectionViewCell.swift in Sources */,
@ -2272,6 +2275,7 @@
AA26850C244840AE00CE34CC /* HeadersH2TinyButton.swift in Sources */, AA26850C244840AE00CE34CC /* HeadersH2TinyButton.swift in Sources */,
011D95AB2405C553000E3791 /* FormItemProtocol.swift in Sources */, 011D95AB2405C553000E3791 /* FormItemProtocol.swift in Sources */,
D21EE53C23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift in Sources */, D21EE53C23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift in Sources */,
0A25209824645B76000FA9F6 /* TextViewEntryFieldModel.swift in Sources */,
525019DD2406430800EED91C /* ListProgressBarDataModel.swift in Sources */, 525019DD2406430800EED91C /* ListProgressBarDataModel.swift in Sources */,
C6FA7D5223C77A4A00A3614A /* UnOrderedList.swift in Sources */, C6FA7D5223C77A4A00A3614A /* UnOrderedList.swift in Sources */,
01509D8F2327EC6F00EF99AA /* MoleculeTableViewCell.swift in Sources */, 01509D8F2327EC6F00EF99AA /* MoleculeTableViewCell.swift in Sources */,

View File

@ -69,6 +69,7 @@ import UIKit
get { return entryFieldContainer.showError } get { return entryFieldContainer.showError }
set (error) { set (error) {
self.feedback = error ? entryFieldModel?.errorMessage : entryFieldModel?.feedback self.feedback = error ? entryFieldModel?.errorMessage : entryFieldModel?.feedback
self.feedbackLabel.textColor = error ? entryFieldModel?.errorTextColor?.uiColor ?? .mvmBlack : .mvmBlack
self.entryFieldContainer.showError = error self.entryFieldContainer.showError = error
self.entryFieldModel?.showError = error self.entryFieldModel?.showError = error
} }
@ -215,11 +216,10 @@ import UIKit
entryFieldContainer.refreshUI() entryFieldContainer.refreshUI()
} }
/** /// Intended to add the interactive content (i.e. textField) to the entryFieldContainer.
Method to override. @objc open func setupFieldContainerContent(_ container: UIView) {
Intended to add the interactive content (i.e. textField) to the entryFieldContainer. // To Be Overriden
*/ }
@objc open func setupFieldContainerContent(_ container: UIView) { }
@objc open override func updateView(_ size: CGFloat) { @objc open override func updateView(_ size: CGFloat) {
super.updateView(size) super.updateView(size)
@ -242,6 +242,7 @@ import UIKit
titleLabel.textColor = .mvmBlack titleLabel.textColor = .mvmBlack
feedbackLabel.font = Styler.Font.RegularMicro.getFont() feedbackLabel.font = Styler.Font.RegularMicro.getFont()
feedbackLabel.textColor = .mvmBlack feedbackLabel.textColor = .mvmBlack
entryFieldContainer.disableAllBorders = false
feedbackLabel.text = nil feedbackLabel.text = nil
entryFieldContainer.reset() entryFieldContainer.reset()
} }
@ -257,6 +258,7 @@ import UIKit
title = model.title title = model.title
feedback = model.feedback feedback = model.feedback
isEnabled = model.enabled isEnabled = model.enabled
entryFieldContainer.disableAllBorders = model.hideBorders
if let isLocked = model.locked { if let isLocked = model.locked {
self.isLocked = isLocked self.isLocked = isLocked

View File

@ -22,8 +22,10 @@ import Foundation
public var title: String? public var title: String?
public var feedback: String? public var feedback: String?
public var errorMessage: String? public var errorMessage: String?
public var errorTextColor: Color?
public var enabled: Bool = true public var enabled: Bool = true
public var showError: Bool? public var showError: Bool?
public var hideBorders = false
public var locked: Bool? public var locked: Bool?
public var selected: Bool? public var selected: Bool?
public var text: String? public var text: String?
@ -37,7 +39,7 @@ import Foundation
} }
/// Temporary binding mechanism for the view to update on enable changes. /// Temporary binding mechanism for the view to update on enable changes.
public var updateUI: (() -> ())? public var updateUI: ActionBlock?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
@ -50,9 +52,11 @@ import Foundation
case enabled case enabled
case feedback case feedback
case errorMessage case errorMessage
case errorTextColor
case locked case locked
case selected case selected
case showError case showError
case hideBorders
case text case text
case fieldKey case fieldKey
case groupName case groupName
@ -67,7 +71,12 @@ import Foundation
} }
public func setValidity(_ valid: Bool, rule: RulesProtocol) { public func setValidity(_ valid: Bool, rule: RulesProtocol) {
if let fieldKey = fieldKey,
let ruleErrorMessage = rule.errorMessage?[fieldKey] {
self.errorMessage = ruleErrorMessage
}
self.isValid = valid self.isValid = valid
} }
//-------------------------------------------------- //--------------------------------------------------
@ -89,10 +98,12 @@ import Foundation
title = try typeContainer.decodeIfPresent(String.self, forKey: .title) title = try typeContainer.decodeIfPresent(String.self, forKey: .title)
feedback = try typeContainer.decodeIfPresent(String.self, forKey: .feedback) feedback = try typeContainer.decodeIfPresent(String.self, forKey: .feedback)
errorMessage = try typeContainer.decodeIfPresent(String.self, forKey: .errorMessage) errorMessage = try typeContainer.decodeIfPresent(String.self, forKey: .errorMessage)
errorTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .errorTextColor)
enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true
locked = try typeContainer.decodeIfPresent(Bool.self, forKey: .locked) locked = try typeContainer.decodeIfPresent(Bool.self, forKey: .locked)
selected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected) selected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected)
text = try typeContainer.decodeIfPresent(String.self, forKey: .text) text = try typeContainer.decodeIfPresent(String.self, forKey: .text)
hideBorders = try typeContainer.decodeIfPresent(Bool.self, forKey: .hideBorders) ?? false
baseValue = text baseValue = text
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
@ -111,8 +122,10 @@ import Foundation
try container.encodeIfPresent(locked, forKey: .locked) try container.encodeIfPresent(locked, forKey: .locked)
try container.encodeIfPresent(showError, forKey: .showError) try container.encodeIfPresent(showError, forKey: .showError)
try container.encodeIfPresent(selected, forKey: .selected) try container.encodeIfPresent(selected, forKey: .selected)
try container.encodeIfPresent(errorTextColor, forKey: .errorTextColor)
try container.encodeIfPresent(errorMessage, forKey: .errorMessage) try container.encodeIfPresent(errorMessage, forKey: .errorMessage)
try container.encode(enabled, forKey: .enabled) try container.encode(enabled, forKey: .enabled)
try container.encode(hideBorders, forKey: .hideBorders)
try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
try container.encodeIfPresent(groupName, forKey: .groupName) try container.encodeIfPresent(groupName, forKey: .groupName)
} }

View File

@ -244,14 +244,21 @@ import UIKit
self.isValid = isValid self.isValid = isValid
if previousValidity && !isValid { if previousValidity && !isValid {
showError = true shouldShowError(true)
observingTextFieldDelegate?.isInvalid?(textfield: self)
} else if (!previousValidity && isValid) { } else if (!previousValidity && isValid) {
showError = false shouldShowError(false)
observingTextFieldDelegate?.isValid?(textfield: self) }
}
func shouldShowError(_ showError: Bool) {
self.showError = showError
if showError {
observingTextFieldDelegate?.isValid?(textfield: self)
entryFieldContainer.originalUI()
} else {
observingTextFieldDelegate?.isInvalid?(textfield: self)
} }
} }
/// Executes on UITextField.textDidBeginEditingNotification /// Executes on UITextField.textDidBeginEditingNotification
@objc func startEditing() { @objc func startEditing() {
isSelected = true isSelected = true
@ -268,10 +275,16 @@ import UIKit
/// Executes on UITextField.textDidEndEditingNotification /// Executes on UITextField.textDidEndEditingNotification
@objc func endInputing() { @objc func endInputing() {
resignFirstResponder() resignFirstResponder()
if isValid {
showError = false // Don't show error till user starts typing.
entryFieldContainer.bottomBar?.backgroundColor = UIColor.mvmBlack.cgColor guard text?.count ?? 0 != 0 else {
return
} }
if let isValid = (model as? TextEntryFieldModel)?.isValid {
self.isValid = isValid
}
shouldShowError(!isValid)
} }
@objc public func dismissFieldInput(_ sender: Any?) { @objc public func dismissFieldInput(_ sender: Any?) {

View File

@ -31,6 +31,7 @@
public var placeholder: String? public var placeholder: String?
public var enabledTextColor: Color = Color(uiColor: .mvmBlack) public var enabledTextColor: Color = Color(uiColor: .mvmBlack)
public var disabledTextColor: Color = Color(uiColor: .mvmCoolGray3) public var disabledTextColor: Color = Color(uiColor: .mvmCoolGray3)
public var textAlignment: NSTextAlignment = .left
public var type: EntryType? public var type: EntryType?
//-------------------------------------------------- //--------------------------------------------------
@ -39,6 +40,7 @@
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case placeholder case placeholder
case textAlignment
case enabledTextColor case enabledTextColor
case disabledTextColor case disabledTextColor
case type case type
@ -51,6 +53,7 @@
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
try super.init(from: decoder) try super.init(from: decoder)
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
placeholder = try typeContainer.decodeIfPresent(String.self, forKey: .placeholder) placeholder = try typeContainer.decodeIfPresent(String.self, forKey: .placeholder)
type = try typeContainer.decodeIfPresent(EntryType.self, forKey: .type) type = try typeContainer.decodeIfPresent(EntryType.self, forKey: .type)
@ -61,12 +64,17 @@
if let disabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledTextColor) { if let disabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledTextColor) {
self.disabledTextColor = disabledTextColor self.disabledTextColor = disabledTextColor
} }
if let textAlignment = try typeContainer.decodeIfPresent(NSTextAlignment.self, forKey: .textAlignment) {
self.textAlignment = textAlignment
}
} }
public override func encode(to encoder: Encoder) throws { public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder) try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(placeholder, forKey: .placeholder) try container.encodeIfPresent(placeholder, forKey: .placeholder)
try container.encodeIfPresent(textAlignment, forKey: .textAlignment)
try container.encode(enabledTextColor, forKey: .enabledTextColor) try container.encode(enabledTextColor, forKey: .enabledTextColor)
try container.encode(disabledTextColor, forKey: .disabledTextColor) try container.encode(disabledTextColor, forKey: .disabledTextColor)
try container.encodeIfPresent(type, forKey: .type) try container.encodeIfPresent(type, forKey: .type)

View File

@ -0,0 +1,279 @@
//
// TextViewEntryField.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 5/7/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import UIKit
class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDelegate {
//--------------------------------------------------
// MARK: - Outlets
//--------------------------------------------------
open private(set) var textView: TextView = {
let textView = TextView()
textView.setContentCompressionResistancePriority(.required, for: .vertical)
return textView
}()
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
/// Validate on each entry in the textView. Default: true
public var validateEachCharacter: Bool = true
private var observingForChange: Bool = false
//--------------------------------------------------
// MARK: - Computed Properties
//--------------------------------------------------
public var textViewEntryFieldModel: TextViewEntryFieldModel? {
return model as? TextViewEntryFieldModel
}
public override var isEnabled: Bool {
get { return super.isEnabled }
set (enabled) {
super.isEnabled = enabled
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.textView.isEnabled = enabled
if self.textView.isShowingPlaceholder {
self.textView.textColor = self.textView.placeholderTextColor
} else {
self.textView.textColor = (enabled ? self.textViewEntryFieldModel?.enabledTextColor : self.textViewEntryFieldModel?.disabledTextColor)?.uiColor
}
}
}
}
public override var showError: Bool {
get { return super.showError }
set (error) {
if error {
textView.accessibilityValue = String(format: MVMCoreUIUtility.hardcodedString(withKey: "textView_error_message") ?? "", textView.text ?? "", entryFieldModel?.errorMessage ?? "")
} else {
textView.accessibilityValue = nil
}
super.showError = error
}
}
/// The text of this textView.
open override var text: String? {
get { return textView.text }
set {
textView.text = newValue
textViewEntryFieldModel?.text = newValue
}
}
/// Placeholder access for the textView.
public var placeholder: String? {
get { return textViewEntryFieldModel?.placeholder }
set {
textView.placeholder = newValue ?? ""
textViewEntryFieldModel?.placeholder = newValue
textView.setPlaceholderIfAvailable()
}
}
//--------------------------------------------------
// MARK: - Constraint
//--------------------------------------------------
public var heightConstraint: NSLayoutConstraint?
private var topConstraint: NSLayoutConstraint?
private var leadingConstraint: NSLayoutConstraint?
private var trailingConstraint: NSLayoutConstraint?
private var bottomConstraint: NSLayoutConstraint?
private func adjustMarginConstraints(constant: CGFloat) {
topConstraint?.constant = constant
leadingConstraint?.constant = constant
trailingConstraint?.constant = constant
bottomConstraint?.constant = constant
}
//--------------------------------------------------
// MARK: - Delegate Properties
//--------------------------------------------------
/// The delegate and block for validation. Validates if the text that the user has entered.
public weak var observingTextViewDelegate: ObservingTextFieldDelegate? {
didSet {
if observingTextViewDelegate != nil && !observingForChange {
observingForChange = true
NotificationCenter.default.addObserver(self, selector: #selector(valueChanged), name: UITextView.textDidChangeNotification, object: textView)
NotificationCenter.default.addObserver(self, selector: #selector(endInputing), name: UITextView.textDidEndEditingNotification, object: textView)
NotificationCenter.default.addObserver(self, selector: #selector(startEditing), name: UITextView.textDidBeginEditingNotification, object: textView)
} else if observingTextViewDelegate == nil && observingForChange {
observingForChange = false
NotificationCenter.default.removeObserver(self, name: UITextView.textDidChangeNotification, object: textView)
NotificationCenter.default.removeObserver(self, name: UITextView.textDidEndEditingNotification, object: textView)
NotificationCenter.default.removeObserver(self, name: UITextView.textDidBeginEditingNotification, object: textView)
}
}
}
/// If you're using a ViewController, you must set this to it
public weak var uiTextViewDelegate: UITextViewDelegate? {
get { return textView.delegate }
set { textView.delegate = newValue }
}
@objc public func setBothTextDelegates(to delegate: (UITextViewDelegate & ObservingTextFieldDelegate)?) {
observingTextViewDelegate = delegate
uiTextViewDelegate = delegate
}
open func setupTextViewToolbar() {
let observingDelegate = observingTextViewDelegate ?? self
textView.inputAccessoryView = UIToolbar.getToolbarWithDoneButton(delegate: observingDelegate,
action: #selector(observingDelegate.dismissFieldInput))
}
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
@objc open override func setupFieldContainerContent(_ container: UIView) {
container.addSubview(textView)
topConstraint = textView.topAnchor.constraint(equalTo: container.topAnchor, constant: Padding.Three)
leadingConstraint = textView.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: Padding.Three)
trailingConstraint = container.trailingAnchor.constraint(equalTo: textView.trailingAnchor, constant: Padding.Three)
bottomConstraint = container.bottomAnchor.constraint(equalTo: textView.bottomAnchor, constant: Padding.Three)
topConstraint?.isActive = true
leadingConstraint?.isActive = true
trailingConstraint?.isActive = true
bottomConstraint?.isActive = true
heightConstraint = textView.heightAnchor.constraint(equalToConstant: 0)
accessibilityElements = [titleLabel, textView, feedbackLabel]
}
open override func updateView(_ size: CGFloat) {
super.updateView(size)
textView.updateView(size)
}
open override func reset() {
super.reset()
textView.reset()
adjustMarginConstraints(constant: Padding.Three)
heightConstraint?.constant = 0
heightConstraint?.isActive = false
}
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
/// Validates the text of the entry field.
@objc public func validateTextView() {
text = textView.text
if let isValid = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) {
self.isValid = isValid
}
}
/// Executes on UITextView.textDidBeginEditingNotification
@objc func startEditing() {
isSelected = true
_ = textView.becomeFirstResponder()
}
/// Executes on UITextView.textDidChangeNotification (each character entry)
@objc func valueChanged() {
guard validateEachCharacter else { return }
validateTextView()
}
/// Executes on UITextView.textDidEndEditingNotification
@objc func endInputing() {
resignFirstResponder()
isSelected = false
showError = !isValid
}
//--------------------------------------------------
// MARK: - MoleculeViewProtocol
//--------------------------------------------------
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData)
guard let model = model as? TextViewEntryFieldModel else { return }
if let height = model.height {
heightConstraint?.constant = height
heightConstraint?.isActive = true
}
text = model.text
uiTextViewDelegate = delegateObject?.uiTextViewDelegate
observingTextViewDelegate = delegateObject?.observingTextFieldDelegate
if let accessibilityText = model.accessibilityText {
accessibilityLabel = accessibilityText
}
textView.isEditable = model.editable
textView.textAlignment = model.textAlignment
textView.textColor = model.enabled ? model.enabledTextColor.uiColor : model.disabledTextColor.uiColor
textView.font = model.fontStyle.getFont()
textView.placeholder = model.placeholder ?? ""
textView.placeholderFontStyle = model.placeholderFontStyle
textView.placeholderTextColor = model.placeholderTextColor.uiColor
textView.setPlaceholderIfAvailable()
switch model.type {
case .secure, .password:
textView.isSecureTextEntry = true
case .number:
textView.keyboardType = .numberPad
case .email:
textView.keyboardType = .emailAddress
default: break
}
/// No point in configuring if the TextView is Read-only.
if textView.isEditable {
FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
setupTextViewToolbar()
if isSelected {
DispatchQueue.main.async {
_ = self.textView.becomeFirstResponder()
}
}
}
if model.hideBorders {
adjustMarginConstraints(constant: 0)
}
if !model.enabled {
isEnabled = false
}
}
}

View File

@ -1,15 +1,15 @@
// //
// TextViewModel.swift // TextViewEntryFieldModel.swift
// MVMCoreUI // MVMCoreUI
// //
// Created by Kevin Christiano on 4/2/20. // Created by Kevin Christiano on 5/7/20.
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation import UIKit
open class TextViewModel: TextEntryFieldModel { class TextViewEntryFieldModel: TextEntryFieldModel {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -19,26 +19,21 @@ open class TextViewModel: TextEntryFieldModel {
} }
public var accessibilityText: String? public var accessibilityText: String?
public var fontStyle: Styler.Font = Styler.Font.RegularBodySmall public var fontStyle: Styler.Font = Styler.Font.RegularBodyLarge
public var textAlignment: NSTextAlignment = .left
public var height: CGFloat? public var height: CGFloat?
public var placeholderTextColor: Color = Color(uiColor: .mvmCoolGray3) public var placeholderTextColor: Color = Color(uiColor: .mvmCoolGray3)
public var placeholderFontStyle: Styler.Font = Styler.Font.RegularMicro public var placeholderFontStyle: Styler.Font = Styler.Font.RegularMicro
public var showsPlaceholder: Bool = false
public var hideBorders: Bool = false
public var editable: Bool = true public var editable: Bool = true
public var showsPlaceholder: Bool = false
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
//-------------------------------------------------- //--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case text
case accessibilityText case accessibilityText
case fontStyle case fontStyle
case textAlignment
case height case height
case hideBorders
case placeholderFontStyle case placeholderFontStyle
case placeholderTextColor case placeholderTextColor
case editable case editable
@ -60,22 +55,14 @@ open class TextViewModel: TextEntryFieldModel {
self.placeholderTextColor = placeholderTextColor self.placeholderTextColor = placeholderTextColor
} }
if let textAlignment = try typeContainer.decodeIfPresent(NSTextAlignment.self, forKey: .textAlignment) { if let fontStyle = try typeContainer.decodeIfPresent(Styler.Font.self, forKey: .fontStyle) {
self.textAlignment = textAlignment self.fontStyle = fontStyle
}
if let hideBorders = try typeContainer.decodeIfPresent(Bool.self, forKey: .hideBorders) {
self.hideBorders = hideBorders
} }
if let editable = try typeContainer.decodeIfPresent(Bool.self, forKey: .editable) { if let editable = try typeContainer.decodeIfPresent(Bool.self, forKey: .editable) {
self.editable = editable self.editable = editable
} }
if let fontStyle = try typeContainer.decodeIfPresent(Styler.Font.self, forKey: .fontStyle) {
self.fontStyle = fontStyle
}
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)
} }
@ -86,11 +73,8 @@ open class TextViewModel: TextEntryFieldModel {
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText) try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
try container.encodeIfPresent(height, forKey: .height) try container.encodeIfPresent(height, forKey: .height)
try container.encode(fontStyle, forKey: .fontStyle) try container.encode(fontStyle, forKey: .fontStyle)
try container.encode(hideBorders, forKey: .hideBorders) try container.encode(editable, forKey: .editable)
try container.encode(text, forKey: .text)
try container.encode(placeholderFontStyle, forKey: .placeholderFontStyle) try container.encode(placeholderFontStyle, forKey: .placeholderFontStyle)
try container.encode(placeholderTextColor, forKey: .placeholderTextColor) try container.encode(placeholderTextColor, forKey: .placeholderTextColor)
try container.encode(textAlignment, forKey: .textAlignment)
try container.encode(editable, forKey: .editable)
} }
} }

View File

@ -32,16 +32,16 @@ import UIKit
switch style { switch style {
case .standard: case .standard:
updateLineConstraints(constant: 1) updateLineConstraints(constant: 1)
backgroundColor = lineModel?.backgroundColor?.uiColor ?? .mfSilver() backgroundColor = lineModel?.backgroundColor?.uiColor ?? .mvmCoolGray3
case .thin: case .thin:
updateLineConstraints(constant: 1) updateLineConstraints(constant: 1)
backgroundColor = lineModel?.backgroundColor?.uiColor ?? .black backgroundColor = lineModel?.backgroundColor?.uiColor ?? .mvmBlack
case .medium: case .medium:
updateLineConstraints(constant: 2) updateLineConstraints(constant: 2)
backgroundColor = lineModel?.backgroundColor?.uiColor ?? .black backgroundColor = lineModel?.backgroundColor?.uiColor ?? .mvmBlack
case .heavy: case .heavy:
updateLineConstraints(constant: 4) updateLineConstraints(constant: 4)
backgroundColor = lineModel?.backgroundColor?.uiColor ?? .black backgroundColor = lineModel?.backgroundColor?.uiColor ?? .mvmBlack
case .none: case .none:
updateLineConstraints(constant: 0) updateLineConstraints(constant: 0)
} }
@ -97,6 +97,7 @@ import UIKit
} }
extension Line: MVMCoreUIViewConstrainingProtocol { extension Line: MVMCoreUIViewConstrainingProtocol {
open func needsToBeConstrained() -> Bool { open func needsToBeConstrained() -> Bool {
return true return true
} }

View File

@ -52,7 +52,7 @@ import Foundation
try? ModelRegistry.register(LabelAttributeActionModel.self) try? ModelRegistry.register(LabelAttributeActionModel.self)
// TextView // TextView
MoleculeObjectMapping.shared()?.register(viewClass: TextView.self, viewModelClass: TextViewModel.self) MoleculeObjectMapping.shared()?.register(viewClass: TextViewEntryField.self, viewModelClass: TextViewEntryFieldModel.self)
// Buttons // Buttons
MoleculeObjectMapping.shared()?.register(viewClass: PillButton.self, viewModelClass: ButtonModel.self) MoleculeObjectMapping.shared()?.register(viewClass: PillButton.self, viewModelClass: ButtonModel.self)

View File

@ -9,7 +9,7 @@
import UIKit import UIKit
@objc open class TextView: UITextView, UITextViewDelegate, MVMCoreViewProtocol { @objc open class TextView: UITextView, MVMCoreViewProtocol, MoleculeViewProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -19,87 +19,18 @@ import UIKit
private var initialSetupPerformed = false private var initialSetupPerformed = false
/// If true then text textView is currently displaying the stored placeholder text as there is not content to display. /// If true then text textView is currently displaying the stored placeholder text as there is not content to display.
public var isShowingPlaceholder: Bool = false { public var isShowingPlaceholder: Bool = false
didSet { textViewModel?.showsPlaceholder = isShowingPlaceholder }
}
/// Set to true to hide the blinking textField cursor. /// Set to true to hide the blinking textField cursor.
public var hideBlinkingCaret = false public var hideBlinkingCaret = false
public var textViewModel: TextViewModel? { public var placeholder = ""
return model as? TextViewModel public var fontStyle: Styler.Font = Styler.Font.RegularBodyLarge
} public var placeholderFontStyle: Styler.Font = Styler.Font.RegularMicro
public var placeholderTextColor: UIColor = .mvmCoolGray3
//-------------------------------------------------- public var isEnabled: Bool = true {
// MARK: - Drawing Properties didSet { isUserInteractionEnabled = isEnabled }
//--------------------------------------------------
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 _isSelected: Bool = false
public var isEnabled: Bool {
get { return _isEnabled }
set (enabled) {
_isEnabled = enabled
_isSelected = false
_showError = false
fieldState = enabled ? .original : .disabled
}
}
public var showError: Bool {
get { return _showError }
set (error) {
_showError = error
_isEnabled = true
_isSelected = false
fieldState = error ? .error : .original
}
}
public var isSelected: Bool {
get { return _isSelected }
set (selected) {
_isSelected = selected
_isEnabled = true
if _showError {
fieldState = selected ? .selectedError : .error
} else {
fieldState = selected ? .selected : .original
}
}
} }
//-------------------------------------------------- //--------------------------------------------------
@ -109,26 +40,6 @@ import UIKit
/// Holds a reference to the delegating class so this class can internally influence the TextField behavior as well. /// Holds a reference to the delegating class so this class can internally influence the TextField behavior as well.
public weak var didDeleteDelegate: TextInputDidDeleteProtocol? 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
}
}
var delegateObject: MVMCoreUIDelegateObject?
//--------------------------------------------------
// MARK: - Constraint
//--------------------------------------------------
public var heightConstraint: NSLayoutConstraint?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initialization // MARK: - Initialization
//-------------------------------------------------- //--------------------------------------------------
@ -147,11 +58,6 @@ import UIKit
initialSetup() initialSetup()
} }
convenience init(delegate: UITextViewDelegate) {
self.init(frame: .zero, textContainer: nil)
self.delegate = delegate
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Lifecycle // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
@ -166,172 +72,53 @@ import UIKit
} }
open func updateView(_ size: CGFloat) { open func updateView(_ size: CGFloat) {
font = (isShowingPlaceholder ? placeholderFontStyle : fontStyle).getFont()
setNeedsDisplay()
} }
/// Will be called only once. /// Will be called only once.
open func setupView() { open func setupView() {
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
initialConfiguration() defaultConfiguration()
} }
public func initialConfiguration() { public func defaultConfiguration() {
text = ""
placeholder = ""
textAlignment = .left
insetsLayoutMarginsFromSafeArea = false insetsLayoutMarginsFromSafeArea = false
showsVerticalScrollIndicator = false showsVerticalScrollIndicator = false
showsHorizontalScrollIndicator = false showsHorizontalScrollIndicator = false
isSecureTextEntry = false isSecureTextEntry = false
textContainerInset = UIEdgeInsets(top: Padding.Three, left: Padding.Three, bottom: Padding.Three, right: Padding.Three)
backgroundColor = .mvmWhite backgroundColor = .mvmWhite
clipsToBounds = true clipsToBounds = true
smartQuotesType = .no smartQuotesType = .no
smartDashesType = .no smartDashesType = .no
smartInsertDeleteType = .no smartInsertDeleteType = .no
inputAccessoryView = nil inputAccessoryView = nil
font = textViewModel?.fontStyle.getFont() isAccessibilityElement = true
accessibilityTraits = .staticText
font = fontStyle.getFont()
keyboardType = .default
isEditable = true isEditable = true
isOpaque = false
} }
open func reset() { open func reset() {
fontStyle = Styler.Font.RegularBodyLarge
placeholderFontStyle = Styler.Font.RegularMicro
placeholderTextColor = .mvmCoolGray3
textColor = .mvmBlack
isEnabled = true
text = "" text = ""
isShowingPlaceholder = false
inputAccessoryView?.removeFromSuperview() inputAccessoryView?.removeFromSuperview()
inputAccessoryView = nil defaultConfiguration()
initialConfiguration()
}
open override func layoutSubviews() {
super.layoutSubviews()
setNeedsDisplay()
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Draw // MARK: - TextInputDidDeleteProtocol
//--------------------------------------------------
/// This handles the top, left, and right border lines.
open override func draw(_ rect: CGRect) {
super.draw(rect)
borderPath.removeAllPoints()
bottomPath.removeAllPoints()
if !hideBorders {
// Brings the other half of the line inside the view to prevent line cropping.
let origin = bounds.origin
let size = frame.size
let insetLean: CGFloat = 0.5
borderPath.lineWidth = 1
// Drawing begins and ends from the bottom left.
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()
let lineWidth: CGFloat = showError || isSelected ? 4 : 1
bottomPath.lineWidth = lineWidth
bottomPath.move(to: CGPoint(x: origin.x + size.width, y: origin.y + size.height - (lineWidth / 2)))
bottomPath.addLine(to: CGPoint(x: origin.x, y: origin.y + size.height - (lineWidth / 2)))
bottomStrokeColor.setStroke()
bottomPath.stroke()
}
}
//--------------------------------------------------
// MARK: - Draw States
//--------------------------------------------------
public enum FieldState {
case original
case error
case selectedError
case selected
case disabled
public func setStateUI(for inputField: TextView) {
switch self {
case .original:
inputField.originalUI()
case .error:
inputField.errorUI()
case .selectedError:
inputField.selectedErrorUI()
case .selected:
inputField.selectedUI()
case .disabled:
inputField.disabledUI()
}
inputField.setNeedsDisplay()
}
}
open func originalUI() {
isEditable = textViewModel?.editable ?? true
isUserInteractionEnabled = true
hideBorders = textViewModel?.hideBorders ?? false
borderStrokeColor = .mvmCoolGray3
bottomStrokeColor = .mvmBlack
textColor = isShowingPlaceholder ? textViewModel?.placeholderTextColor.uiColor : textViewModel?.enabledTextColor.uiColor
}
open func errorUI() {
isEditable = textViewModel?.editable ?? true
isUserInteractionEnabled = true
hideBorders = textViewModel?.hideBorders ?? false
borderStrokeColor = .mvmOrange
bottomStrokeColor = .mvmOrange
textColor = textViewModel?.enabledTextColor.uiColor
}
open func selectedErrorUI() {
isEditable = textViewModel?.editable ?? true
isUserInteractionEnabled = true
hideBorders = textViewModel?.hideBorders ?? false
borderStrokeColor = .mvmBlack
bottomStrokeColor = .mvmOrange
textColor = textViewModel?.enabledTextColor.uiColor
}
open func selectedUI() {
isEditable = textViewModel?.editable ?? true
isUserInteractionEnabled = true
hideBorders = textViewModel?.hideBorders ?? false
borderStrokeColor = .mvmBlack
bottomStrokeColor = .mvmBlack
textColor = textViewModel?.enabledTextColor.uiColor
}
open func disabledUI() {
isEditable = textViewModel?.editable ?? false
isUserInteractionEnabled = false
hideBorders = textViewModel?.hideBorders ?? false
borderStrokeColor = .mvmCoolGray3
bottomStrokeColor = .mvmCoolGray3
textColor = textViewModel?.disabledTextColor.uiColor
}
//--------------------------------------------------
// MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
/// Alters the blinking caret line as per design standards. /// Alters the blinking caret line as per design standards.
@ -350,141 +137,49 @@ import UIKit
didDeleteDelegate?.textInputDidDelete() didDeleteDelegate?.textInputDidDelete()
} }
public func setTextAppearance() { //--------------------------------------------------
// MARK: - Text / Placeholder
//--------------------------------------------------
open override func becomeFirstResponder() -> Bool {
if isShowingPlaceholder { if isShowingPlaceholder {
setTextContentTraits() setTextContentTraits()
} }
return super.becomeFirstResponder()
}
open override func resignFirstResponder() -> Bool {
setPlaceholderIfAvailable()
return super.resignFirstResponder()
} }
public func setPlaceholderIfAvailable() { public func setPlaceholderIfAvailable() {
if let placeholder = textViewModel?.placeholder, !placeholder.isEmpty && text.isEmpty { if !placeholder.isEmpty && text.isEmpty {
setPlaceholderContentTraits() setPlaceholderContentTraits()
} }
} }
public func setTextContentTraits() { open func setTextContentTraits() {
isShowingPlaceholder = false isShowingPlaceholder = false
text = "" text = ""
font = textViewModel?.fontStyle.getFont() font = fontStyle.getFont()
textColor = textViewModel?.enabledTextColor.uiColor textColor = .mvmBlack
} }
public func setPlaceholderContentTraits() { open func setPlaceholderContentTraits() {
isShowingPlaceholder = true isShowingPlaceholder = true
textColor = textViewModel?.placeholderTextColor.uiColor textColor = placeholderTextColor
font = textViewModel?.placeholderFontStyle.getFont() font = placeholderFontStyle.getFont()
text = textViewModel?.placeholder text = placeholder
} }
@objc func dismissFieldInput(_ sender: TextView) { @objc open func dismissFieldInput(_ sender: TextView) {
resignFirstResponder() _ = resignFirstResponder()
}
//--------------------------------------------------
// MARK: - UITextViewDelegate
//--------------------------------------------------
@objc public func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
return proprietorTextDelegate?.textViewShouldBeginEditing?(textView) ?? true
}
@objc public func textViewDidBeginEditing(_ textView: UITextView) {
setTextAppearance()
isSelected = true
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()
isSelected = false
proprietorTextDelegate?.textViewDidEndEditing?(textView)
}
}
// MARK:- MoleculeViewProtocol
extension TextView: MoleculeViewProtocol {
open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
self.model = model
self.delegateObject = delegateObject
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.editable
textAlignment = model.textAlignment
textColor = model.enabledTextColor.uiColor
hideBorders = model.hideBorders
text = model.text
uiTextViewDelegate = delegateObject?.uiTextViewDelegate
if let accessibilityText = model.accessibilityText {
accessibilityLabel = accessibilityText
}
switch model.type {
case .secure, .password:
isSecureTextEntry = true
case .number:
keyboardType = .numberPad
case .email:
keyboardType = .emailAddress
default:
break
}
font = model.fontStyle.getFont()
setPlaceholderIfAvailable()
if isEditable {
FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
let observingDelegate = delegateObject?.uiTextViewDelegate ?? self
inputAccessoryView = UIToolbar.getToolbarWithDoneButton(delegate: observingDelegate,
action: #selector(dismissFieldInput))
if (model.selected ?? false) && !model.wasInitiallySelected {
model.wasInitiallySelected = true
DispatchQueue.main.async {
self.becomeFirstResponder()
}
}
}
if !model.enabled {
isEnabled = false
}
} }
} }

View File

@ -58,7 +58,7 @@ import UIKit
if navigationController == MVMCoreUISession.sharedGlobal()?.navigationController, if navigationController == MVMCoreUISession.sharedGlobal()?.navigationController,
navigationController.topViewController == viewController { navigationController.topViewController == viewController {
// Update line. // Update line.
MVMCoreUISession.sharedGlobal()?.navigationController?.separatorView?.setStyle(navigationItemModel.line?.type ?? .standard) MVMCoreUISession.sharedGlobal()?.navigationController?.separatorView?.isHidden = navigationItemModel.line?.type ?? .standard == .none
} }
if navigationController == MVMCoreUISplitViewController.main()?.navigationController, if navigationController == MVMCoreUISplitViewController.main()?.navigationController,

View File

@ -15,6 +15,7 @@ public class RuleAllValueChangedModel: RulesProtocol {
public static var identifier: String = "allValueChanged" public static var identifier: String = "allValueChanged"
public var type: String = RuleAllValueChangedModel.identifier public var type: String = RuleAllValueChangedModel.identifier
public var errorMessage: [String: String]?
public var fields: [String] public var fields: [String]
//-------------------------------------------------- //--------------------------------------------------

View File

@ -17,6 +17,7 @@ public class RuleAnyRequiredModel: RulesProtocol {
public static var identifier: String = "anyRequired" public static var identifier: String = "anyRequired"
public var type: String = RuleRequiredModel.identifier public var type: String = RuleRequiredModel.identifier
public var fields: [String] public var fields: [String]
public var errorMessage: [String: String]?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Methods // MARK: - Methods

View File

@ -16,6 +16,7 @@ public class RuleAnyValueChangedModel: RulesProtocol {
public static var identifier: String = "anyValueChanged" public static var identifier: String = "anyValueChanged"
public var type: String = RuleAnyValueChangedModel.identifier public var type: String = RuleAnyValueChangedModel.identifier
public var errorMessage: [String: String]?
public var fields: [String] public var fields: [String]
//-------------------------------------------------- //--------------------------------------------------

View File

@ -17,6 +17,7 @@ public class RuleEqualsModel: RulesProtocol {
public static var identifier: String = "equals" public static var identifier: String = "equals"
public var type: String = RuleEqualsModel.identifier public var type: String = RuleEqualsModel.identifier
public var fields: [String] public var fields: [String]
public var errorMessage: [String: String]?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Validation // MARK: - Validation
@ -27,9 +28,9 @@ public class RuleEqualsModel: RulesProtocol {
} }
public func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool { public func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool {
var valid = true var valid = true
var compareValue: AnyHashable? var compareValue: AnyHashable?
for formKey in fields { for formKey in fields {
guard let formField = fieldMolecules[formKey] else { continue } guard let formField = fieldMolecules[formKey] else { continue }
@ -37,13 +38,16 @@ public class RuleEqualsModel: RulesProtocol {
compareValue = formField.formFieldValue() compareValue = formField.formFieldValue()
continue continue
} }
if compareValue != formField.formFieldValue() { if compareValue != formField.formFieldValue() {
valid = false valid = false
(formField as? FormRuleWatcherFieldProtocol)?.setValidity(valid, rule: self)
break break
} else {
(formField as? FormRuleWatcherFieldProtocol)?.setValidity(valid, rule: self)
} }
} }
return valid return valid
} }
} }

View File

@ -18,6 +18,7 @@ public class RuleRegexModel: RulesProtocol {
public var type: String = RuleRegexModel.identifier public var type: String = RuleRegexModel.identifier
public var fields: [String] public var fields: [String]
public var regex: String public var regex: String
public var errorMessage: [String: String]?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties

View File

@ -16,6 +16,7 @@ public class RuleRequiredModel: RulesProtocol {
public static var identifier: String = "allRequired" public static var identifier: String = "allRequired"
public var type: String = RuleRequiredModel.identifier public var type: String = RuleRequiredModel.identifier
public var errorMessage: [String: String]?
public var fields: [String] public var fields: [String]
//-------------------------------------------------- //--------------------------------------------------

View File

@ -16,7 +16,9 @@ public enum RulesCodingKey: String, CodingKey {
public protocol RulesProtocol: ModelProtocol { public protocol RulesProtocol: ModelProtocol {
// The type of rule // The type of rule
var type: String { get } var type: String { get }
var errorMessage: [String: String]? { get }
// The fields that this rule applies to. // The fields that this rule applies to.
var fields: [String] { get set } var fields: [String] { get set }