diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 96c5476a..92c110b2 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; }; 0A41BA7F23453A6400D4C0BC /* TextEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */; }; 0A6BF4722360C56C0028F841 /* DropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6BF4712360C56C0028F841 /* DropdownEntryField.swift */; }; + 0A74EB4B236C72C400941B4B /* BoundaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A74EB4A236C72C400941B4B /* BoundaryView.swift */; }; 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */; }; 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; }; 9455B19C234F8A0400A574DB /* MVMAnimationFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */; }; @@ -231,6 +232,7 @@ 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = ""; }; 0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = ""; }; 0A6BF4712360C56C0028F841 /* DropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownEntryField.swift; sourceTree = ""; }; + 0A74EB4A236C72C400941B4B /* BoundaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoundaryView.swift; sourceTree = ""; }; 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButton.swift; sourceTree = ""; }; 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = ""; }; @@ -437,6 +439,14 @@ path = FormUIHelpers; sourceTree = ""; }; + 0A74EB49236C728C00941B4B /* ViewContainer */ = { + isa = PBXGroup; + children = ( + 0A74EB4A236C72C400941B4B /* BoundaryView.swift */, + ); + path = ViewContainer; + sourceTree = ""; + }; D224798823142BF2003FCCF9 /* SwitchMolecules */ = { isa = PBXGroup; children = ( @@ -641,6 +651,7 @@ D29DF11921E68467003B2FB9 /* Containers */ = { isa = PBXGroup; children = ( + 0A74EB49236C728C00941B4B /* ViewContainer */, D29DF2B721E7BE79003B2FB9 /* TabBarController */, D29DF2B621E7BE66003B2FB9 /* SplitViewController */, D206997521FB8A0B00CAE0DE /* MVMCoreUINavigationController.h */, @@ -1071,7 +1082,6 @@ 01004F3022721C3800991ECC /* RadioButton.swift in Sources */, D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */, D29DF11721E6805F003B2FB9 /* UIColor+MFConvenience.m in Sources */, - D29DF25321E6A177003B2FB9 /* MFDigitTextField.m in Sources */, D2B18B7F2360913400A9AEDC /* Control.swift in Sources */, D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */, DBC4392122491730001AB423 /* LabelWithInternalButton.swift in Sources */, @@ -1092,8 +1102,6 @@ D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */, D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */, 0A21DB85235E06EF00C160A2 /* MFTextField.m in Sources */, - B8200E152280C4CF007245F4 /* ProgressBar.swift in Sources */, - D29DF25421E6A177003B2FB9 /* MFMdnTextField.m in Sources */, D282AABA224131D100C46919 /* MFTransparentGIFView.swift in Sources */, D2A514672213885800345BFB /* StandardHeaderView.swift in Sources */, DBEFFA04225A829700230692 /* Label.swift in Sources */, @@ -1123,6 +1131,7 @@ D29DF2BF21E7BEA4003B2FB9 /* MVMCoreUITabBarPageControlViewController.m in Sources */, D29DF28321E7AB24003B2FB9 /* MVMCoreUICommonViewsUtility.m in Sources */, D206997821FB8A0B00CAE0DE /* MVMCoreUINavigationController.m in Sources */, + 0A74EB4B236C72C400941B4B /* BoundaryView.swift in Sources */, D29DF27A21E7A533003B2FB9 /* MVMCoreUISession.m in Sources */, 01DF55E021F8FAA800CC099B /* MFTextFieldListView.swift in Sources */, D2A5146B2214905000345BFB /* ThreeLayerViewController.swift in Sources */, diff --git a/MVMCoreUI/Atoms/TextFields/DigitBox.swift b/MVMCoreUI/Atoms/TextFields/DigitBox.swift index 1f6860ee..a9bb85d0 100644 --- a/MVMCoreUI/Atoms/TextFields/DigitBox.swift +++ b/MVMCoreUI/Atoms/TextFields/DigitBox.swift @@ -13,20 +13,29 @@ import UIKit } @objcMembers open class DigitBox: UITextField, MVMCoreViewProtocol { - //-------------------------------------------------- - // MARK: - Outlets - //-------------------------------------------------- - - private weak var bottomBar: SeparatorView? - //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- + public var bottomBar: CAShapeLayer = { + let layer = CAShapeLayer() + layer.backgroundColor = UIColor.black.cgColor + layer.drawsAsynchronously = true + layer.anchorPoint = CGPoint(x: 0.5, y: 1.0); + return layer + }() + weak var textBoxDelegate: DigitBoxDelegate? private var previousSize: CGFloat = 0.0 + /// Determines if a border should be drawn. + private var hideBorder = false + private var showError = false + + private var borderStrokeColor: UIColor = .mfSilver() + private var borderPath: UIBezierPath = UIBezierPath() + //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- @@ -65,20 +74,50 @@ import UIKit textAlignment = .center keyboardType = .numberPad layer.borderWidth = 1 - hideError() + showError(false) - widthConstraint = widthAnchor.constraint(equalToConstant: 40) + widthConstraint = widthAnchor.constraint(equalToConstant: 39) widthConstraint?.isActive = true - heightConstraint = heightAnchor.constraint(equalToConstant: 60) + heightConstraint = heightAnchor.constraint(equalToConstant: 44) heightConstraint?.isActive = true - bottomBar = SeparatorView.separatorAdd(to: self, position: SeparatorPositionBot) - bottomBar?.setAsMedium() + layer.addSublayer(bottomBar) updateView(MVMCoreUISplitViewController.getDetailViewWidth()) } + open override func draw(_ rect: CGRect) { + super.draw(rect) + + borderPath.removeAllPoints() + + if !hideBorder { + // Brings the other half of the line inside the view to prevent cropping. + let origin = frame.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() + } + + layoutIfNeeded() + } + + open override func layoutSubviews() { + super.layoutSubviews() + + let barHeight: CGFloat = showError ? 4 : 1 + bottomBar.frame = CGRect(x: 0, y: bounds.height - barHeight, width: bounds.width, height: barHeight) + } + //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- @@ -122,16 +161,15 @@ import UIKit } } - func setAsError() { + func showError(_ show: Bool) { - layer.borderColor = UIColor.mfPumpkin().cgColor - bottomBar?.backgroundColor = UIColor.mfPumpkin() - bottomBar?.height?.constant = 4 - } - - func hideError() { + showError = show + borderStrokeColor = show ? .mfPumpkin() : .mfSilver() + bottomBar.backgroundColor = show ? UIColor.mfPumpkin().cgColor : UIColor.black.cgColor + let barHeight: CGFloat = show ? 4 : 1 + bottomBar.frame = CGRect(x: 0, y: bounds.height - barHeight, width: bounds.width, height: barHeight) - layer.borderColor = UIColor.mfSilver().cgColor - bottomBar?.setAsMedium() + setNeedsDisplay() + layoutIfNeeded() } } diff --git a/MVMCoreUI/Atoms/TextFields/DigitEntryField.swift b/MVMCoreUI/Atoms/TextFields/DigitEntryField.swift index 7d9e0edb..34c3afc5 100644 --- a/MVMCoreUI/Atoms/TextFields/DigitEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/DigitEntryField.swift @@ -14,7 +14,7 @@ import UIKit // MARK: - Outlets //-------------------------------------------------- - private weak var digitFieldsView: UIView? + public private(set) var digitFieldsView: UIView? //-------------------------------------------------- // MARK: - Properties @@ -132,7 +132,7 @@ import UIKit guard let self = self else { return } for field in self.digitFields ?? [] { - field.hideError() + field.showError(false) } } } @@ -179,6 +179,16 @@ import UIKit fatalError("DigitEntryField xib has not been implemented") } + //-------------------------------------------------- + // MARK: - Setup + //-------------------------------------------------- + + open func setup() { + + titleLabel.styleB2(true) + alignCenterHorizontal() + } + open override func setupFieldContainerContent(_ container: UIView) { setupTextFieldsView(forSize: CGFloat(numberOfDigits)) @@ -188,13 +198,6 @@ import UIKit // MARK: - Lifecycle //-------------------------------------------------- - open func setup() { - - titleLabel.styleB2(true) - title = "" - alignCenterHorizontal() - } - open override func updateView(_ size: CGFloat) { super.updateView(size) @@ -203,6 +206,7 @@ import UIKit self.titleLabel.updateView(size) + if let digitFields = self.digitFields, !digitFields.isEmpty { // Remove all current UI. diff --git a/MVMCoreUI/Atoms/TextFields/DropdownEntryField.swift b/MVMCoreUI/Atoms/TextFields/DropdownEntryField.swift index 887ff1b3..2f1bcbaa 100644 --- a/MVMCoreUI/Atoms/TextFields/DropdownEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/DropdownEntryField.swift @@ -31,8 +31,6 @@ import UIKit //-------------------------------------------------- public weak var datePicker: UIDatePicker? - public var pickerView: UIPickerView? - //-------------------------------------------------- // MARK: - Properties @@ -72,7 +70,7 @@ import UIKit setupView() MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: bothDelegates) - setBothTextDelegates(bothDelegates) + setBothTextDelegates(to: bothDelegates) } required public init?(coder: NSCoder) { diff --git a/MVMCoreUI/Atoms/TextFields/FormEntryField.swift b/MVMCoreUI/Atoms/TextFields/FormEntryField.swift index 118740bb..ce1ffeba 100644 --- a/MVMCoreUI/Atoms/TextFields/FormEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/FormEntryField.swift @@ -55,7 +55,7 @@ import UIKit return layer }() - var delegateObject: MVMCoreUIDelegateObject? + weak var delegateObject: MVMCoreUIDelegateObject? //-------------------------------------------------- // MARK: - Properties diff --git a/MVMCoreUI/Atoms/TextFields/MdnEntryField.swift b/MVMCoreUI/Atoms/TextFields/MdnEntryField.swift index ae4253a1..06252a76 100644 --- a/MVMCoreUI/Atoms/TextFields/MdnEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/MdnEntryField.swift @@ -20,6 +20,8 @@ import MVMCore public var isNationalMdn = true public var shouldValidateMDN = false +// public var pickerView: UIPickerView? + public var mdn: String? { get { return MVMCoreUIUtility.removeMdnFormat(text) } set { text = MVMCoreUIUtility.formatMdn(newValue) } @@ -82,7 +84,7 @@ import MVMCore let isValid = hasValidMDN() if isValid { - clearError() + clearErrorState() } else { errorMessage = errorMessage ?? MVMCoreUIUtility.hardcodedString(withKey: "textfield_phone_format_error_message") UIAccessibility.post(notification: UIAccessibility.Notification.layoutChanged, argument: textField) @@ -94,7 +96,7 @@ import MVMCore return true } - func getContacts(_ sender: Any?) { + @objc func getContacts(_ sender: Any?) { let picker = CNContactPickerViewController() picker.delegate = self @@ -125,7 +127,6 @@ import MVMCore } text = unformattedMDN - textFieldShouldReturn(textField) textFieldDidEndEditing(textField) } diff --git a/MVMCoreUI/Atoms/TextFields/TextEntryField.swift b/MVMCoreUI/Atoms/TextFields/TextEntryField.swift index 07f4e5be..46664042 100644 --- a/MVMCoreUI/Atoms/TextFields/TextEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/TextEntryField.swift @@ -1,5 +1,5 @@ // -// TextField.swift +// TextEntryField.swift // MVMCoreUI // // Created by Kevin Christiano on 10/2/19. @@ -24,7 +24,7 @@ import UIKit // MARK: - Outlets //-------------------------------------------------- - private(set) var textField: UITextField = { + public private(set) var textField: UITextField = { let textField = UITextField(frame: .zero) textField.translatesAutoresizingMaskIntoConstraints = false textField.setContentCompressionResistancePriority(.required, for: .vertical) @@ -39,10 +39,9 @@ import UIKit // MARK: - Properties //-------------------------------------------------- - public var enabledTextColor: UIColor? - public var disabledTextColor: UIColor? + public var textColor: (enabled: UIColor?, disabled: UIColor?) - public var observingForChanges = false + public var observingForChange = false public override var isEnabled: Bool { didSet { @@ -50,7 +49,7 @@ import UIKit guard let self = self else { return } self.textField.isEnabled = self.isEnabled - self.textField.textColor = self.isEnabled ? self.enabledTextColor : self.disabledTextColor + self.textField.textColor = self.isEnabled ? self.textColor.enabled : self.textColor.disabled } } } @@ -64,6 +63,13 @@ import UIKit } } + public var placeholder: String? { + get { return textField.placeholder } + set { + textField.placeholder = newValue + } + } + public var validationBlock: ((_ value: String?) -> Bool)? { didSet { valueChanged() @@ -83,13 +89,13 @@ import UIKit /// The delegate and block for validation. Validates if the text that the user has entered is valid or not. Checked after each change if there is a delegate. public weak var mfTextFieldDelegate: TextFieldDelegate? { didSet { - if mfTextFieldDelegate != nil && !observingForChanges { - observingForChanges = true + if mfTextFieldDelegate != nil && !observingForChange { + observingForChange = true NotificationCenter.default.addObserver(self, selector: #selector(valueChanged), name: UITextField.textDidChangeNotification, object: textField) NotificationCenter.default.addObserver(self, selector: #selector(endInputing), name: UITextField.textDidEndEditingNotification, object: textField) NotificationCenter.default.addObserver(self, selector: #selector(startEditing), name: UITextField.textDidBeginEditingNotification, object: textField) - } else if mfTextFieldDelegate == nil && observingForChanges { - observingForChanges = false + } else if mfTextFieldDelegate == nil && observingForChange { + observingForChange = false NotificationCenter.default.removeObserver(self, name: UITextField.textDidChangeNotification, object: textField) NotificationCenter.default.removeObserver(self, name: UITextField.textDidEndEditingNotification, object: textField) NotificationCenter.default.removeObserver(self, name: UITextField.textDidBeginEditingNotification, object: textField) @@ -115,7 +121,6 @@ import UIKit public override init(frame: CGRect) { super.init(frame: frame) -// setupView() } public convenience init() { @@ -125,9 +130,7 @@ import UIKit /// - parameter bothDelegates: Sets both MF/UI Text Field Delegates. public init(bothDelegates: (UITextFieldDelegate & TextFieldDelegate)?) { super.init(frame: .zero) - -// setupView() - setBothTextDelegates(bothDelegates) + setBothTextDelegates(to: bothDelegates) } required public init?(coder: NSCoder) { @@ -142,7 +145,6 @@ import UIKit open override func setupFieldContainerContent(_ container: UIView) { MFStyler.styleTextField(textField) - container.addSubview(textField) NSLayoutConstraint.activate([ @@ -159,25 +161,24 @@ import UIKit super.updateView(size) MFStyler.styleTextField(textField) - layoutIfNeeded() } deinit { - setBothTextDelegates(nil) + setBothTextDelegates(to: nil) } //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- - open func clearError() { + open func clearErrorState() { - feedback = nil textField.accessibilityValue = nil + updateUI(appearance: .original) } - public func setBothTextDelegates(_ delegate: (UITextFieldDelegate & TextFieldDelegate)?) { + public func setBothTextDelegates(to delegate: (UITextFieldDelegate & TextFieldDelegate)?) { mfTextFieldDelegate = delegate uiTextFieldDelegate = delegate @@ -215,7 +216,7 @@ import UIKit mfTextFieldDelegate?.isInvalid?(textfield: self) } else if !previousValidity && isValid { - clearError() + clearErrorState() if let mfTextFieldDelegate = mfTextFieldDelegate { mfTextFieldDelegate.isValid?(textfield: self) @@ -226,8 +227,7 @@ import UIKit @objc func endInputing() { if isValid { - clearError() - bottomBar.backgroundColor = UIColor.black.cgColor + clearErrorState() } else if let errMessage = errorMessage { feedback = errMessage @@ -254,11 +254,19 @@ extension TextEntryField { FormValidator.setupValidation(molecule: self, delegate: delegateObject.formValidationProtocol) if let enabledTextColorHex = dictionary["enabledTextColor"] as? String { - enabledTextColor = UIColor.mfGet(forHex: enabledTextColorHex) + textColor.enabled = UIColor.mfGet(forHex: enabledTextColorHex) } if let disabledTextColorHex = dictionary["disabledTextColor"] as? String { - disabledTextColor = UIColor.mfGet(forHex: disabledTextColorHex) + textColor.disabled = UIColor.mfGet(forHex: disabledTextColorHex) + } + + if let text = dictionary[KeyText] as? String { + self.text = text + } + + if let placeholder = dictionary[placeholder] as? String { + self.placeholder = placeholder } switch dictionary.stringForkey(KeyType) { diff --git a/MVMCoreUI/Containers/ViewContainer/BoundaryView.swift b/MVMCoreUI/Containers/ViewContainer/BoundaryView.swift new file mode 100644 index 00000000..0256ddd3 --- /dev/null +++ b/MVMCoreUI/Containers/ViewContainer/BoundaryView.swift @@ -0,0 +1,13 @@ +// +// BoundaryView.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 11/1/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +class BoundaryView: UIView { + +}