diff --git a/VDS/Components/DatePicker/DatePicker.swift b/VDS/Components/DatePicker/DatePicker.swift index a61b70a5..fdc6e05f 100644 --- a/VDS/Components/DatePicker/DatePicker.swift +++ b/VDS/Components/DatePicker/DatePicker.swift @@ -101,15 +101,11 @@ open class DatePicker: EntryFieldBase, DatePickerViewControllerDelegate, UIPopov open override func setup() { super.setup() - fieldStackView.isAccessibilityElement = true - fieldStackView.accessibilityLabel = "Date Picker" - fieldStackView.accessibilityHint = "Double Tap to open" - // setting color config selectedDateLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() // tap gesture - fieldStackView + containerView .publisher(for: UITapGestureRecognizer()) .sink { [weak self] _ in guard let self else { return } @@ -144,14 +140,7 @@ open class DatePicker: EntryFieldBase, DatePickerViewControllerDelegate, UIPopov selectedDateLabel.isEnabled = isEnabled calendarIcon.color = iconColorConfiguration.getColor(self) } - - open override func updateAccessibility() { - super.updateAccessibility() - fieldStackView.accessibilityLabel = "Date Picker, \(accessibilityLabelText)" - fieldStackView.accessibilityHint = isReadOnly || !isEnabled ? "" : "Double tap to open." - fieldStackView.accessibilityValue = value - } - + /// Resets to default settings. open override func reset() { super.reset() @@ -184,7 +173,7 @@ open class DatePicker: EntryFieldBase, DatePickerViewControllerDelegate, UIPopov controller.dismiss(animated: true) { [weak self] in guard let self else { return } self.sendActions(for: .valueChanged) - UIAccessibility.post(notification: .layoutChanged, argument: self.fieldStackView) + UIAccessibility.post(notification: .layoutChanged, argument: self.containerView) } } diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index b06eac6a..1496f392 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -66,6 +66,8 @@ open class DropdownSelect: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + internal override var responder: UIResponder? { dropdownField } + internal var minWidthDefault = 66.0 internal var minWidthInlineLabel = 102.0 internal override var minWidth: CGFloat { showInlineLabel ? minWidthInlineLabel : minWidthDefault } @@ -131,7 +133,6 @@ open class DropdownSelect: EntryFieldBase { open override func setup() { super.setup() - fieldStackView.isAccessibilityElement = true inlineDisplayLabel.isAccessibilityElement = true dropdownField.width(0) @@ -276,57 +277,11 @@ open class DropdownSelect: EntryFieldBase { statusIcon.color = iconColorConfiguration.getColor(self) } - open override func updateAccessibility() { - super.updateAccessibility() - fieldStackView.accessibilityLabel = "Dropdown Select, \(accessibilityLabelText)" - fieldStackView.accessibilityHint = isReadOnly || !isEnabled ? "" : "has popup, Double tap to open." - fieldStackView.accessibilityValue = value - } - - open override var accessibilityElements: [Any]? { - get { - var elements = [Any]() - elements.append(contentsOf: [titleLabel, fieldStackView]) - - if showError { - elements.append(statusIcon) - if let errorText, !errorText.isEmpty { - elements.append(errorLabel) - } - } - - if let helperText, !helperText.isEmpty { - elements.append(helperLabel) - } - - return elements - } - - set { super.accessibilityElements = newValue } - } - - @objc open func pickerDoneClicked() { optionsPicker.isHidden = true dropdownField.resignFirstResponder() setNeedsUpdate() - UIAccessibility.post(notification: .layoutChanged, argument: fieldStackView) - } - - open override var canBecomeFirstResponder: Bool { - return dropdownField.canBecomeFirstResponder - } - - open override func becomeFirstResponder() -> Bool { - return dropdownField.becomeFirstResponder() - } - - open override var canResignFirstResponder: Bool { - return dropdownField.canResignFirstResponder - } - - open override func resignFirstResponder() -> Bool { - return dropdownField.resignFirstResponder() + UIAccessibility.post(notification: .layoutChanged, argument: containerView) } } @@ -337,8 +292,8 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource { internal func launchPicker() { if optionsPicker.isHidden { - UIAccessibility.post(notification: .layoutChanged, argument: optionsPicker) dropdownField.becomeFirstResponder() + UIAccessibility.post(notification: .layoutChanged, argument: optionsPicker) } else { dropdownField.resignFirstResponder() } diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 2111a7f0..4e7422ee 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -40,6 +40,8 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + internal var responder: UIResponder? { return nil } + internal let mainStackView = UIStackView().with { $0.axis = .vertical $0.alignment = .fill @@ -95,6 +97,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { internal var containerView: UIView = { return UIView().with { $0.translatesAutoresizingMaskIntoConstraints = false + $0.isAccessibilityElement = true } }() @@ -243,7 +246,8 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { open var accessibilityLabelText: String { var accessibilityLabels = [String]() - if let text = titleLabel.text { + + if let text = titleLabel.text?.trimmingCharacters(in: .whitespaces) { accessibilityLabels.append(text) } if isReadOnly { @@ -255,9 +259,14 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { if let errorText, showError { accessibilityLabels.append("error, \(errorText)") } + + accessibilityLabels.append("\(Self.self)") + return accessibilityLabels.joined(separator: ", ") } + open var accessibilityHintText: String = "Double tap to open" + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -360,6 +369,22 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { isReadOnly = false onChange = nil } + + open override var canBecomeFirstResponder: Bool { + responder?.canBecomeFirstResponder ?? super.canBecomeFirstResponder + } + + open override func becomeFirstResponder() -> Bool { + responder?.becomeFirstResponder() ?? super.becomeFirstResponder() + } + + open override var canResignFirstResponder: Bool { + responder?.canResignFirstResponder ?? super.canResignFirstResponder + } + + open override func resignFirstResponder() -> Bool { + responder?.resignFirstResponder() ?? super.resignFirstResponder() + } //-------------------------------------------------- // MARK: - Public Methods @@ -447,6 +472,35 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { } } + open override func updateAccessibility() { + super.updateAccessibility() + containerView.accessibilityLabel = accessibilityLabelText + containerView.accessibilityHint = isReadOnly || !isEnabled ? "" : accessibilityHintText + containerView.accessibilityValue = value + } + + open override var accessibilityElements: [Any]? { + get { + var elements = [Any]() + elements.append(contentsOf: [titleLabel, containerView]) + + if showError { + elements.append(statusIcon) + if let errorText, !errorText.isEmpty { + elements.append(errorLabel) + } + } + + if let helperText, !helperText.isEmpty { + elements.append(helperLabel) + } + + return elements + } + + set { super.accessibilityElements = newValue } + } + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index 3764c0c3..c2f779d4 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -34,6 +34,8 @@ open class InputField: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + internal override var responder: UIResponder? { textField } + internal override var containerBackgroundColor: UIColor { if showSuccess { return backgroundColorConfiguration.getColor(self) @@ -102,6 +104,7 @@ open class InputField: EntryFieldBase { open var textField = TextField().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.textStyle = TextStyle.bodyLarge + $0.isAccessibilityElement = false } /// Color configuration for the textField. @@ -181,6 +184,8 @@ open class InputField: EntryFieldBase { /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() + accessibilityHintText = "Double tap to edit" + textField.heightAnchor.constraint(equalToConstant: 20).isActive = true textField.delegate = self bottomContainerStackView.insertArrangedSubview(successLabel, at: 0) @@ -227,13 +232,7 @@ open class InputField: EntryFieldBase { textField.isEnabled = isEnabled textField.isUserInteractionEnabled = isEnabled && !isReadOnly } - - open override func updateAccessibility() { - super.updateAccessibility() - textField.accessibilityLabel = accessibilityLabelText - textField.accessibilityHint = isReadOnly || !isEnabled ? "" : "Double tap to open." - } - + open override func updateErrorLabel() { super.updateErrorLabel() @@ -264,7 +263,7 @@ open class InputField: EntryFieldBase { open override var accessibilityElements: [Any]? { get { var elements = [Any]() - elements.append(contentsOf: [titleLabel, textField]) + elements.append(contentsOf: [titleLabel, containerView]) if showError { elements.append(statusIcon) if let errorText, !errorText.isEmpty { @@ -283,22 +282,6 @@ open class InputField: EntryFieldBase { set { super.accessibilityElements = newValue } } - - open override var canBecomeFirstResponder: Bool { - return textField.canBecomeFirstResponder - } - - open override func becomeFirstResponder() -> Bool { - return textField.becomeFirstResponder() - } - - open override var canResignFirstResponder: Bool { - return textField.canResignFirstResponder - } - - open override func resignFirstResponder() -> Bool { - return textField.resignFirstResponder() - } } extension InputField: UITextFieldDelegate { @@ -311,6 +294,7 @@ extension InputField: UITextFieldDelegate { public func textFieldDidEndEditing(_ textField: UITextField) { fieldType.handler().textFieldDidEndEditing(self, textField: textField) validate() + UIAccessibility.post(notification: .layoutChanged, argument: self.containerView) } public func textFieldDidChangeSelection(_ textField: UITextField) { diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index b1c244e5..a487dc53 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -32,6 +32,8 @@ open class TextArea: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + internal override var responder: UIResponder? { textView } + internal var textViewHeightConstraint: NSLayoutConstraint? internal var inputFieldStackView: UIStackView = { @@ -42,14 +44,14 @@ open class TextArea: EntryFieldBase { $0.spacing = VDSLayout.space3X } }() - + open var characterCounterLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.textStyle = .bodySmall $0.textAlignment = .right $0.numberOfLines = 1 } - + open var minHeight: Height = .twoX { didSet { setNeedsUpdate() } } //-------------------------------------------------- @@ -101,13 +103,15 @@ open class TextArea: EntryFieldBase { open override var value: String? { return textView.text } - + /// UITextView shown in the TextArea. open var textView = TextView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.sizeToFit() - $0.isScrollEnabled = false + $0.isAccessibilityElement = false + $0.isScrollEnabled = true $0.textContainerInset = .zero + $0.autocorrectionType = .no $0.textContainer.lineFragmentPadding = 0 } @@ -137,10 +141,8 @@ open class TextArea: EntryFieldBase { /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() - fieldStackView.pinToSuperView(.uniform(VDSFormControls.spaceInset)) - - textView.isScrollEnabled = true - textView.autocorrectionType = .no + + accessibilityHintText = "Double tap to edit" //events textView @@ -159,6 +161,7 @@ open class TextArea: EntryFieldBase { .publisher(for: .editingDidEnd) .sink { [weak self] _ in self?.validate() + UIAccessibility.post(notification: .layoutChanged, argument: self?.containerView) }.store(in: &subscribers) textViewHeightConstraint = textView.heightAnchor.constraint(greaterThanOrEqualToConstant: containerSize.height) @@ -192,13 +195,7 @@ open class TextArea: EntryFieldBase { characterCounterLabel.surface = surface highlightCharacterOverflow() } - - open override func updateAccessibility() { - super.updateAccessibility() - textView.accessibilityLabel = accessibilityLabelText - textView.accessibilityHint = isReadOnly || !isEnabled ? "" : "Double tap to open." - } - + override func updateRules() { super.updateRules() @@ -222,46 +219,7 @@ open class TextArea: EntryFieldBase { stackView.addArrangedSubview(characterCounterLabel) return stackView } - - open override var accessibilityElements: [Any]? { - get { - var elements = [Any]() - elements.append(contentsOf: [titleLabel, textView]) - - if showError { - elements.append(statusIcon) - if let errorText, !errorText.isEmpty { - elements.append(errorLabel) - } - } - - if let helperText, !helperText.isEmpty { - elements.append(helperLabel) - } - - return elements - } - - set { super.accessibilityElements = newValue } - } - - - open override var canBecomeFirstResponder: Bool { - return textView.canBecomeFirstResponder - } - - open override func becomeFirstResponder() -> Bool { - return textView.becomeFirstResponder() - } - - open override var canResignFirstResponder: Bool { - return textView.canResignFirstResponder - } - - open override func resignFirstResponder() -> Bool { - return textView.resignFirstResponder() - } - + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- diff --git a/VDS/SupportingFiles/Icons.xcassets/CreditCard/discover.imageset/Contents.json b/VDS/SupportingFiles/Icons.xcassets/CreditCard/discover.imageset/Contents.json index 28cfbca3..d8a92d8d 100644 --- a/VDS/SupportingFiles/Icons.xcassets/CreditCard/discover.imageset/Contents.json +++ b/VDS/SupportingFiles/Icons.xcassets/CreditCard/discover.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "discover.svg", + "filename" : "Discover-02.svg", "idiom" : "universal" } ], diff --git a/VDS/SupportingFiles/Icons.xcassets/CreditCard/discover.imageset/Discover-02.svg b/VDS/SupportingFiles/Icons.xcassets/CreditCard/discover.imageset/Discover-02.svg new file mode 100644 index 00000000..ea75afb4 --- /dev/null +++ b/VDS/SupportingFiles/Icons.xcassets/CreditCard/discover.imageset/Discover-02.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/VDS/SupportingFiles/Icons.xcassets/CreditCard/discover.imageset/discover.svg b/VDS/SupportingFiles/Icons.xcassets/CreditCard/discover.imageset/discover.svg deleted file mode 100644 index a056ab58..00000000 --- a/VDS/SupportingFiles/Icons.xcassets/CreditCard/discover.imageset/discover.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/VDS/SupportingFiles/Icons.xcassets/CreditCard/jcb.imageset/Contents.json b/VDS/SupportingFiles/Icons.xcassets/CreditCard/jcb.imageset/Contents.json index 8a4c248a..c4aba6fe 100644 --- a/VDS/SupportingFiles/Icons.xcassets/CreditCard/jcb.imageset/Contents.json +++ b/VDS/SupportingFiles/Icons.xcassets/CreditCard/jcb.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "jcb.svg", + "filename" : "jcb-emblem-logo.svg", "idiom" : "universal" } ], diff --git a/VDS/SupportingFiles/Icons.xcassets/CreditCard/jcb.imageset/jcb-emblem-logo.svg b/VDS/SupportingFiles/Icons.xcassets/CreditCard/jcb.imageset/jcb-emblem-logo.svg new file mode 100644 index 00000000..4b8f6dd9 --- /dev/null +++ b/VDS/SupportingFiles/Icons.xcassets/CreditCard/jcb.imageset/jcb-emblem-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/VDS/SupportingFiles/Icons.xcassets/CreditCard/jcb.imageset/jcb.svg b/VDS/SupportingFiles/Icons.xcassets/CreditCard/jcb.imageset/jcb.svg deleted file mode 100644 index ab197f4b..00000000 --- a/VDS/SupportingFiles/Icons.xcassets/CreditCard/jcb.imageset/jcb.svg +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -