diff --git a/MVMCoreUI/Atoms/TextFields/DigitBox.swift b/MVMCoreUI/Atoms/TextFields/DigitBox.swift index f4edd1a1..05e60146 100644 --- a/MVMCoreUI/Atoms/TextFields/DigitBox.swift +++ b/MVMCoreUI/Atoms/TextFields/DigitBox.swift @@ -28,10 +28,10 @@ import UIKit private var previousSize: CGFloat = 0.0 /// Determines if a border should be drawn. - private var hideBorder = false - private var showError = false + var hideBorder = false + var showError = false - private var borderStrokeColor: UIColor = .mfSilver() + var borderStrokeColor: UIColor = .mfSilver() private var borderPath: UIBezierPath = UIBezierPath() //-------------------------------------------------- diff --git a/MVMCoreUI/Atoms/TextFields/DigitEntryField.swift b/MVMCoreUI/Atoms/TextFields/DigitEntryField.swift index cb96a2cb..d7533162 100644 --- a/MVMCoreUI/Atoms/TextFields/DigitEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/DigitEntryField.swift @@ -10,40 +10,35 @@ import UIKit /** * Subclass of TextEntryField due to the conveniences provided. - * TODO: Compare overrides to determine if TextEntryField is necessary. */ @objcMembers open class DigitEntryField: TextEntryField, DigitBoxDelegate { //-------------------------------------------------- // MARK: - Outlets //-------------------------------------------------- - public private(set) var digitFieldsView: UIView? + public private(set) var digitFieldsStack: UIStackView? //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - private var numberOfDigits = 4 + private(set) var numberOfDigits = 4 private var switchedAutomatically = false + public var digitFields: [DigitBox] = [] public override var isEnabled: Bool { didSet { - if isEnabled { - titleLabel.textColor = .black - } else { - titleLabel.textColor = .mfBattleshipGrey() - } + titleLabel.textColor = isEnabled ? .black : .black - for textField in digitFields { - textField.isUserInteractionEnabled = isEnabled - textField.isEnabled = isEnabled - textField.textColor = isEnabled ? .black : .mfBattleshipGrey() + for digitBox in digitFields { + digitBox.isUserInteractionEnabled = isEnabled + digitBox.isEnabled = isEnabled + digitBox.textColor = isEnabled ? .black : .mfBattleshipGrey() } } } - /// Sets placeholder text in the textField. public override var placeholder: String? { get { var string = "" @@ -85,6 +80,7 @@ import UIKit } } + /// Traverses each digitbox to retrieve the held value. public override var text: String? { get { var string = "" @@ -107,34 +103,48 @@ import UIKit } } - public override var title: String? { - get { return titleLabel.text } - set { - if let formText = newValue, !formText.isEmpty { - titleContainerDistance?.constant = 10 - } else { - titleContainerDistance?.constant = 0 + /// Updates the visual appearance of the container, with some logical laterations as well. + public override func updateUI(appearance: Appearance) { + + self.appearance = appearance + isUserInteractionEnabled = true + titleLabel.textColor = .mfBattleshipGrey() + hideBorder = false + feedback = showError ? errorMessage : nil + + switch appearance { + case .original: + digitFields.forEach { + $0.borderStrokeColor = .mfSilver() + $0.bottomBar.backgroundColor = UIColor.black.cgColor } - - super.title = newValue - } - } - - public override var errorMessage: String? { - didSet { - if let errorMessage = errorMessage, !errorMessage.isEmpty { - DispatchQueue.main.async { [weak self] in - guard let self = self else { return } - - if self.showError { - self.feedbackContainerDistance?.constant = 10 - self.setNeedsLayout() - } - - self.digitFields.forEach { $0.showErrorState(true) } - } + case .error: + digitFields.forEach { + $0.borderStrokeColor = .mfPumpkin() + $0.bottomBar.backgroundColor = UIColor.mfPumpkin().cgColor } + self.digitFields.forEach { $0.showErrorState(true) } + case .lock: + digitFields.forEach { + $0.isUserInteractionEnabled = false + $0.hideBorder = true + $0.bottomBar.backgroundColor = UIColor.clear.cgColor + } + case .select: + digitFields.forEach { + $0.borderStrokeColor = .black + $0.bottomBar.backgroundColor = UIColor.black.cgColor + } + case .disable: + digitFields.forEach { + $0.isUserInteractionEnabled = false + $0.borderStrokeColor = .mfSilver() + $0.bottomBar.backgroundColor = self.isEnabled ? UIColor.black.cgColor : UIColor.mfSilver().cgColor + } + titleLabel.textColor = self.isEnabled ? .mfBattleshipGrey() : .mfSilver() } + + refreshBorderUI(bottomBarSize: appearance == .error ? 4 : 1) } //-------------------------------------------------- @@ -158,7 +168,7 @@ import UIKit } public init(numberOfDigits: Int, bothDelegates delegate: (UITextFieldDelegate & TextFieldDelegate)?, size: CGFloat? = nil) { - super.init(bothDelegates: delegate as? (TextFieldDelegate & UITextFieldDelegate)) + super.init(bothDelegates: delegate) setup() self.numberOfDigits = numberOfDigits @@ -176,52 +186,18 @@ import UIKit open func setup() { + // Border for Field Container will not be shown. hideBorder = true - // titleLabel.styleB2(true) alignCenterHorizontal() + isAccessibilityElement = false } open override func setupFieldContainerContent(_ container: UIView) { + textField.removeFromSuperview() setupTextFieldsView(forSize: CGFloat(numberOfDigits)) } - //-------------------------------------------------- - // MARK: - Lifecycle - //-------------------------------------------------- - - open override func updateView(_ size: CGFloat) { - super.updateView(size) - - DispatchQueue.main.async { [weak self] in - guard let self = self else { return } - - self.titleLabel.updateView(size) - - if !self.digitFields.isEmpty { - - StackableViewController.remove(self.digitFields) - - self.digitFields.forEach { $0.updateView(size) } - } - - // Layout text boxes. - self.setupTextFieldsView(forSize: size) - } - } - - //-------------------------------------------------- - // MARK: - Methods - //-------------------------------------------------- - - func removeError() { - DispatchQueue.main.async { [weak self] in - guard let self = self else { return } - - self.digitFields.forEach { $0.showErrorState(false) } - } - } - func createDigitField() -> DigitBox { let textField = DigitBox() @@ -237,7 +213,7 @@ import UIKit // Remove all current UI. if !digitFields.isEmpty { - StackableViewController.remove(digitFields) + digitFieldsStack?.subviews.forEach { $0.removeFromSuperview() } } if numberOfDigits > 0 { @@ -258,6 +234,42 @@ import UIKit accessibilityElements = accessibleElements + [feedbackLabel] } + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + + self.titleLabel.updateView(size) + + if !self.digitFields.isEmpty { + + self.digitFieldsStack?.subviews.forEach { $0.removeFromSuperview() } + self.digitFields.forEach { $0.updateView(size) } + } + + // Layout text boxes. + self.setupTextFieldsView(forSize: size) + } + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + func removeError() { + + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + + self.digitFields.forEach { $0.showErrorState(false) } + } + } + override func valueChanged() { super.valueChanged() @@ -289,27 +301,13 @@ import UIKit func setupTextFieldsView(forSize size: CGFloat) { - guard let space = MFSizeObject(standardSize: 5, smalliPhoneSize: 3)?.getValueBasedOnScreenSize(), - let digitFieldsView = digitFieldsView - else { return } + guard let space = MFSizeObject(standardSize: 5, smalliPhoneSize: 3)?.getValueBasedOnScreenSize() else { return } - StackableViewController.populateViewHorizontally(digitFieldsView, withUIArray: digitFields, withSpacingBlock: { object in - - var inset = UIEdgeInsets(top: 0, left: space, bottom: 0, right: space) - - if self.digitFields.count == 1 { - inset.left = 0 - inset.right = 0 - - } else if let field = object as? UITextField, field == self.digitFields.first { - inset.left = 0 - - } else if let field = object as? UITextField, field == self.digitFields.last { - inset.right = 0 - } - - return inset - }) + let stack = UIStackView(arrangedSubviews: digitFields) + self.digitFieldsStack = stack + textField.addSubview(stack) + stack.distribution = .equalSpacing + stack.spacing = space } public override func defaultValidationBlock() { @@ -367,6 +365,7 @@ import UIKit //-------------------------------------------------- open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) guard let dictionary = json else { return } @@ -382,25 +381,12 @@ import UIKit } buildTextFieldsView(size: MVMCoreUIUtility.getWidth()) - - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) } open override class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 44 } - //-------------------------------------------------- - // MARK: - Accessibility - //-------------------------------------------------- - - open override class func accessibilityElements() -> [Any]? { - // let fields = [] - - // return self.digitFields - return nil - } - //-------------------------------------------------- // MARK: - Text Field Delegate //-------------------------------------------------- diff --git a/MVMCoreUI/Atoms/TextFields/FormEntryField.swift b/MVMCoreUI/Atoms/TextFields/FormEntryField.swift index f3e72664..22d971d8 100644 --- a/MVMCoreUI/Atoms/TextFields/FormEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/FormEntryField.swift @@ -73,7 +73,7 @@ import UIKit return layer }() - public private(set) var appearance: Appearance = .original + public var appearance: Appearance = .original public var showError = false diff --git a/MVMCoreUI/Atoms/Views/CaretView.swift b/MVMCoreUI/Atoms/Views/CaretView.swift index 8d44e343..70056219 100644 --- a/MVMCoreUI/Atoms/Views/CaretView.swift +++ b/MVMCoreUI/Atoms/Views/CaretView.swift @@ -160,6 +160,7 @@ strokeColor = .black } + /// Ensure you have defined a CaretSize with Orientation before calling. @objc public func setConstraints() { guard let dimensions = size?.dimensions() else { return } diff --git a/MVMCoreUI/Molecules/Items/TableViewCell.swift b/MVMCoreUI/Molecules/Items/TableViewCell.swift index 588b0b78..673e5e80 100644 --- a/MVMCoreUI/Molecules/Items/TableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/TableViewCell.swift @@ -23,6 +23,7 @@ import UIKit // For separation between cells. public var topSeparatorView: SeparatorView? public var bottomSeparatorView: SeparatorView? + public enum SeparatorFrequency: String { case all case allExceptTop @@ -40,17 +41,21 @@ import UIKit // MARK: - Styling open func style(with styleString: String?) { - guard let styleString = styleString else { - return - } + + guard let styleString = styleString else { return } + switch styleString { case "standard": styleStandard() + case "header": styleHeader() + case "none": styleNone() - default: break + + default: + break } } @@ -204,13 +209,18 @@ import UIKit // MARK: - Arrow /// Adds the standard mvm style caret to the accessory view @objc public func addCaretViewAccessory() { + guard accessoryView == nil else { return } - let width: CGFloat = 6 - let height: CGFloat = 10 + caretView = CaretView(lineWidth: 1) - caretView?.frame = CGRect(x: 0, y: 0, width: width, height: height) - caretViewWidthSizeObject = MFSizeObject(standardSize: width, standardiPadPortraitSize: 9) - caretViewHeightSizeObject = MFSizeObject(standardSize: height, standardiPadPortraitSize: 16) + caretView?.size = .small(.vertical) + caretView?.setConstraints() + + if let size = caretView?.size?.dimensions() { + caretViewWidthSizeObject = MFSizeObject(standardSize: size.width, standardiPadPortraitSize: 9) + caretViewHeightSizeObject = MFSizeObject(standardSize: size.height, standardiPadPortraitSize: 16) + } + accessoryView = caretView } @@ -258,7 +268,7 @@ import UIKit } else { topSeparatorView?.hide() bottomSeparatorView?.setAsLight() - setSeparatorFrequency(TableViewCell.SeparatorFrequency.allExceptTop, indexPath: indexPath) + setSeparatorFrequency(.allExceptTop, indexPath: indexPath) } }