Skip to content

Commit

Permalink
Improve VoiceOver and Voice Control experience (#211)
Browse files Browse the repository at this point in the history
* Settings: Add button trait

* Improve VoiceOver on Detail ViewController

* Use formatter for duration when possible

* Add button accessibility trait to all movie cells

* Add more granular context for release date in VoiceOver announcement

* Split between accessibilityLabel and accessibilityValue

* Fix Xcode warning

* Fix condition in formatter
  • Loading branch information
fbernutz authored May 15, 2024
1 parent 5dc5ae2 commit 2bd83bf
Show file tree
Hide file tree
Showing 11 changed files with 87 additions and 20 deletions.
Binary file modified Cineaste/Base.lproj/Localizable.strings
Binary file not shown.
2 changes: 2 additions & 0 deletions Cineaste/Custom UI/Button.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class LinkButton: Button {
guard let title = title else { return }

super.setTitle("▶︎ " + title, for: state)

accessibilityLabel = title.replacingOccurrences(of: "▶︎ ", with: "")
}

override func setup() {
Expand Down
6 changes: 5 additions & 1 deletion Cineaste/Extension/String+Cineaste.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ extension String {
static let searchInCategoryPlaceholder = NSLocalizedString("searchIn", comment: "Placeholder for search textField in MoviesList")
static let noContentTitle = NSLocalizedString("noContent", comment: "Title for no content")
static let onDate = NSLocalizedString("onDate", comment: "on a date")
static let genreAccessibilityLabel = NSLocalizedString("genre", comment: "Genre for a movie")
static let releasedOnDateAccessibilityLabel = NSLocalizedString("releasedOnDateVoiceOver", comment: "Movie released on dates")
static let releasedInYearAccessibilityLabel = NSLocalizedString("releasedInYearVoiceOver", comment: "Movie released in year")
static let releaseOnDateAccessibilityLabel = NSLocalizedString("releaseOnDateVoiceOver", comment: "Release date accessibility label for a movie")

// MARK: SEARCH VIEWCONTROLLER
static let discoverMovieTitle = NSLocalizedString("discoverMovieTitle", comment: "Title for search")
Expand All @@ -69,7 +73,7 @@ extension String {
}
}

static func voting(for vote: String) -> String {
static func votingAccessibilityLabel(for vote: String) -> String {
String.localizedStringWithFormat(NSLocalizedString("%@ of 10", comment: "Voting description"), vote)
}

Expand Down
39 changes: 37 additions & 2 deletions Cineaste/Models/Movie+Formatted.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ extension Movie {
return release.formatted
}

var accessibilityFormattedReleaseDate: String {
guard let release = releaseDate else {
return String.unknownReleaseDate
}

return String.releasedOnDateAccessibilityLabel + " " + release.formatted
}

var formattedRelativeReleaseInformation: String {
guard let release = releaseDate else {
return String.unknownReleaseDate
Expand All @@ -36,12 +44,39 @@ extension Movie {
}
}

// swiftlint:disable:next identifier_name
var accessibilityFormattedRelativeReleaseInformation: String {
guard let release = releaseDate else {
return String.unknownReleaseDate
}

let isCurrentYear = release.formattedOnlyYear == Current.date().formattedOnlyYear
if isCurrentYear {
if soonAvailable {
// Release on May 4, 2030
return String.releaseOnDateAccessibilityLabel + " " + release.formatted
} else {
// Released on May 4, 2023
return String.releasedOnDateAccessibilityLabel + " " + release.formatted
}
} else {
// Released in 2024
return String.releasedInYearAccessibilityLabel + " " + release.formattedOnlyYear
}
}

var formattedRuntime: String {
guard runtime != 0 else {
guard let runtime, runtime != 0 else {
return "\(String.unknownRuntime) min"
}

return "\(runtime?.formatted ?? String.unknownRuntime) min"
if #available(iOS 16.0, *) {
let duration = Duration.seconds(runtime * 60)
let format = duration.formatted(.units(allowed: [.minutes], width: .abbreviated))
return format
} else {
return "\(runtime.formatted ?? String.unknownRuntime) min"
}
}

var formattedWatchedDate: String? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,18 +222,35 @@ class MovieDetailViewController: UIViewController {
else { return }

titleLabel.text = movie.title
titleLabel.accessibilityTraits.insert(.header)

releaseDateAndRuntimeLabel.text = movie.formattedReleaseDate
+ ""
+ movie.formattedRuntime
releaseDateAndRuntimeLabel.accessibilityLabel = movie.accessibilityFormattedReleaseDate
+ ", "
+ movie.formattedRuntime

if !movie.formattedGenres.isEmpty {
genreLabel.isHidden = false
genreLabel.text = movie.formattedGenres
genreLabel.accessibilityLabel = String.genreAccessibilityLabel
genreLabel.accessibilityValue = movie.formattedGenres
} else {
genreLabel.isHidden = true
}

moreInformationStackView.isAccessibilityElement = true
moreInformationStackView.accessibilityTraits.insert(.link)
moreInformationStackView.accessibilityLabel = [
moreInformationButton.accessibilityLabel,
buttonInfoLabel.text
]
.compactMap { $0 }
.joined(separator: " ")

votingLabel.text = movie.formattedVoteAverage
votingLabel.accessibilityLabel = String.votingAccessibilityLabel(for: movie.formattedVoteAverage)
descriptionTextView.text = movie.overview
posterImageView.loadingImage(from: movie.posterPath, in: .original)
}
Expand Down
3 changes: 2 additions & 1 deletion Cineaste/ViewController/Movies/SeenMovieCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ class SeenMovieCell: UITableViewCell {

private func applyAccessibility(for movie: Movie) {
isAccessibilityElement = true
accessibilityTraits.insert(.button)

accessibilityLabel = movie.title

if let watchedDate = movie.formattedWatchedDate {
accessibilityLabel?.append(", \(watchedDate)")
accessibilityValue = watchedDate
}
}

Expand Down
14 changes: 10 additions & 4 deletions Cineaste/ViewController/Movies/WatchlistMovieCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,19 @@ class WatchlistMovieCell: UITableViewCell {

private func applyAccessibility(for movie: Movie) {
isAccessibilityElement = true
accessibilityTraits.insert(.button)

accessibilityLabel = movie.title

let voting = String.voting(for: movie.formattedVoteAverage)
accessibilityLabel?.append(", \(voting)")
accessibilityLabel?.append(", \(movie.formattedRelativeReleaseInformation)")
accessibilityLabel?.append(", \(movie.formattedRuntime)")
let value = [
String.votingAccessibilityLabel(for: movie.formattedVoteAverage),
movie.accessibilityFormattedRelativeReleaseInformation,
movie.formattedRuntime
]
.compactMap { $0 }
.filter { !$0.isEmpty }
.joined(separator: ", ")
accessibilityValue = value
}

private func updatePosterWidthIfNeeded() {
Expand Down
25 changes: 13 additions & 12 deletions Cineaste/ViewController/SearchMovies/SearchMoviesCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,23 +68,24 @@ class SearchMoviesCell: UITableViewCell {

private func applyAccessibility(for movie: Movie) {
isAccessibilityElement = true
accessibilityTraits.insert(.button)

accessibilityLabel = movie.title

if let state = String.state(for: movie.currentWatchState) {
accessibilityLabel?.append(", \(state)")
}

let voting = String.voting(for: movie.formattedVoteAverage)
accessibilityLabel?.append(", \(voting)")

let isSoonAvailable = !soonHint.isHidden
accessibilityLabel?.append(
let value = [
String.state(for: movie.currentWatchState),
String.votingAccessibilityLabel(for: movie.formattedVoteAverage),
isSoonAvailable
? ", \(String.soonReleaseInformationLong)"
: ""
)
accessibilityLabel?.append(", \(movie.formattedRelativeReleaseInformation)")
? String.soonReleaseInformationLong
: "",
movie.accessibilityFormattedRelativeReleaseInformation
]
.compactMap { $0 }
.filter { !$0.isEmpty }
.joined(separator: ", ")

accessibilityValue = value
}

private func updatePosterWidthIfNeeded() {
Expand Down
1 change: 1 addition & 0 deletions Cineaste/ViewController/Settings/SettingsCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class SettingsCell: UITableViewCell {
} else {
descriptionLabel.isHidden = true
}
accessibilityTraits.insert(.button)
}

}
Binary file modified Cineaste/de.lproj/Localizable.strings
Binary file not shown.
Binary file modified Cineaste/en.lproj/Localizable.strings
Binary file not shown.

0 comments on commit 2bd83bf

Please sign in to comment.