Benjohn
Benjohn

Reputation: 13887

Make UITextField with VoiceOver read numbers as digits

Our App has a few UITextField where users enter alphanumeric input codes such as "AA149873A".

We'd like voice over to read these as "A A 1 4 9 8 7 2 A", but it instead reads the digits as a number: "One hundred and forty nine thousand, eight hundred and seventy three".

Is there some way to configure UITextField so that it knows its content shouldn't be thought of as numbers, but individual digits?

Thanks.

Upvotes: 3

Views: 3551

Answers (2)

Benjohn
Benjohn

Reputation: 13887

As of iOS 13 it is possible to add a string attribute to direct voice-over vocalisation to spell strings out as individual letters and digits.

I've not found a way to direct UITextField to add this attribute to its content. However, a UITextField subclass can override it's accessibilityValue to achieve this.

The subclass given here adds a property to enable or disable this behaviour.

final class AccessibilityTextField: UITextField {

    var isAlphanumeric: Bool = false

    override public var accessibilityAttributedValue: NSAttributedString? {
        get {
            guard let text = text, !text.isEmpty else {
                return nil
            }

            return NSAttributedString(string: text, attributes: valueAttributes)
        }

        set {
            // Ignore these values.
            _ = newValue
        }
    }

    private var valueAttributes: [NSAttributedString.Key: Any] {
        guard #available(iOS 13.0, *), isAlphanumeric else {
            return [:]
        }

        return [.accessibilitySpeechSpellOut: true]
    }
}

An alternative approach is given in another answer here that doesn't use the iOS 13 feature . accessibilitySpeechSpellOut. However, I've seen it suggested that this is not ideal for brail output systems as they also use accessibilityLabel. Perhaps this is a good fallback on pre iOS 13 systems.

Upvotes: 4

XLE_22
XLE_22

Reputation: 5671

We'd like voice over to read these as "A A 1 4 9 8 7 2 A", but it instead reads the digits as a number: "One hundred and forty nine thousand, eight hundred and seventy three".

The fastest wy to reach your goal is to add spaces between each character in the accessibilityValue of the UITextField as follows: 🤓

class AccessibilityTextField: UITextField {

    var _str: String?
    override var text: String? {
        get { return _str}
        set {
            _str = newValue!
            accessibilityValue = _str!.map{String($0)}.joined(separator: " ")
        }
    }

    convenience init() { self.init() }

    required init?(coder: NSCoder) { super.init(coder: coder) }
}

I didn't implement all the text field delegate stuff to test it ⟹ I created a blank project only with an UITextField adding a "BB0024FG" plain text and changed the text in the viewDidAppear of my view controller:

myTextField.text = "AA14987A"

In the end, VoiceOver spells out each character after another without reading out the initial plain text. 👍

Following this rationale, you have a way that let VoiceOver know that the UITextField content must be thought as individual digits. 😉

Upvotes: 4

Related Questions