Skip to content

Commit

Permalink
Merge pull request #175 from iZettle/text-editor-accessibility-value
Browse files Browse the repository at this point in the history
PLT-2650 Add the Ability to Supply an `accessibilityValue` to `ValueField` Through `TextEditor`
  • Loading branch information
Alasdair Baxter authored Dec 3, 2021
2 parents ba925c4 + 94fdeba commit da0094f
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 3.3.0

- Added (backwards compatible) support for supplying an `accessibilityValue` to `ValueField` through `TextEditor`.

# 3.2.0

- Xcode 13.0 compatibility
Expand Down
19 changes: 19 additions & 0 deletions Form/TextEditor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ public protocol TextEditor {
/// The current formatted text of value and the index into text where insertions happen, useful for placing cursors etc.
var textAndInsertionIndex: (text: String, index: String.Index) { get }

/// The current accessibility value of the edited text.
///
/// Depending on the kind of value being edited, and the UI surrounding it,
/// it might be desirable to have a slightly different string representation
/// exposed to an assistive technology. Defaults to the value of `text`.
var accessibilityValue: String { get }

mutating func insertCharacter(_ char: Character)

mutating func deleteBackward()
Expand All @@ -39,6 +46,10 @@ public extension TextEditor {
return textAndInsertionIndex.text
}

var accessibilityValue: String {
return text
}

var insertionIndex: String.Index {
return textAndInsertionIndex.index
}
Expand Down Expand Up @@ -84,6 +95,10 @@ public class AnyTextEditor<Value>: TextEditor {
set { fatalError() }
}

public var accessibilityValue: String {
fatalError()
}

public var defaultValue: Value {
fatalError()
}
Expand Down Expand Up @@ -172,6 +187,10 @@ private final class _AnyTextEditor<Editor: TextEditor>: AnyTextEditor<Editor.Val
set { editor.value = newValue }
}

public override var accessibilityValue: String {
editor.accessibilityValue
}

public override var shouldResetOnInsertion: Bool {
get { return editor.shouldResetOnInsertion }
set { editor.shouldResetOnInsertion = newValue }
Expand Down
19 changes: 18 additions & 1 deletion Form/ValueEditor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import UIKit
public struct ValueEditor<Value>: TextEditor {
private let isValidCharacter: ((Character) -> Bool)
private let valueToText: (Value) -> String
private let valueToAccessibilityValue: (Value) -> String
private let _textAndInsertionIndex: (Value) -> (String, String.Index)
private let textToValue: (String) -> Value?
private let minCharacters: Int
Expand All @@ -25,23 +26,39 @@ public struct ValueEditor<Value>: TextEditor {
/// Parameters:
/// - defaultValue: The value to use when resetting the editor.
/// - valueToText: How to convert a `Value` to the editable text representation part of the value.
/// - valueToAccessibilityValue: How to convert a `Value` into a string representation for the editor's
/// `accessibilityValue`. Defaults to the text returned by `textAndInsertionIndex`.
/// - textToValue: How to convert the editable text representation part of value back to a `Value`.
/// - isValidCharacter: Whether a character is a valid input to build a value.
/// - minCharacters: Min characters of the editable text representation of value, defaults to zero
/// - maxCharacters: Max characters of the editable text representation of value, defaults to `.max`
/// - textAndInsertionIndex: Format a value for display (adding potential prefix, postfix or other formatting)
/// and the index for insertions, useful for placing cursors etc.
public init(value: Value, defaultValue: Value, valueToText: @escaping (Value) -> String, textToValue: @escaping (String) -> Value?, isValidCharacter: @escaping ((Character) -> Bool), minCharacters: Int = 0, maxCharacters: Int = .max, textAndInsertionIndex: @escaping (Value) -> (String, String.Index)) {
public init(
value: Value,
defaultValue: Value,
valueToText: @escaping (Value) -> String,
valueToAccessibilityValue: ((Value) -> String)? = nil,
textToValue: @escaping (String) -> Value?,
isValidCharacter: @escaping ((Character) -> Bool),
minCharacters: Int = 0, maxCharacters: Int = .max,
textAndInsertionIndex: @escaping (Value) -> (String, String.Index)
) {
self.value = value
self.defaultValue = defaultValue
self.valueToText = valueToText
self.valueToAccessibilityValue = valueToAccessibilityValue ?? { textAndInsertionIndex($0).0 }
self.textToValue = textToValue
_textAndInsertionIndex = textAndInsertionIndex
self.isValidCharacter = isValidCharacter
self.minCharacters = minCharacters
self.maxCharacters = maxCharacters
}

public var accessibilityValue: String {
valueToAccessibilityValue(value)
}

public var textAndInsertionIndex: (text: String, index: String.Index) {
return _textAndInsertionIndex(value)
}
Expand Down
8 changes: 4 additions & 4 deletions Form/ValueField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,11 @@ public final class ValueField<Value>: UIControl, UIKeyInput {
}

public override var accessibilityValue: String? {
get { return label.text }
set {
guard let text = newValue else { return }
label.value = text
get {
editor.accessibilityValue
}
//swiftlint:disable:next unused_setter_value
set { /* accessibilityValue is always read from the editor. */ }
}

public override var inputView: UIView? {
Expand Down
18 changes: 18 additions & 0 deletions FormTests/ValueEditorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,22 @@ class ValueEditorTests: XCTestCase {
test(editor, "1234567890", "12345", 0)
test(editor, "123456R3", "3", 0)
}

func testComputesCorrectAccessibilityValue() {
let defaultEditor = ValueEditor<String>(value: "test")
let customEditor = ValueEditor(
value: "test",
defaultValue: "",
valueToText: { $0 },
valueToAccessibilityValue: { $0 + " modified" },
textToValue: { $0 },
isValidCharacter: { _ in true },
minCharacters: 0,
maxCharacters: .max,
textAndInsertionIndex: { ($0, $0.endIndex) }
)

XCTAssertEqual(defaultEditor.accessibilityValue, defaultEditor.text)
XCTAssertEqual(customEditor.accessibilityValue, "test modified")
}
}
19 changes: 19 additions & 0 deletions FormTests/ValueFieldTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,23 @@ class ValueFieldTests: XCTestCase {

wait(for: [fieldBecameFirstResponder], timeout: 1)
}

func testUsesEditorAccessibilityValue() {
let expectedAccessibilityValue = "accessibility value"
let editor = ValueEditor<String>(
value: "",
defaultValue: "",
valueToText: { $0 },
valueToAccessibilityValue: { _ in expectedAccessibilityValue },
textToValue: { $0 },
isValidCharacter: { _ in true },
minCharacters: 0,
maxCharacters: .max,
textAndInsertionIndex: { ($0, $0.endIndex) }
)

let field = ValueField(value: "", editor: editor)

XCTAssertEqual(field.accessibilityValue, expectedAccessibilityValue)
}
}
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"repositoryURL": "https://github.com/izettle/Flow.git",
"state": {
"branch": null,
"revision": "0563b5bc4b3a5a4869f9d7ff498a8de5031e3dc5",
"version": "1.8.3"
"revision": "3bc56e13c29cfbcc77cdae6193bbad697ee95799",
"version": "1.10.0"
}
}
]
Expand Down

0 comments on commit da0094f

Please sign in to comment.