diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift index 5288e643e..8a4687c52 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift @@ -81,10 +81,10 @@ struct RoomScreen: View { TopBannerLayer(knockRequestsBanner, isVisible: context.viewState.shouldSeeKnockRequests) ], footer: dateBadge) .safeAreaInset(edge: .top) { - // When VoiceOver is enabled, the table view isn't reversed and the scroll gestures - // don't trigger meaning the banner never hides itself and so the .overlay layout - // above permanently obscures the top of the timeline. So whenever VoiceOver is - // enabled we use a safe area inset to vertically stack it above the timeline. + // When VoiceOver is enabled the scroll gestures don't trigger, so the banner never + // hides itself and the .overlay layout above would permanently obscure the top of + // the timeline. So whenever VoiceOver is enabled we use a safe area inset to + // vertically stack it above the timeline instead. if context.viewState.shouldShowPinnedEventsBanner || context.viewState.isSharingLiveLocation, isVoiceOverEnabled { VStack(spacing: 0) { if context.viewState.shouldShowPinnedEventsBanner { diff --git a/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift b/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift index 8063e60d1..a1ed6f09f 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift @@ -180,11 +180,6 @@ class TimelineTableViewController: UIViewController { /// Whether or not the view has been shown on screen yet. private var hasAppearedOnce = false - /// Value that determines if the table view is flipped or not according to the VoiceOver status. - private var scaleY: CGFloat { - UIAccessibility.isVoiceOverRunning ? 1 : -1 - } - init(coordinator: TimelineViewRepresentable.Coordinator, isScrolledToBottom: Binding, isReadMarkerVisible: Binding, @@ -208,9 +203,8 @@ class TimelineTableViewController: UIViewController { tableView.keyboardDismissMode = .onDrag tableView.backgroundColor = .compound.bgCanvasDefault - // The tableview should be flipped to display the newest items at the top - // the only exception is VoiceOver, where we want to keep the latest item at the top as Android. - tableView.transform = CGAffineTransform(scaleX: 1, y: scaleY) + // The tableview is flipped to display the newest items at the bottom. + tableView.transform = CGAffineTransform(scaleX: 1, y: -1) view.addSubview(tableView) // Prevents XCUITest from invoking the diffable dataSource's cellProvider @@ -256,15 +250,6 @@ class TimelineTableViewController: UIViewController { } .store(in: &cancellables) - // Observe voice over status changes to flip the table view accordingly - NotificationCenter.default.publisher(for: UIAccessibility.voiceOverStatusDidChangeNotification) - .sink { [weak self] _ in - guard let self else { return } - tableView.transform = CGAffineTransform(scaleX: 1, y: scaleY) - tableView.reloadData() - } - .store(in: &cancellables) - configureDataSource() } @@ -312,7 +297,7 @@ class TimelineTableViewController: UIViewController { .background(Color.clear) // Flipping the cell can create some issues with cell resizing, so flip the content View - cell.contentView.transform = CGAffineTransform(scaleX: 1, y: scaleY) + cell.contentView.transform = CGAffineTransform(scaleX: 1, y: -1) cell.accessibilityElements = [cell.contentView] // Ensure VoiceOver reads the content view only return cell @@ -339,7 +324,7 @@ class TimelineTableViewController: UIViewController { .background(Color.clear) // Flipping the cell can create some issues with cell resizing, so flip the content View - cell.contentView.transform = CGAffineTransform(scaleX: 1, y: scaleY) + cell.contentView.transform = CGAffineTransform(scaleX: 1, y: -1) return cell } } @@ -633,19 +618,16 @@ extension TimelineTableViewController { /// Returns the timestamp of the newest visible timeline item. /// - /// The table view is flipped (unless VoiceOver is running), so the "newest" - /// visible cell on screen is actually the *last* index path in `indexPathsForVisibleRows` - /// when the table is flipped, or the *first* when it is not. + /// The table view is flipped, so the "newest" visible cell on screen is + /// actually the *last* index path in `indexPathsForVisibleRows`. private func newestVisibleDate() -> Date? { guard let visibleIndexPaths = tableView.indexPathsForVisibleRows, !visibleIndexPaths.isEmpty else { return nil } - // In a flipped table view the last index path is the topmost item on screen; - // when VoiceOver is active the table is not flipped so the first is topmost. - let isFlipped = scaleY == -1 - let orderedPaths = isFlipped ? visibleIndexPaths.reversed() : visibleIndexPaths + // In a flipped table view the last index path is the topmost item on screen. + let orderedPaths = visibleIndexPaths.reversed() // Walk from topmost downward and return the timestamp of the first item that has one. for indexPath in orderedPaths {