Skip to content

Commit

Permalink
VoiceOver fixes (#6)
Browse files Browse the repository at this point in the history
- Don't trigger paging to a different card using voice over; only the
  current card is "accessible" and when using voice over you have to
  page manually (e.g., by using a page control), but not by scrolling
  through the pages.
- Don't read out hidden elements when card is extended
  • Loading branch information
nighthawk authored Oct 19, 2021
1 parent 6319564 commit d4b8956
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 13 deletions.
2 changes: 2 additions & 0 deletions Sources/TGCardViewController/TGCardViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1676,8 +1676,10 @@ extension TGCardViewController {
UIView.animate(withDuration: animated ? 0.25: 0) {
self.topFloatingViewWrapper.alpha = fade ? 0 : 1
self.topFloatingViewWrapper.isUserInteractionEnabled = !fade
self.topFloatingViewWrapper.isAccessibilityElement = !fade
self.bottomFloatingViewWrapper.alpha = fade ? 0 : 1
self.bottomFloatingViewWrapper.isUserInteractionEnabled = !fade
self.bottomFloatingViewWrapper.isAccessibilityElement = !fade
}
}

Expand Down
22 changes: 11 additions & 11 deletions Sources/TGCardViewController/TGCardViewController.xib
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19162" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="ipad12_9rounded" orientation="portrait" layout="fullscreen" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19144"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
Expand Down Expand Up @@ -52,13 +52,13 @@
<rect key="frame" x="0.0" y="0.0" width="1024" height="1366"/>
</view>
<view hidden="YES" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" translatesAutoresizingMaskIntoConstraints="NO" id="Bxf-J1-ygI" userLabel="Top Info Wrapper">
<rect key="frame" x="376" y="8" width="272" height="44"/>
<rect key="frame" x="376" y="32" width="272" height="44"/>
<constraints>
<constraint firstAttribute="height" priority="750" constant="44" placeholder="YES" id="SYM-Qy-rtL"/>
</constraints>
</view>
<visualEffectView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalCompressionResistancePriority="749" placeholderIntrinsicWidth="45" placeholderIntrinsicHeight="91" translatesAutoresizingMaskIntoConstraints="NO" id="DJJ-p5-rXz" userLabel="Top Floating View Wrapper">
<rect key="frame" x="664" y="8" width="352" height="91"/>
<rect key="frame" x="664" y="32" width="352" height="91"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="3Nr-Q0-9p1">
<rect key="frame" x="0.0" y="0.0" width="352" height="91"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
Expand All @@ -82,19 +82,19 @@
</userDefinedRuntimeAttributes>
</visualEffectView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="zlR-ey-yL8" userLabel="Sidebar Background">
<rect key="frame" x="0.0" y="0.0" width="360" height="1346"/>
<rect key="frame" x="0.0" y="24" width="360" height="1322"/>
<subviews>
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IEW-xx-P4c">
<rect key="frame" x="0.0" y="0.0" width="360" height="1346"/>
<rect key="frame" x="0.0" y="0.0" width="360" height="1322"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="KLf-du-5ZR">
<rect key="frame" x="0.0" y="0.0" width="360" height="1346"/>
<rect key="frame" x="0.0" y="0.0" width="360" height="1322"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<visualEffectView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="EWh-dP-38F">
<rect key="frame" x="0.0" y="0.0" width="120" height="1218"/>
<rect key="frame" x="0.0" y="0.0" width="120" height="1194"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="PaT-N6-scp">
<rect key="frame" x="0.0" y="0.0" width="120" height="1218"/>
<rect key="frame" x="0.0" y="0.0" width="120" height="1194"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
<vibrancyEffect>
Expand All @@ -107,7 +107,7 @@
<blurEffect style="regular"/>
</visualEffectView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2dL-e1-o2y">
<rect key="frame" x="360" y="0.0" width="1" height="1346"/>
<rect key="frame" x="360" y="0.0" width="1" height="1322"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" constant="1" id="QnL-37-2Pi"/>
Expand All @@ -130,7 +130,7 @@
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" notEnabled="YES"/>
<bool key="isElement" value="YES"/>
<bool key="isElement" value="NO"/>
</accessibility>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ewk-8T-6Fw" userLabel="Header View">
Expand Down
32 changes: 30 additions & 2 deletions Sources/TGCardViewController/cards/TGPageCardView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ class TGPageCardView: TGCardView {
(contentView.subviews as? [TGCardView]) ?? []
}

private func cardView(index: Int) -> TGCardView? {
let cardViews = self.cardViews
guard index >= 0, index <= cardViews.count else { return nil }
return cardViews[index]
}

override var preferredView: UIView {
cardViews.first?.preferredView ?? self
}
Expand Down Expand Up @@ -136,6 +142,8 @@ class TGPageCardView: TGCardView {
// This will be used in both `moveForward` and `moveBackward`, so
// it's important to "initailise" this value correctly.
lastHorizontalOffset = CGFloat(pageCard.initialPageIndex) * (frame.width + Constants.spaceBetweenCards)

self.accessibilityElements = [cardView(index: pageCard.initialPageIndex)].compactMap { $0 }
}

override func updateDismissButton(show: Bool, isSpringLoaded: Bool) {
Expand Down Expand Up @@ -206,6 +214,10 @@ class TGPageCardView: TGCardView {
}

func moveForward(animated: Bool = true) {
// Assign `visiblePage` here so that we allow paging there even if VoiceOver is enabled
visiblePage = min(cardViews.count - 1, visiblePage + 1)
self.accessibilityElements = [cardView(index: visiblePage)].compactMap { $0 }

// Shift by the entire width of the card view
let nextFullWidthHorizontalOffset = pager.contentOffset.x + frame.width + Constants.spaceBetweenCards

Expand All @@ -230,6 +242,10 @@ class TGPageCardView: TGCardView {
}

func moveBackward(animated: Bool = true) {
// Assign `visiblePage` here so that we allow paging there even if VoiceOver is enabled
visiblePage = max(0, visiblePage - 1)
self.accessibilityElements = [cardView(index: visiblePage)].compactMap { $0 }

// See `moveForward()` for comments.
let nextFullWidthHorizontalOffset = pager.contentOffset.x - frame.width - Constants.spaceBetweenCards
let nextFullPageHorizontalOffset = lastHorizontalOffset - frame.width - Constants.spaceBetweenCards
Expand All @@ -246,9 +262,12 @@ class TGPageCardView: TGCardView {
func move(to cardIndex: Int, animated: Bool = true) {
// index must fall within the range of available content cards.
guard 0..<contentView.subviews.count ~= cardIndex else { return }


// Assign `visiblePage` here so that we allow paging there even if VoiceOver is enabled
visiblePage = cardIndex
self.accessibilityElements = [cardView(index: visiblePage)].compactMap { $0 }

let newX = (frame.width + Constants.spaceBetweenCards) * CGFloat(cardIndex)

pager.setContentOffset(CGPoint(x: newX, y: 0), animated: animated)
}

Expand All @@ -273,6 +292,15 @@ extension TGPageCardView: UIScrollViewDelegate {
}

fileprivate func scrollViewDidComeToCompleteStop(_ scrollView: UIScrollView) {

// Don't allow paging by scrolling when VoiceOver is running. This is working
// around a weird behaviour in (at least) iOS 15 that immediately scrolls to
// offset.y == 0 on presenting the initial page.
if UIAccessibility.isVoiceOverRunning, currentPage != visiblePage {
move(to: visiblePage, animated: false)
return
}

delegate?.didChangeCurrentPage(to: currentPage, animated: true)
visiblePage = currentPage
lastHorizontalOffset = scrollView.contentOffset.y
Expand Down

0 comments on commit d4b8956

Please sign in to comment.