-
Notifications
You must be signed in to change notification settings - Fork 47
/
Copy pathAccessibilityContainer.swift
80 lines (69 loc) · 2.88 KB
/
AccessibilityContainer.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import BlueprintUI
import UIKit
/// Acts as an accessibility container for any accessible subviews.
///
/// Accessible subviews are found using the following algorithm:
///
/// Recurse subviews until a view is found that either
/// - has`isAccessibilityElement` set to `true` or
/// - returns a non-nil value from `accessibilityElements` (i.e., is a container itself)
///
/// If an accessibility element is found, we add it to the `accessibilityElements`
/// and terminate the search down that branch. If a container is found,
/// the elements returned from the container are added to the `accessibilityElements`
/// and the search down that branch is also terminated.
public struct AccessibilityContainer: Element {
/// An optional `accessibilityIdentifier` to give the container. Defaults to `nil`.
public var identifier: String?
public var wrapped: Element
public var accessibilityElements: [Any]?
/// Creates a new `AccessibilityContainer` wrapping the provided element.
public init(identifier: String? = nil, accessibilityElements: [Any]? = nil, wrapping element: Element) {
self.identifier = identifier
self.accessibilityElements = accessibilityElements
wrapped = element
}
//
// MARK: Element
//
public var content: ElementContent {
ElementContent(child: wrapped)
}
public func backingViewDescription(with context: ViewDescriptionContext) -> ViewDescription? {
AccessibilityContainerView.describe { config in
config[\.accessibilityIdentifier] = identifier
config[\.explicitAccessibilityElements] = self.accessibilityElements
}
}
}
extension Element {
/// Acts as an accessibility container for any subviews
/// where `isAccessibilityElement == true`.
public func accessibilityContainer(identifier: String? = nil, accessibilityElements: [Any]? = nil) -> Element {
AccessibilityContainer(identifier: identifier, accessibilityElements: accessibilityElements, wrapping: self)
}
}
extension AccessibilityContainer {
private final class AccessibilityContainerView: UIView {
var explicitAccessibilityElements: [Any]?
override var accessibilityElements: [Any]? {
get { explicitAccessibilityElements ?? recursiveAccessibleSubviews() }
set { fatalError("This property is not settable") }
}
}
}
extension UIView {
func recursiveAccessibleSubviews() -> [Any] {
subviews.flatMap { subview -> [Any] in
if subview.accessibilityElementsHidden || subview.isHidden {
return []
} else if let accessibilityElements = subview.accessibilityElements {
return accessibilityElements
} else if subview.isAccessibilityElement {
return [subview]
} else {
return subview.recursiveAccessibleSubviews()
}
}
}
}