Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CM-1243] Support for reduce motion accessibility. #15

Merged
merged 4 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 5.6
// swift-tools-version: 5.5

import PackageDescription

Expand All @@ -17,11 +17,11 @@ let package = Package(
dependencies: [
.package(
url: "https://github.com/yml-org/YCoreUI.git",
from: "1.4.0"
from: "1.5.0"
),
.package(
url: "https://github.com/yml-org/YMatterType.git",
from: "1.4.0"
from: "1.6.0"
)
],
targets: [
Expand Down
11 changes: 11 additions & 0 deletions Sources/YBottomSheet/Animation/BottomSheetAnimator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ import UIKit
class BottomSheetAnimator: NSObject {
/// Bottom sheet controller.
let sheetViewController: BottomSheetController

/// Override for isReduceMotionEnabled. Default is `nil`.
///
/// For unit testing. When non-`nil` it will be returned instead of
/// `UIAccessibility.isReduceMotionEnabled`,
var reduceMotionOverride: Bool?

/// Accessibility reduce motion is enabled or not.
var isReduceMotionEnabled: Bool {
reduceMotionOverride ?? UIAccessibility.isReduceMotionEnabled
}

/// Initializes a bottom sheet animator.
/// - Parameter sheetViewController: the sheet being animated.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ class BottomSheetDismissAnimator: BottomSheetAnimator {
delay: .zero,
options: [.beginFromCurrentState, sheet.appearance.dismissAnimationCurve]
) {
sheet.sheetView.frame = sheetFrame
if self.isReduceMotionEnabled {
sheet.sheetView.alpha = 0
} else {
sheet.sheetView.frame = sheetFrame
}
mpospese marked this conversation as resolved.
Show resolved Hide resolved
} completion: { _ in
if !transitionContext.transitionWasCancelled {
fromViewController.view.removeFromSuperview()
Expand Down
20 changes: 14 additions & 6 deletions Sources/YBottomSheet/Animation/BottomSheetPresentAnimator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ class BottomSheetPresentAnimator: BottomSheetAnimator {
sheet.view.layoutSubviews()
sheet.dimmerView.alpha = 0

let toFinalFrame = transitionContext.finalFrame(for: toViewController)
var sheetFrame = sheet.sheetView.frame
sheetFrame.origin.y = toFinalFrame.maxY + (sheet.appearance.elevation?.extent.top ?? 0)
sheet.sheetView.frame = sheetFrame
sheet.view.setNeedsLayout()
if isReduceMotionEnabled {
sheet.sheetView.alpha = 0
} else {
let toFinalFrame = transitionContext.finalFrame(for: toViewController)
var sheetFrame = sheet.sheetView.frame
sheetFrame.origin.y = toFinalFrame.maxY + (sheet.appearance.elevation?.extent.top ?? 0)
sheet.sheetView.frame = sheetFrame
sheet.view.setNeedsLayout()
}
mpospese marked this conversation as resolved.
Show resolved Hide resolved

let duration = transitionDuration(using: transitionContext)

Expand All @@ -42,7 +46,11 @@ class BottomSheetPresentAnimator: BottomSheetAnimator {
delay: .zero,
options: [.beginFromCurrentState, sheet.appearance.presentAnimationCurve]
) {
sheet.view.layoutIfNeeded()
if self.isReduceMotionEnabled {
sheet.sheetView.alpha = 1
} else {
sheet.view.layoutIfNeeded()
}
} completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@ import XCTest

final class BottomSheetDismissAnimatorTests: XCTestCase {
func test_animate() throws {
let sheetController = BottomSheetController(
title: "Bottom Sheet",
childView: UIView(),
appearance: BottomSheetController.Appearance(animationDuration: 0.0)
)
let sheetController = makeSheet()
let (sut, context) = try makeSUT(sheetViewController: sheetController, to: sheetController)

XCTAssertTrue(sut is BottomSheetDismissAnimator)
Expand All @@ -26,15 +22,44 @@ final class BottomSheetDismissAnimatorTests: XCTestCase {
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
XCTAssertTrue(context.wasCompleteCalled)
XCTAssertTrue(context.didComplete)
}

func test_animateWithoutReduceMotion_TranslatesDown() throws {
let sheetController = makeSheet()
let (sut, context) = try makeSUT(
sheetViewController: sheetController,
to: sheetController,
isReduceMotionEnabled: false
)

sut.animateTransition(using: context)

// Wait for the run loop to tick (animate keyboard)
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
XCTAssertEqual(sheetController.dimmerView.alpha, 0)
XCTAssertEqual(sheetController.sheetView.alpha, 1)
XCTAssertEqual(sheetController.sheetView.frame.minY, context.containerView.bounds.maxY)
}

func test_animateWithoutTo_Fails() throws {
let sheetController = BottomSheetController(
title: "Bottom Sheet",
childView: UIView()
func test_animateWithReduceMotion_FadesOut() throws {
let sheetController = makeSheet()
let (sut, context) = try makeSUT(
sheetViewController: sheetController,
to: sheetController,
isReduceMotionEnabled: true
)

sut.animateTransition(using: context)

// Wait for the run loop to tick (animate keyboard)
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
XCTAssertEqual(sheetController.dimmerView.alpha, 0)
XCTAssertEqual(sheetController.sheetView.alpha, 0)
XCTAssertLessThan(sheetController.sheetView.frame.minY, context.containerView.bounds.maxY)
}

func test_animateWithoutTo_Fails() throws {
let sheetController = makeSheet()
let (sut, context) = try makeSUT(sheetViewController: sheetController, to: nil)

XCTAssertFalse(context.wasCompleteCalled)
Expand All @@ -49,14 +74,31 @@ private extension BottomSheetDismissAnimatorTests {
func makeSUT(
sheetViewController: BottomSheetController,
to: UIViewController?,
isReduceMotionEnabled: Bool? = nil,
file: StaticString = #filePath,
line: UInt = #line
) throws -> (UIViewControllerAnimatedTransitioning, MockAnimationContext) {
let main = UIViewController()
let animator = try XCTUnwrap(sheetViewController.animationController(forDismissed: sheetViewController))
let animator = try XCTUnwrap(
sheetViewController.animationController(forDismissed: sheetViewController) as? BottomSheetAnimator
)
animator.reduceMotionOverride = isReduceMotionEnabled
let context = MockAnimationContext(from: main, to: to)
trackForMemoryLeaks(animator, file: file, line: line)
trackForMemoryLeaks(context, file: file, line: line)
return (animator, context)
}

func makeSheet(
file: StaticString = #filePath,
line: UInt = #line
) -> BottomSheetController {
let sheet = BottomSheetController(
title: "Bottom Sheet",
childView: UIView(),
appearance: BottomSheetController.Appearance(animationDuration: 0.0)
)
trackForMemoryLeaks(sheet)
return sheet
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@ import XCTest

final class BottomSheetPresentAnimatorTests: XCTestCase {
func test_animate() throws {
let sheetController = BottomSheetController(
title: "Bottom Sheet",
childView: UIView(),
appearance: BottomSheetController.Appearance(animationDuration: 0.0)
)
let sheetController = makeSheet()
let (sut, context) = try makeSUT(sheetViewController: sheetController, to: sheetController)

XCTAssertTrue(sut is BottomSheetPresentAnimator)
Expand All @@ -26,15 +22,42 @@ final class BottomSheetPresentAnimatorTests: XCTestCase {
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
XCTAssertTrue(context.wasCompleteCalled)
XCTAssertTrue(context.didComplete)
}

func test_animateWithoutReduceMotion_TranslatesUp() throws {
let sheetController = makeSheet()
let (sut, context) = try makeSUT(
sheetViewController: sheetController,
to: sheetController,
isReduceMotionEnabled: false
)

sut.animateTransition(using: context)

// Wait for the run loop to tick (animate keyboard)
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
XCTAssertEqual(sheetController.dimmerView.alpha, 1)
XCTAssertLessThan(sheetController.sheetView.frame.minY, context.containerView.bounds.maxY)
}

func test_animateWithoutTo_Fails() throws {
let sheetController = BottomSheetController(
title: "Bottom Sheet",
childView: UIView()
func test_animateWithReduceMotion_FadesIn() throws {
let sheetController = makeSheet()
let (sut, context) = try makeSUT(
sheetViewController: sheetController,
to: sheetController,
isReduceMotionEnabled: true
)

sut.animateTransition(using: context)

// Wait for the run loop to tick (animate keyboard)
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
XCTAssertEqual(sheetController.dimmerView.alpha, 1)
XCTAssertEqual(sheetController.sheetView.alpha, 1)
}

func test_animateWithoutTo_Fails() throws {
let sheetController = makeSheet()
let (sut, context) = try makeSUT(sheetViewController: sheetController, to: nil)

XCTAssertFalse(context.wasCompleteCalled)
Expand All @@ -49,16 +72,35 @@ private extension BottomSheetPresentAnimatorTests {
func makeSUT(
sheetViewController: BottomSheetController,
to: UIViewController?,
isReduceMotionEnabled: Bool? = nil,
file: StaticString = #filePath,
line: UInt = #line
) throws -> (UIViewControllerAnimatedTransitioning, MockAnimationContext) {
let main = UIViewController()
let animator = try XCTUnwrap(
sheetViewController.animationController(forPresented: sheetViewController, presenting: main, source: main)
sheetViewController.animationController(
forPresented: sheetViewController,
presenting: main,
source: main
) as? BottomSheetAnimator
)
animator.reduceMotionOverride = isReduceMotionEnabled
let context = MockAnimationContext(from: main, to: to)
trackForMemoryLeaks(animator, file: file, line: line)
trackForMemoryLeaks(context, file: file, line: line)
return (animator, context)
}

func makeSheet(
file: StaticString = #filePath,
line: UInt = #line
) -> BottomSheetController {
let sheet = BottomSheetController(
title: "Bottom Sheet",
childView: UIView(),
appearance: BottomSheetController.Appearance(animationDuration: 0.0)
)
trackForMemoryLeaks(sheet)
return sheet
}
}