Skip to content

Commit 69aa5ac

Browse files
devkaranCTMark Pospesel
andauthored
[CM-1243] Support for reduce motion accessibility. (#15)
* [CM-1243] Support for reduce motion accessibility. * [CM-1243] Comments updated. * Expand unit test coverage * Update dependencies --------- Co-authored-by: Mark Pospesel <[email protected]>
1 parent 320cf01 commit 69aa5ac

File tree

6 files changed

+137
-30
lines changed

6 files changed

+137
-30
lines changed

Package.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version: 5.6
1+
// swift-tools-version: 5.5
22

33
import PackageDescription
44

@@ -17,11 +17,11 @@ let package = Package(
1717
dependencies: [
1818
.package(
1919
url: "https://github.com/yml-org/YCoreUI.git",
20-
from: "1.4.0"
20+
from: "1.5.0"
2121
),
2222
.package(
2323
url: "https://github.com/yml-org/YMatterType.git",
24-
from: "1.4.0"
24+
from: "1.6.0"
2525
)
2626
],
2727
targets: [

Sources/YBottomSheet/Animation/BottomSheetAnimator.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ import UIKit
1212
class BottomSheetAnimator: NSObject {
1313
/// Bottom sheet controller.
1414
let sheetViewController: BottomSheetController
15+
16+
/// Override for isReduceMotionEnabled. Default is `nil`.
17+
///
18+
/// For unit testing. When non-`nil` it will be returned instead of
19+
/// `UIAccessibility.isReduceMotionEnabled`,
20+
var reduceMotionOverride: Bool?
21+
22+
/// Accessibility reduce motion is enabled or not.
23+
var isReduceMotionEnabled: Bool {
24+
reduceMotionOverride ?? UIAccessibility.isReduceMotionEnabled
25+
}
1526

1627
/// Initializes a bottom sheet animator.
1728
/// - Parameter sheetViewController: the sheet being animated.

Sources/YBottomSheet/Animation/BottomSheetDismissAnimator.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ class BottomSheetDismissAnimator: BottomSheetAnimator {
3737
delay: .zero,
3838
options: [.beginFromCurrentState, sheet.appearance.dismissAnimationCurve]
3939
) {
40-
sheet.sheetView.frame = sheetFrame
40+
if self.isReduceMotionEnabled {
41+
sheet.sheetView.alpha = 0
42+
} else {
43+
sheet.sheetView.frame = sheetFrame
44+
}
4145
} completion: { _ in
4246
if !transitionContext.transitionWasCancelled {
4347
fromViewController.view.removeFromSuperview()

Sources/YBottomSheet/Animation/BottomSheetPresentAnimator.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@ class BottomSheetPresentAnimator: BottomSheetAnimator {
2121
sheet.view.layoutSubviews()
2222
sheet.dimmerView.alpha = 0
2323

24-
let toFinalFrame = transitionContext.finalFrame(for: toViewController)
25-
var sheetFrame = sheet.sheetView.frame
26-
sheetFrame.origin.y = toFinalFrame.maxY + (sheet.appearance.elevation?.extent.top ?? 0)
27-
sheet.sheetView.frame = sheetFrame
28-
sheet.view.setNeedsLayout()
24+
if isReduceMotionEnabled {
25+
sheet.sheetView.alpha = 0
26+
} else {
27+
let toFinalFrame = transitionContext.finalFrame(for: toViewController)
28+
var sheetFrame = sheet.sheetView.frame
29+
sheetFrame.origin.y = toFinalFrame.maxY + (sheet.appearance.elevation?.extent.top ?? 0)
30+
sheet.sheetView.frame = sheetFrame
31+
sheet.view.setNeedsLayout()
32+
}
2933

3034
let duration = transitionDuration(using: transitionContext)
3135

@@ -42,7 +46,11 @@ class BottomSheetPresentAnimator: BottomSheetAnimator {
4246
delay: .zero,
4347
options: [.beginFromCurrentState, sheet.appearance.presentAnimationCurve]
4448
) {
45-
sheet.view.layoutIfNeeded()
49+
if self.isReduceMotionEnabled {
50+
sheet.sheetView.alpha = 1
51+
} else {
52+
sheet.view.layoutIfNeeded()
53+
}
4654
} completion: { _ in
4755
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
4856
}

Tests/YBottomSheetTests/Animation/BottomSheetDismissAnimatorTests.swift

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@ import XCTest
1111

1212
final class BottomSheetDismissAnimatorTests: XCTestCase {
1313
func test_animate() throws {
14-
let sheetController = BottomSheetController(
15-
title: "Bottom Sheet",
16-
childView: UIView(),
17-
appearance: BottomSheetController.Appearance(animationDuration: 0.0)
18-
)
14+
let sheetController = makeSheet()
1915
let (sut, context) = try makeSUT(sheetViewController: sheetController, to: sheetController)
2016

2117
XCTAssertTrue(sut is BottomSheetDismissAnimator)
@@ -26,15 +22,44 @@ final class BottomSheetDismissAnimatorTests: XCTestCase {
2622
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
2723
XCTAssertTrue(context.wasCompleteCalled)
2824
XCTAssertTrue(context.didComplete)
25+
}
26+
27+
func test_animateWithoutReduceMotion_TranslatesDown() throws {
28+
let sheetController = makeSheet()
29+
let (sut, context) = try makeSUT(
30+
sheetViewController: sheetController,
31+
to: sheetController,
32+
isReduceMotionEnabled: false
33+
)
34+
35+
sut.animateTransition(using: context)
36+
37+
// Wait for the run loop to tick (animate keyboard)
38+
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
2939
XCTAssertEqual(sheetController.dimmerView.alpha, 0)
40+
XCTAssertEqual(sheetController.sheetView.alpha, 1)
3041
XCTAssertEqual(sheetController.sheetView.frame.minY, context.containerView.bounds.maxY)
3142
}
3243

33-
func test_animateWithoutTo_Fails() throws {
34-
let sheetController = BottomSheetController(
35-
title: "Bottom Sheet",
36-
childView: UIView()
44+
func test_animateWithReduceMotion_FadesOut() throws {
45+
let sheetController = makeSheet()
46+
let (sut, context) = try makeSUT(
47+
sheetViewController: sheetController,
48+
to: sheetController,
49+
isReduceMotionEnabled: true
3750
)
51+
52+
sut.animateTransition(using: context)
53+
54+
// Wait for the run loop to tick (animate keyboard)
55+
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
56+
XCTAssertEqual(sheetController.dimmerView.alpha, 0)
57+
XCTAssertEqual(sheetController.sheetView.alpha, 0)
58+
XCTAssertLessThan(sheetController.sheetView.frame.minY, context.containerView.bounds.maxY)
59+
}
60+
61+
func test_animateWithoutTo_Fails() throws {
62+
let sheetController = makeSheet()
3863
let (sut, context) = try makeSUT(sheetViewController: sheetController, to: nil)
3964

4065
XCTAssertFalse(context.wasCompleteCalled)
@@ -49,14 +74,31 @@ private extension BottomSheetDismissAnimatorTests {
4974
func makeSUT(
5075
sheetViewController: BottomSheetController,
5176
to: UIViewController?,
77+
isReduceMotionEnabled: Bool? = nil,
5278
file: StaticString = #filePath,
5379
line: UInt = #line
5480
) throws -> (UIViewControllerAnimatedTransitioning, MockAnimationContext) {
5581
let main = UIViewController()
56-
let animator = try XCTUnwrap(sheetViewController.animationController(forDismissed: sheetViewController))
82+
let animator = try XCTUnwrap(
83+
sheetViewController.animationController(forDismissed: sheetViewController) as? BottomSheetAnimator
84+
)
85+
animator.reduceMotionOverride = isReduceMotionEnabled
5786
let context = MockAnimationContext(from: main, to: to)
5887
trackForMemoryLeaks(animator, file: file, line: line)
5988
trackForMemoryLeaks(context, file: file, line: line)
6089
return (animator, context)
6190
}
91+
92+
func makeSheet(
93+
file: StaticString = #filePath,
94+
line: UInt = #line
95+
) -> BottomSheetController {
96+
let sheet = BottomSheetController(
97+
title: "Bottom Sheet",
98+
childView: UIView(),
99+
appearance: BottomSheetController.Appearance(animationDuration: 0.0)
100+
)
101+
trackForMemoryLeaks(sheet)
102+
return sheet
103+
}
62104
}

Tests/YBottomSheetTests/Animation/BottomSheetPresentAnimatorTests.swift

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@ import XCTest
1111

1212
final class BottomSheetPresentAnimatorTests: XCTestCase {
1313
func test_animate() throws {
14-
let sheetController = BottomSheetController(
15-
title: "Bottom Sheet",
16-
childView: UIView(),
17-
appearance: BottomSheetController.Appearance(animationDuration: 0.0)
18-
)
14+
let sheetController = makeSheet()
1915
let (sut, context) = try makeSUT(sheetViewController: sheetController, to: sheetController)
2016

2117
XCTAssertTrue(sut is BottomSheetPresentAnimator)
@@ -26,15 +22,42 @@ final class BottomSheetPresentAnimatorTests: XCTestCase {
2622
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
2723
XCTAssertTrue(context.wasCompleteCalled)
2824
XCTAssertTrue(context.didComplete)
25+
}
26+
27+
func test_animateWithoutReduceMotion_TranslatesUp() throws {
28+
let sheetController = makeSheet()
29+
let (sut, context) = try makeSUT(
30+
sheetViewController: sheetController,
31+
to: sheetController,
32+
isReduceMotionEnabled: false
33+
)
34+
35+
sut.animateTransition(using: context)
36+
37+
// Wait for the run loop to tick (animate keyboard)
38+
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
2939
XCTAssertEqual(sheetController.dimmerView.alpha, 1)
3040
XCTAssertLessThan(sheetController.sheetView.frame.minY, context.containerView.bounds.maxY)
3141
}
3242

33-
func test_animateWithoutTo_Fails() throws {
34-
let sheetController = BottomSheetController(
35-
title: "Bottom Sheet",
36-
childView: UIView()
43+
func test_animateWithReduceMotion_FadesIn() throws {
44+
let sheetController = makeSheet()
45+
let (sut, context) = try makeSUT(
46+
sheetViewController: sheetController,
47+
to: sheetController,
48+
isReduceMotionEnabled: true
3749
)
50+
51+
sut.animateTransition(using: context)
52+
53+
// Wait for the run loop to tick (animate keyboard)
54+
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
55+
XCTAssertEqual(sheetController.dimmerView.alpha, 1)
56+
XCTAssertEqual(sheetController.sheetView.alpha, 1)
57+
}
58+
59+
func test_animateWithoutTo_Fails() throws {
60+
let sheetController = makeSheet()
3861
let (sut, context) = try makeSUT(sheetViewController: sheetController, to: nil)
3962

4063
XCTAssertFalse(context.wasCompleteCalled)
@@ -49,16 +72,35 @@ private extension BottomSheetPresentAnimatorTests {
4972
func makeSUT(
5073
sheetViewController: BottomSheetController,
5174
to: UIViewController?,
75+
isReduceMotionEnabled: Bool? = nil,
5276
file: StaticString = #filePath,
5377
line: UInt = #line
5478
) throws -> (UIViewControllerAnimatedTransitioning, MockAnimationContext) {
5579
let main = UIViewController()
5680
let animator = try XCTUnwrap(
57-
sheetViewController.animationController(forPresented: sheetViewController, presenting: main, source: main)
81+
sheetViewController.animationController(
82+
forPresented: sheetViewController,
83+
presenting: main,
84+
source: main
85+
) as? BottomSheetAnimator
5886
)
87+
animator.reduceMotionOverride = isReduceMotionEnabled
5988
let context = MockAnimationContext(from: main, to: to)
6089
trackForMemoryLeaks(animator, file: file, line: line)
6190
trackForMemoryLeaks(context, file: file, line: line)
6291
return (animator, context)
6392
}
93+
94+
func makeSheet(
95+
file: StaticString = #filePath,
96+
line: UInt = #line
97+
) -> BottomSheetController {
98+
let sheet = BottomSheetController(
99+
title: "Bottom Sheet",
100+
childView: UIView(),
101+
appearance: BottomSheetController.Appearance(animationDuration: 0.0)
102+
)
103+
trackForMemoryLeaks(sheet)
104+
return sheet
105+
}
64106
}

0 commit comments

Comments
 (0)