Merge branch 'mbruce/bugfix' into 'develop'
CXTDT-584278 – InputField - Accessibility See merge request BPHV_MIPS/vds_ios!270
This commit is contained in:
commit
71c5e444f4
@ -93,13 +93,16 @@ open class Icon: View {
|
|||||||
backgroundColor = .clear
|
backgroundColor = .clear
|
||||||
|
|
||||||
isAccessibilityElement = true
|
isAccessibilityElement = true
|
||||||
accessibilityTraits = .image
|
accessibilityTraits = .none
|
||||||
|
accessibilityHint = "image"
|
||||||
|
|
||||||
bridge_accessibilityLabelBlock = { [weak self] in
|
bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
guard let self else { return "" }
|
guard let self else { return "" }
|
||||||
return name?.rawValue ?? "icon"
|
return name?.rawValue ?? "icon"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
|
|||||||
@ -92,12 +92,6 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
/// This is the view that will be wrapped with the border for userInteraction.
|
|
||||||
/// The only subview of this view is the fieldStackView
|
|
||||||
internal var containerView = View().with {
|
|
||||||
$0.isAccessibilityElement = true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is set by a local method.
|
/// This is set by a local method.
|
||||||
internal var bottomContainerView: UIView!
|
internal var bottomContainerView: UIView!
|
||||||
|
|
||||||
@ -163,6 +157,12 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Public Properties
|
// MARK: - Public Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
/// This is the view that will be wrapped with the border for userInteraction.
|
||||||
|
/// The only subview of this view is the fieldStackView
|
||||||
|
open var containerView = View().with {
|
||||||
|
$0.isAccessibilityElement = true
|
||||||
|
}
|
||||||
|
|
||||||
open var onChangeSubscriber: AnyCancellable?
|
open var onChangeSubscriber: AnyCancellable?
|
||||||
|
|
||||||
open var titleLabel = Label().with {
|
open var titleLabel = Label().with {
|
||||||
@ -186,6 +186,8 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
$0.isAccessibilityElement = true
|
$0.isAccessibilityElement = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open var useRequiredRule: Bool = true { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
open var labelText: String? { didSet { setNeedsUpdate() } }
|
open var labelText: String? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
open var helperText: String? { didSet { setNeedsUpdate() } }
|
open var helperText: String? { didSet { setNeedsUpdate() } }
|
||||||
@ -453,27 +455,34 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open func updateErrorLabel(){
|
open func updateErrorLabel(){
|
||||||
if showError, let errorText {
|
|
||||||
errorLabel.text = errorText
|
/// always show the errorIcon if there is an error
|
||||||
errorLabel.surface = surface
|
if showError || hasInternalError {
|
||||||
errorLabel.isEnabled = isEnabled
|
|
||||||
errorLabel.isHidden = false
|
|
||||||
statusIcon.name = .error
|
|
||||||
statusIcon.surface = surface
|
|
||||||
statusIcon.isHidden = !isEnabled || state.contains(.focused)
|
|
||||||
} else if hasInternalError, let internalErrorText {
|
|
||||||
errorLabel.text = internalErrorText
|
|
||||||
errorLabel.surface = surface
|
|
||||||
errorLabel.isEnabled = isEnabled
|
|
||||||
errorLabel.isHidden = false
|
|
||||||
statusIcon.name = .error
|
statusIcon.name = .error
|
||||||
statusIcon.surface = surface
|
statusIcon.surface = surface
|
||||||
statusIcon.isHidden = !isEnabled || state.contains(.focused)
|
statusIcon.isHidden = !isEnabled || state.contains(.focused)
|
||||||
} else {
|
} else {
|
||||||
statusIcon.isHidden = true
|
statusIcon.isHidden = true
|
||||||
errorLabel.isHidden = true
|
|
||||||
}
|
}
|
||||||
statusIcon.color = iconColorConfiguration.getColor(self)
|
statusIcon.color = iconColorConfiguration.getColor(self)
|
||||||
|
|
||||||
|
// only show errorLabel if there is a message
|
||||||
|
var message: String?
|
||||||
|
if showError, let errorText {
|
||||||
|
message = errorText
|
||||||
|
} else if hasInternalError, let internalErrorText {
|
||||||
|
message = internalErrorText
|
||||||
|
}
|
||||||
|
|
||||||
|
if let message {
|
||||||
|
errorLabel.text = message
|
||||||
|
errorLabel.surface = surface
|
||||||
|
errorLabel.isEnabled = isEnabled
|
||||||
|
errorLabel.isHidden = false
|
||||||
|
} else {
|
||||||
|
errorLabel.isHidden = true
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open func updateHelperLabel(){
|
open func updateHelperLabel(){
|
||||||
@ -515,7 +524,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
internal func updateRules() {
|
internal func updateRules() {
|
||||||
rules.removeAll()
|
rules.removeAll()
|
||||||
if self.isRequired {
|
if isRequired && useRequiredRule {
|
||||||
let rule = RequiredRule()
|
let rule = RequiredRule()
|
||||||
if let errorText, !errorText.isEmpty {
|
if let errorText, !errorText.isEmpty {
|
||||||
rule.errorMessage = errorText
|
rule.errorMessage = errorText
|
||||||
|
|||||||
@ -67,7 +67,14 @@ extension InputField {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func formatUSNumber(_ number: String) -> String {
|
override func textFieldDidEndEditing(_ inputField: InputField, textField: UITextField) {
|
||||||
|
if let text = inputField.text {
|
||||||
|
let rawNumber = text.filter { $0.isNumber }
|
||||||
|
textField.text = formatUSNumber(rawNumber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatUSNumber(_ number: String) -> String {
|
||||||
// Format the number in the style XXX-XXX-XXXX
|
// Format the number in the style XXX-XXX-XXXX
|
||||||
let areaCodeLength = 3
|
let areaCodeLength = 3
|
||||||
let centralOfficeCodeLength = 3
|
let centralOfficeCodeLength = 3
|
||||||
|
|||||||
@ -107,6 +107,9 @@ open class InputField: EntryFieldBase {
|
|||||||
$0.isAccessibilityElement = false
|
$0.isAccessibilityElement = false
|
||||||
$0.autocorrectionType = .no
|
$0.autocorrectionType = .no
|
||||||
$0.spellCheckingType = .no
|
$0.spellCheckingType = .no
|
||||||
|
$0.smartQuotesType = .no
|
||||||
|
$0.smartDashesType = .no
|
||||||
|
$0.smartInsertDeleteType = .no
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Color configuration for the textField.
|
/// Color configuration for the textField.
|
||||||
@ -184,6 +187,8 @@ open class InputField: EntryFieldBase {
|
|||||||
super.setup()
|
super.setup()
|
||||||
accessibilityHintText = "Double tap to edit"
|
accessibilityHintText = "Double tap to edit"
|
||||||
|
|
||||||
|
actionTextLink.accessibilityTraits = .button
|
||||||
|
|
||||||
textField.heightAnchor.constraint(equalToConstant: 20).isActive = true
|
textField.heightAnchor.constraint(equalToConstant: 20).isActive = true
|
||||||
textField.delegate = self
|
textField.delegate = self
|
||||||
bottomContainerStackView.insertArrangedSubview(successLabel, at: 0)
|
bottomContainerStackView.insertArrangedSubview(successLabel, at: 0)
|
||||||
@ -207,11 +212,11 @@ open class InputField: EntryFieldBase {
|
|||||||
accessibilityLabels.append(text)
|
accessibilityLabels.append(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let formatText = textField.formatText, !formatText.isEmpty {
|
if let formatText = textField.formatText, !formatText.isEmpty, textField.text.isEmpty {
|
||||||
accessibilityLabels.append("format, \(formatText)")
|
accessibilityLabels.append("format, \(formatText)")
|
||||||
}
|
}
|
||||||
|
|
||||||
if let placeholderText = textField.placeholder, !placeholderText.isEmpty {
|
if let placeholderText = textField.placeholder, !placeholderText.isEmpty, textField.text.isEmpty {
|
||||||
accessibilityLabels.append("placeholder, \(placeholderText)")
|
accessibilityLabels.append("placeholder, \(placeholderText)")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,6 +251,11 @@ open class InputField: EntryFieldBase {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
containerView.bridge_accessibilityValueBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return textField.isSecureTextEntry ? "\(textField.text.count) stars" : value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func getFieldContainer() -> UIView {
|
open override func getFieldContainer() -> UIView {
|
||||||
@ -340,19 +350,19 @@ open class InputField: EntryFieldBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension InputField: UITextFieldDelegate {
|
extension InputField: UITextFieldDelegate {
|
||||||
public func textFieldDidBeginEditing(_ textField: UITextField) {
|
open func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||||
fieldType.handler().textFieldDidBeginEditing(self, textField: textField)
|
fieldType.handler().textFieldDidBeginEditing(self, textField: textField)
|
||||||
updateContainerView()
|
updateContainerView()
|
||||||
updateErrorLabel()
|
updateErrorLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func textFieldDidEndEditing(_ textField: UITextField) {
|
open func textFieldDidEndEditing(_ textField: UITextField) {
|
||||||
fieldType.handler().textFieldDidEndEditing(self, textField: textField)
|
fieldType.handler().textFieldDidEndEditing(self, textField: textField)
|
||||||
validate()
|
validate()
|
||||||
UIAccessibility.post(notification: .layoutChanged, argument: self.containerView)
|
UIAccessibility.post(notification: .layoutChanged, argument: self.containerView)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func textFieldDidChangeSelection(_ textField: UITextField) {
|
open func textFieldDidChangeSelection(_ textField: UITextField) {
|
||||||
fieldType.handler().textFieldDidChangeSelection(self, textField: textField)
|
fieldType.handler().textFieldDidChangeSelection(self, textField: textField)
|
||||||
if fieldType.handler().validateOnChange {
|
if fieldType.handler().validateOnChange {
|
||||||
validate()
|
validate()
|
||||||
@ -361,7 +371,7 @@ extension InputField: UITextFieldDelegate {
|
|||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
open func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||||
let shouldChange = fieldType.handler().textField(self, textField: textField, shouldChangeCharactersIn: range, replacementString: string)
|
let shouldChange = fieldType.handler().textField(self, textField: textField, shouldChangeCharactersIn: range, replacementString: string)
|
||||||
if shouldChange {
|
if shouldChange {
|
||||||
UIAccessibility.post(notification: .announcement, argument: string)
|
UIAccessibility.post(notification: .announcement, argument: string)
|
||||||
|
|||||||
@ -47,6 +47,11 @@ open class TextField: UITextField, ViewProtocol, Errorable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
/// Set to true to hide the blinking textField cursor.
|
||||||
|
open var hideBlinkingCaret = false
|
||||||
|
open var enableClipboardActions: Bool = true
|
||||||
|
open var onDidDeleteBackwards: (() -> Void)?
|
||||||
|
|
||||||
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
||||||
open var shouldUpdateView: Bool = true
|
open var shouldUpdateView: Bool = true
|
||||||
|
|
||||||
@ -209,6 +214,23 @@ open class TextField: UITextField, ViewProtocol, Errorable {
|
|||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func caretRect(for position: UITextPosition) -> CGRect {
|
||||||
|
|
||||||
|
if hideBlinkingCaret {
|
||||||
|
return .zero
|
||||||
|
}
|
||||||
|
|
||||||
|
let caretRect = super.caretRect(for: position)
|
||||||
|
return CGRect(origin: caretRect.origin, size: CGSize(width: 1, height: caretRect.height))
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func deleteBackward() {
|
||||||
|
super.deleteBackward()
|
||||||
|
onDidDeleteBackwards?()
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { enableClipboardActions }
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Methods
|
// MARK: - Private Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -111,7 +111,9 @@ open class TextArea: EntryFieldBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
didSet {
|
didSet {
|
||||||
validate()
|
if textView.isFirstResponder {
|
||||||
|
validate()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -45,6 +45,15 @@ open class TextView: UITextView, ViewProtocol, Errorable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
open var placeholder: String? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
open var placeholderLabel = Label().with {
|
||||||
|
$0.textColorConfiguration = ViewColorConfiguration().with {
|
||||||
|
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true)
|
||||||
|
$0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: false)
|
||||||
|
}.eraseToAnyColorable()
|
||||||
|
}
|
||||||
|
|
||||||
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
||||||
open var shouldUpdateView: Bool = true
|
open var shouldUpdateView: Bool = true
|
||||||
|
|
||||||
@ -88,6 +97,7 @@ open class TextView: UITextView, ViewProtocol, Errorable {
|
|||||||
if textAlignment != oldValue {
|
if textAlignment != oldValue {
|
||||||
// Text alignment can be part of our paragraph style, so we may need to
|
// Text alignment can be part of our paragraph style, so we may need to
|
||||||
// re-style when changed
|
// re-style when changed
|
||||||
|
placeholderLabel.textAlignment = textAlignment
|
||||||
updateLabel()
|
updateLabel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,6 +128,9 @@ open class TextView: UITextView, ViewProtocol, Errorable {
|
|||||||
done.pinCenterY()
|
done.pinCenterY()
|
||||||
.pinTrailing(16)
|
.pinTrailing(16)
|
||||||
inputAccessoryView = accessView
|
inputAccessoryView = accessView
|
||||||
|
|
||||||
|
addSubview(placeholderLabel)
|
||||||
|
placeholderLabel.pinToSuperView()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func doneButtonAction() {
|
@objc func doneButtonAction() {
|
||||||
@ -145,6 +158,10 @@ open class TextView: UITextView, ViewProtocol, Errorable {
|
|||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
placeholderLabel.preferredMaxLayoutWidth = textContainer.size.width - textContainer.lineFragmentPadding * 2
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Accessibility
|
// MARK: - Accessibility
|
||||||
@ -297,6 +314,10 @@ open class TextView: UITextView, ViewProtocol, Errorable {
|
|||||||
} else {
|
} else {
|
||||||
attributedText = nil
|
attributedText = nil
|
||||||
}
|
}
|
||||||
|
placeholderLabel.textStyle = textStyle
|
||||||
|
placeholderLabel.surface = surface
|
||||||
|
placeholderLabel.text = placeholder
|
||||||
|
placeholderLabel.isHidden = !text.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
1.0.71
|
1.0.71
|
||||||
----------------
|
----------------
|
||||||
- CXTDT-581803 - DatePicker - Calendar does not switch to Dark Mode
|
- CXTDT-581803 - DatePicker - Calendar does not switch to Dark Mode
|
||||||
|
- CXTDT-584278 – InputField - Accessibility
|
||||||
|
|
||||||
1.0.70
|
1.0.70
|
||||||
----------------
|
----------------
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user