Skip to content

Commit 55f75e3

Browse files
authored
a11ySnapshot (#91)
1 parent d6673e1 commit 55f75e3

File tree

7 files changed

+87
-9
lines changed

7 files changed

+87
-9
lines changed

DemoApp/DemoApp/TestViews/PreviewVariants.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,15 @@ extension NamedViewModifier {
6464
static var xxlTextSize: NamedViewModifier {
6565
.init(name: "XXL Text Size", value: { $0.dynamicTypeSize(.xxxLarge) })
6666
}
67+
68+
static var accessibility: NamedViewModifier {
69+
.init(name: "Accessibility", value: { $0.emergeAccessibility(true) })
70+
}
6771
}
6872

6973
extension [NamedViewModifier] {
7074
/// The default named view modifiers in a ``PreviewVariants``.
7175
static var previewDefault: [NamedViewModifier] {
72-
[.unmodified, .darkMode, .xxlTextSize]
76+
[.unmodified, .darkMode, .xxlTextSize, .accessibility]
7377
}
7478
}

DemoApp/DemoApp/TestViews/RideShareButton.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,14 @@ struct RideShareButtonView_Previews: PreviewProvider {
4545
.preferredColorScheme(.dark)
4646
.previewLayout(.sizeThatFits)
4747
.padding()
48+
49+
RideShareButtonView(title: "Request Ride") {
50+
print("Button tapped")
51+
}
52+
.previewLayout(.sizeThatFits)
53+
.padding()
54+
.previewDisplayName("Ride Share Button View - Light")
55+
.emergeRenderingMode(.coreAnimation)
56+
.emergeAccessibility(true)
4857
}
4958
}

Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,15 @@ let package = Package(
3434
],
3535
dependencies: [
3636
.package(url: "https://github.com/swhitty/FlyingFox.git", exact: "0.12.2"),
37+
.package(url: "https://github.com/EmergeTools/AccessibilitySnapshot.git", exact: "1.0.0"),
3738
],
3839
targets: [
3940
// Targets are the basic building blocks of a package, defining a module or a test suite.
4041
// Targets can depend on other targets in this package and products from dependencies.
4142
// Target that provides the XCTest
4243
.target(name: "SnapshottingTests"),
4344
// Core functionality
44-
.target(name: "SnapshotPreviewsCore", dependencies: ["PreviewsSupport"]),
45+
.target(name: "SnapshotPreviewsCore", dependencies: ["PreviewsSupport", .product(name: "AccessibilitySnapshotCore", package: "AccessibilitySnapshot")]),
4546
.target(name: "SnapshotPreferences", dependencies: ["SnapshotPreviewsCore"]),
4647
// Inserted dylib
4748
.target(name: "Snapshotting", dependencies: ["SnapshottingSwift"]),
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//
2+
// AccessibiltyPreference.swift
3+
//
4+
//
5+
// Created by Noah Martin on 2/2/24.
6+
//
7+
8+
import Foundation
9+
import SwiftUI
10+
11+
struct AccessibilityPreferenceKey: PreferenceKey {
12+
static func reduce(value: inout Bool?, nextValue: () -> Bool?) {
13+
if value == nil {
14+
value = nextValue()
15+
}
16+
}
17+
18+
static var defaultValue: Bool? = nil
19+
}
20+
21+
extension View {
22+
public func emergeAccessibility(_ enabled: Bool?) -> some View {
23+
preference(key: AccessibilityPreferenceKey.self, value: enabled)
24+
}
25+
}

Sources/SnapshotPreferences/EmergeModifierFinder.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ class EmergeModifierState: NSObject {
2323
expansionPreference = nil
2424
renderingMode = nil
2525
precision = nil
26+
accessibilityEnabled = nil
2627
}
2728

2829
var expansionPreference: Bool?
2930
var renderingMode: EmergeRenderingMode.RawValue?
3031
var precision: Float?
32+
var accessibilityEnabled: Bool?
3133
}
3234

3335
@objc(EmergeModifierFinder)
@@ -44,5 +46,8 @@ class EmergeModifierFinder: NSObject {
4446
.onPreferenceChange(PrecisionPreferenceKey.self, perform: { value in
4547
EmergeModifierState.shared.precision = value
4648
})
49+
.onPreferenceChange(AccessibilityPreferenceKey.self, perform: { value in
50+
EmergeModifierState.shared.accessibilityEnabled = value
51+
})
4752
}
4853
}

