Skip to content

Commit

Permalink
Adding AXCustomContent support
Browse files Browse the repository at this point in the history
  • Loading branch information
RoyalPineapple committed Jan 23, 2024
1 parent c6b18fb commit d0387e9
Show file tree
Hide file tree
Showing 20 changed files with 422 additions and 6 deletions.
4 changes: 4 additions & 0 deletions Example/AccessibilitySnapshot.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
66E2CD14CD63946657E17B15 /* Pods_SnapshotTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A3192D7B9B16BD10FB517A2 /* Pods_SnapshotTests.framework */; };
83A295842AC22D9D00DFBE4F /* UserInputLabelsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83A295832AC22D9D00DFBE4F /* UserInputLabelsViewController.swift */; };
83A295862AC22EEE00DFBE4F /* UserInputLabelsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83A295852AC22EEE00DFBE4F /* UserInputLabelsTests.swift */; };
AC725B842B06D07E009AD59B /* AccessibilityCustomContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC725B832B06D07E009AD59B /* AccessibilityCustomContentViewController.swift */; };
C47F6C5316FB0C043BEB59F3 /* Pods_AccessibilitySnapshotDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A886964D2E787399E137105 /* Pods_AccessibilitySnapshotDemo.framework */; };
D38F6F4E508A3D067D677F69 /* Pods_UnitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BFCB4FD6BC17AB232B26E72 /* Pods_UnitTests.framework */; };
/* End PBXBuildFile section */
Expand Down Expand Up @@ -139,6 +140,7 @@
83A295832AC22D9D00DFBE4F /* UserInputLabelsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInputLabelsViewController.swift; sourceTree = "<group>"; };
83A295852AC22EEE00DFBE4F /* UserInputLabelsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInputLabelsTests.swift; sourceTree = "<group>"; };
88C33CBF672C290CE1EE86AF /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
AC725B832B06D07E009AD59B /* AccessibilityCustomContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityCustomContentViewController.swift; sourceTree = "<group>"; };
C78F90CE7A2A315AADF80144 /* Pods-SnapshotTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SnapshotTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SnapshotTests/Pods-SnapshotTests.debug.xcconfig"; sourceTree = "<group>"; };
CCFF2A604706B71DC0CBD38B /* Pods-AccessibilitySnapshotDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AccessibilitySnapshotDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-AccessibilitySnapshotDemo/Pods-AccessibilitySnapshotDemo.release.xcconfig"; sourceTree = "<group>"; };
DBA3D7413A13111BA7DE4750 /* Pods-UnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.release.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -198,6 +200,7 @@
3DBAC2902242F9B200EF4D0A /* LandmarkContainerViewController.swift */,
3D3F2E132263E6B900F7608E /* InvertColorsViewController.swift */,
3DDE7FF524C6D6BF00999ABA /* AccessibilityCustomActionsViewController.swift */,
AC725B832B06D07E009AD59B /* AccessibilityCustomContentViewController.swift */,
);
name = "Accessibility Screens";
sourceTree = "<group>";
Expand Down Expand Up @@ -625,6 +628,7 @@
3DF46502220D7F9B0048D446 /* ElementOrderViewController.swift in Sources */,
3DDE7FF624C6D6BF00999ABA /* AccessibilityCustomActionsViewController.swift in Sources */,
3DBAC28F2242E7C700EF4D0A /* ListContainerViewController.swift in Sources */,
AC725B842B06D07E009AD59B /* AccessibilityCustomContentViewController.swift in Sources */,
3DBEAA5B2222953E00FAE61D /* SwitchControlViewController.swift in Sources */,
3DBAC2912242F9B200EF4D0A /* LandmarkContainerViewController.swift in Sources */,
3D39BFAC223314C1009C3EF4 /* TabBarViewController.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//
// Copyright 2020 Square Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Paralayout
import UIKit
import Accessibility

@available(iOS 14.0, *)
final class AccessibilityCustomContentViewController: AccessibilityViewController {

// MARK: - UIViewController

override func loadView() {
view = View(
views: [
.init(includeLabel: true, includeHint: true),
.init(includeLabel: true, includeHint: false),
.init(includeLabel: false, includeHint: true),
.init(includeLabel: false, includeHint: false),
]
)
}

}

// MARK: -
@available(iOS 14.0, *)
private extension AccessibilityCustomContentViewController {

final class View: UIView {

// MARK: - Life Cycle

init(views: [CustomContentView], frame: CGRect = .zero) {
self.views = views

super.init(frame: frame)

views.forEach(addSubview)
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: - Private Properties

private let views: [CustomContentView]

// MARK: - UIView

override func layoutSubviews() {
views.forEach { $0.bounds.size = .init(width: bounds.width / 2, height: 50) }

let statusBarHeight = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0

var distributionSpecifiers: [ViewDistributionSpecifying] = [ statusBarHeight.fixed, 1.flexible ]
for subview in views {
distributionSpecifiers.append(subview)
distributionSpecifiers.append(1.flexible)
}
applyVerticalSubviewDistribution(distributionSpecifiers)
}

}

}

// MARK: -
@available(iOS 14.0, *)
private extension AccessibilityCustomContentViewController {

// Swift 5.4 ships with Xcode 12.5 and the iOS 14.5 SDK.
#if swift(>=5.4)
final class CustomContentView: UIView, AXCustomContentProvider {
// MARK: - Life Cycle

init(includeLabel: Bool, includeHint: Bool) {
super.init(frame: .zero)

backgroundColor = .gray

isAccessibilityElement = true

accessibilityLabel = includeLabel ? "Label" : nil
accessibilityHint = includeHint ? "Hint" : nil
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: - UIAccessibility
var accessibilityCustomContent: [AXCustomContent]! = {
let customContent = AXCustomContent(label: "Custom Content Label", value: "Custom Content Value")

let highImportance = AXCustomContent(label: "High Importance Label", value: "High Importance Value")
highImportance.importance = .high

return [customContent, highImportance]
}()
}

#else
final class CustomContentView: UIView {
// This shouldnt ever get called because of the @available(iOS 14.0, *)
init(includeLabel: Bool, includeHint: Bool) {
super.init(frame: .zero)

backgroundColor = .gray

isAccessibilityElement = true

accessibilityLabel = "Accessibility Custom Content not avilable in this SDK"
accessibilityHint = "Please use iOS 14 or later for Custom Content"
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#endif
}
7 changes: 7 additions & 0 deletions Example/AccessibilitySnapshot/RootViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ final class RootViewController: UITableViewController {
("Accessibility Paths", { _ in return AccessibilityPathViewController() }),
("Accessibility Activation Point", { _ in return ActivationPointViewController() }),
("Accessibility Custom Actions", { _ in return AccessibilityCustomActionsViewController() }),
("Accessibility Custom Content", {
if #available(iOS 14.0, *) {
return AccessibilityCustomContentViewController()
} else {
return $0
} }),
("Data Table", { presentingViewController in
return DataTableViewController.makeConfigurationSelectionViewController(
presentingViewController: presentingViewController
Expand All @@ -62,6 +68,7 @@ final class RootViewController: UITableViewController {
("Invert Colors", { _ in return InvertColorsViewController() }),
("User Input Labels", { _ in return UserInputLabelsViewController() }),
]


super.init(nibName: nil, bundle: nil)
}
Expand Down
23 changes: 21 additions & 2 deletions Example/AccessibilitySnapshot/SwiftUIView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,27 @@ struct SwiftUIView: View {
.accessibility(label: Text("Label"))
.accessibility(value: Text("Value"))
.accessibility(hint: Text("Hint"))

Spacer()

if #available(iOS 14.0, *) {
// View with label, value, hint, and Custom Actions.
Circle()
.accessibility(label: Text("Label"))
.accessibility(value: Text("Value"))
.accessibility(hint: Text("Hint"))
.accessibilityAction(named: "Custom") {}
}
// Swift 5.5 ships with Xcode 13 and the iOS 15 SDK.
#if swift(>=5.5)
if #available(iOS 15.0, *) {
// View with label, value, hint, and Custom Content.
Circle()
.accessibility(label: Text("Label"))
.accessibility(value: Text("Value"))
.accessibility(hint: Text("Hint"))
.accessibilityCustomContent("Key", "Value")
.accessibilityCustomContent("Important Key", "Important Value", importance: .high)
}
#endif
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions Example/SnapshotTests/AccessibilityPropertiesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,21 @@ final class AccessibilitySnapshotTests: SnapshotTestCase {
customActionsViewController.view.frame = UIScreen.main.bounds
SnapshotVerifyAccessibility(customActionsViewController.view)
}

func testCustomContent() {
// Swift 5.4 ships with Xcode 12.5 and the iOS 14.5 SDK.
#if swift(>=5.4)
@available(iOS 14.0, *)
func iosFourteenOnly() {
let customContentViewController = AccessibilityCustomContentViewController()
customContentViewController.view.frame = UIScreen.main.bounds
SnapshotVerifyAccessibility(customContentViewController.view)
}
if #available(iOS 14.0, *) {
iosFourteenOnly()
}
#endif
}

func testLargeView() throws {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 1400, height: 1400))
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified ...shotTests.SwiftUISnapshotTests/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified ...shotTests.SwiftUISnapshotTests/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified ...shotTests.SwiftUISnapshotTests/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified ...mages/_64/SnapshotTests.SwiftUISnapshotTests/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//

import UIKit
import Accessibility

public struct AccessibilityMarker {

Expand Down Expand Up @@ -57,6 +58,9 @@ public struct AccessibilityMarker {

/// The names of the custom actions supported by the element.
public var customActions: [String]

/// The names of the custom content included by the element.
public var customContent: [(label:String, value: String, isImportant:Bool)]

/// The language code of the language used to localize strings in the description.
public var accessibilityLanguage: String?
Expand Down Expand Up @@ -211,6 +215,7 @@ public final class AccessibilityHierarchyParser {
activationPoint: root.convert(activationPoint, from: nil),
usesDefaultActivationPoint: (activationPoint == defaultActivationPoint(for: element.object)),
customActions: element.object.accessibilityCustomActions?.map { $0.name } ?? [],
customContent: element.object.customContent,
accessibilityLanguage: element.object.accessibilityLanguage
)
}
Expand Down Expand Up @@ -700,5 +705,44 @@ extension UIView {
newPath.apply(transform)
return newPath
}



}


extension NSObject {
var customContent: [(label: String, value: String, isImportant:Bool)] {

// Compile time checks require diferentiation by swift version rather than SDK.
// See https://swiftversion.net/ for mapping swift version to Xcode versions.

// Swift 5.4 ships with Xcode 12.5 and the iOS 14.5 SDK.
#if swift(>=5.4)
if #available(iOS 14.0, *) {
if let provider = self as? AXCustomContentProvider {

// Swift 5.9 ships with Xcode 15 and the iOS 17 SDK.
#if swift(>=5.9)
if #available(iOS 17.0, *) {
if let customContentBlock = provider.accessibilityCustomContentBlock {
if let content = customContentBlock?() {
return content.map { content in
return (content.label, content.value, content.importance == .high)
}
}
}
}
#endif
if let content = provider.accessibilityCustomContent {
return content.map { content in
return (content.label, content.value, content.importance == .high)
}
}
}
}
#endif

return []
}
}
Loading

0 comments on commit d0387e9

Please sign in to comment.