From 4a9a4d9d34762a9906c6f66aa356617c2f08d9d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Fri, 27 Oct 2023 18:04:58 +0200 Subject: [PATCH] Fix toggling scroll mode when VoiceOver is enabled --- .../EPUB/EPUBNavigatorViewModel.swift | 72 +++++++++++++++---- .../EPUB/Preferences/EPUBSettings.swift | 50 ++++++------- 2 files changed, 84 insertions(+), 38 deletions(-) diff --git a/Sources/Navigator/EPUB/EPUBNavigatorViewModel.swift b/Sources/Navigator/EPUB/EPUBNavigatorViewModel.swift index a9b994d28..b0ddd2ae2 100644 --- a/Sources/Navigator/EPUB/EPUBNavigatorViewModel.swift +++ b/Sources/Navigator/EPUB/EPUBNavigatorViewModel.swift @@ -168,11 +168,8 @@ final class EPUBNavigatorViewModel: Loggable { self.useLegacySettings = useLegacySettings legacyReadingProgression = publication.metadata.effectiveReadingProgression - settings = EPUBSettings( - preferences: config.preferences, - defaults: config.defaults, - metadata: publication.metadata - ) + preferences = config.preferences + settings = EPUBSettings(publication: publication, config: config) css = ReadiumCSS( layout: CSSLayout(), @@ -182,9 +179,18 @@ final class EPUBNavigatorViewModel: Loggable { ) css.update(with: settings) + + NotificationCenter.default.addObserver( + self, + selector: #selector(voiceOverStatusDidChange), + name: UIAccessibility.voiceOverStatusDidChangeNotification, + object: nil + ) } deinit { + NotificationCenter.default.removeObserver(self) + if let endpoint = publicationEndpoint { httpServer?.remove(at: endpoint) } @@ -222,15 +228,25 @@ final class EPUBNavigatorViewModel: Loggable { // MARK: - User preferences + /// Currently applied settings. private(set) var settings: EPUBSettings + /// Last submitted preferences. + private var preferences: EPUBPreferences + func submitPreferences(_ preferences: EPUBPreferences) { + self.preferences = preferences + applyPreferences() + } + + private func applyPreferences() { let oldSettings = settings let newSettings = EPUBSettings( preferences: preferences, - defaults: config.defaults, - metadata: publication.metadata + publication: publication, + config: config ) + settings = newSettings updateSpread() updateCSS(with: settings) @@ -287,12 +303,7 @@ final class EPUBNavigatorViewModel: Loggable { } var scroll: Bool { - // Force-enables scroll when VoiceOver is running, because pagination - // breaks the screen reader. - guard !UIAccessibility.isVoiceOverRunning else { - return true - } - return useLegacySettings ? legacyScroll : settings.scroll + useLegacySettings ? legacyScroll : settings.scroll } private var legacyScroll: Bool { @@ -414,4 +425,39 @@ final class EPUBNavigatorViewModel: Loggable { ) } } + + // MARK: - Accessibility + + private var isVoiceOverRunning = UIAccessibility.isVoiceOverRunning + + @objc private func voiceOverStatusDidChange() { + // Avoids excessive settings refresh when the status didn't change. + guard isVoiceOverRunning != UIAccessibility.isVoiceOverRunning else { + return + } + isVoiceOverRunning = UIAccessibility.isVoiceOverRunning + + // Re-apply preferences to force the scroll mode if needed. + applyPreferences() + } +} + +private extension EPUBSettings { + init( + preferences: EPUBPreferences? = nil, + publication: Publication, + config: EPUBNavigatorViewController.Configuration + ) { + self.init( + preferences: preferences ?? config.preferences, + defaults: config.defaults, + metadata: publication.metadata + ) + + // Force-enables scroll when VoiceOver is running, because pagination + // breaks the screen reader. + if UIAccessibility.isVoiceOverRunning { + scroll = true + } + } } diff --git a/Sources/Navigator/EPUB/Preferences/EPUBSettings.swift b/Sources/Navigator/EPUB/Preferences/EPUBSettings.swift index 5f71b5a40..918bc4618 100644 --- a/Sources/Navigator/EPUB/Preferences/EPUBSettings.swift +++ b/Sources/Navigator/EPUB/Preferences/EPUBSettings.swift @@ -11,31 +11,31 @@ import R2Shared /// /// See `EPUBPreferences` public struct EPUBSettings: ConfigurableSettings { - public let backgroundColor: Color? - public let columnCount: ColumnCount - public let fontFamily: FontFamily? - public let fontSize: Double - public let fontWeight: Double? - public let hyphens: Bool? - public let imageFilter: ImageFilter? - public let language: Language? - public let letterSpacing: Double? - public let ligatures: Bool? - public let lineHeight: Double? - public let pageMargins: Double - public let paragraphIndent: Double? - public let paragraphSpacing: Double? - public let publisherStyles: Bool - public let readingProgression: ReadingProgression - public let scroll: Bool - public let spread: Spread - public let textAlign: TextAlignment? - public let textColor: Color? - public let textNormalization: Bool - public let theme: Theme - public let typeScale: Double? - public let verticalText: Bool - public let wordSpacing: Double? + public var backgroundColor: Color? + public var columnCount: ColumnCount + public var fontFamily: FontFamily? + public var fontSize: Double + public var fontWeight: Double? + public var hyphens: Bool? + public var imageFilter: ImageFilter? + public var language: Language? + public var letterSpacing: Double? + public var ligatures: Bool? + public var lineHeight: Double? + public var pageMargins: Double + public var paragraphIndent: Double? + public var paragraphSpacing: Double? + public var publisherStyles: Bool + public var readingProgression: ReadingProgression + public var scroll: Bool + public var spread: Spread + public var textAlign: TextAlignment? + public var textColor: Color? + public var textNormalization: Bool + public var theme: Theme + public var typeScale: Double? + public var verticalText: Bool + public var wordSpacing: Double? public var effectiveBackgroundColor: Color { backgroundColor ?? theme.backgroundColor