Sources/SnapshotPreviewsCore/ExpandingViewController.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public final class ExpandingViewController: UIHostingController<AnyView> {
5353

5454
private var heightAnchor: NSLayoutConstraint?
5555

56-
var expansionSettled: ((EmergeRenderingMode?, Float?) -> Void)?
56+
var expansionSettled: ((EmergeRenderingMode?, Float?, Bool?) -> Void)?
5757

5858
init<Content: View>(rootView: Content, layout: PreviewLayout) {
5959
let newView = finder?(rootView)
@@ -81,7 +81,8 @@ public final class ExpandingViewController: UIHostingController<AnyView> {
8181
didCall = true
8282
let renderingMode = stateMirror?.descendant("renderingMode") as? EmergeRenderingMode.RawValue
8383
let emergeRenderingMode = renderingMode != nil ? EmergeRenderingMode(rawValue: renderingMode!) : nil
84-
expansionSettled?(emergeRenderingMode, stateMirror?.descendant("precision") as? Float)
84+
let accessibilityEnabled = stateMirror?.descendant("accessibilityEnabled") as? Bool
85+
expansionSettled?(emergeRenderingMode, stateMirror?.descendant("precision") as? Float, accessibilityEnabled)
8586
}
8687

8788
public override func viewDidLayoutSubviews() {

Sources/SnapshotPreviewsCore/View+Snapshot.swift

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import Foundation
1010
import SwiftUI
1111
import UIKit
12+
import AccessibilitySnapshotCore
1213

1314
public enum RenderingError: Error {
1415
case failedRendering(CGSize)
@@ -35,16 +36,38 @@ extension View {
3536

3637
let (windowRootVC, containerVC) = Self.setupRootVC(subVC: controller)
3738
window.rootViewController = windowRootVC
38-
controller.expansionSettled = { [weak containerVC, weak controller] renderingMode, precision in
39+
controller.expansionSettled = { [weak containerVC, weak controller] renderingMode, precision, accessibilityEnabled in
3940
guard let containerVC, let controller else { return }
4041

4142
if async {
4243
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
43-
completion(Self.takeSnapshot(layout: layout, renderingMode: renderingMode, rootVC: containerVC, controller: controller), precision)
44+
completion(Self.takeSnapshot(layout: layout, renderingMode: renderingMode, rootVC: containerVC, targetView: controller.view), precision)
4445
}
4546
} else {
4647
DispatchQueue.main.async {
47-
completion(Self.takeSnapshot(layout: layout, renderingMode: renderingMode, rootVC: containerVC, controller: controller), precision)
48+
if let accessibilityEnabled, accessibilityEnabled {
49+
let containedView: UIView
50+
switch layout {
51+
case .device:
52+
containedView = containerVC.view
53+
default:
54+
containedView = view
55+
}
56+
let a11yView = AccessibilitySnapshotView(
57+
containedView: containedView,
58+
viewRenderingMode: renderingMode?.a11yRenderingMode ?? .drawHierarchyInRect,
59+
activationPointDisplayMode: .never,
60+
showUserInputLabels: true)
61+
62+
a11yView.center = window.center
63+
window.addSubview(a11yView)
64+
65+
try? a11yView.parseAccessibility(useMonochromeSnapshot: false)
66+
a11yView.sizeToFit()
67+
completion(Self.takeSnapshot(layout: .sizeThatFits, renderingMode: renderingMode, rootVC: containerVC, targetView: a11yView), precision)
68+
} else {
69+
completion(Self.takeSnapshot(layout: layout, renderingMode: renderingMode, rootVC: containerVC, targetView: view), precision)
70+
}
4871
}
4972
}
5073
}
@@ -82,9 +105,9 @@ extension View {
82105
layout: PreviewLayout,
83106
renderingMode: EmergeRenderingMode?,
84107
rootVC: UIViewController,
85-
controller: UIViewController) -> Result<UIImage, RenderingError>
108+
targetView: UIView) -> Result<UIImage, RenderingError>
86109
{
87-
let view = controller.view!
110+
let view = targetView
88111
let drawCode: (CGContext) -> Void
89112

90113
CATransaction.commit()
@@ -142,6 +165,16 @@ extension UIView {
142165
}
143166
}
144167

168+
extension EmergeRenderingMode {
169+
var a11yRenderingMode: AccessibilitySnapshotView.ViewRenderingMode {
170+
switch self {
171+
case .coreAnimation:
172+
return .renderLayerInContext
173+
case .uiView:
174+
return .drawHierarchyInRect
175+
}
176+
}
177+
}
145178

146179
extension CALayer {
147180

0 commit comments

Comments
 (0)