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

Native Editor - VoiceOver improvements #4737

Merged
merged 11 commits into from Feb 8, 2024
Expand Up @@ -52,6 +52,8 @@ public struct WKSourceEditorLocalizedStrings {
let keyboardTemplateButtonAccessibility: String
let keyboardCommentButtonAccessibility: String

let wikitextEditorAccessibility: String
let wikitextEditorLoadingAccessibility: String
let findTextFieldAccessibility: String
let findClearButtonAccessibility: String
let findCurrentMatchInfoFormatAccessibility: String
Expand All @@ -66,7 +68,7 @@ public struct WKSourceEditorLocalizedStrings {
let replaceTypeSingleAccessibility: String
let replaceTypeAllAccessibility: String

public init(keyboardTextFormattingTitle: String, keyboardParagraph: String, keyboardHeading: String, keyboardSubheading1: String, keyboardSubheading2: String, keyboardSubheading3: String, keyboardSubheading4: String, findAndReplaceTitle: String, replaceTypeSingle: String, replaceTypeAll: String, replaceTextfieldPlaceholder: String, replaceTypeContextMenuTitle: String, toolbarOpenTextFormatMenuButtonAccessibility: String, toolbarReferenceButtonAccessibility: String, toolbarLinkButtonAccessibility: String, toolbarTemplateButtonAccessibility: String, toolbarImageButtonAccessibility: String, toolbarFindButtonAccessibility: String, toolbarExpandButtonAccessibility: String, toolbarListUnorderedButtonAccessibility: String, toolbarListOrderedButtonAccessibility: String, toolbarIndentIncreaseButtonAccessibility: String, toolbarIndentDecreaseButtonAccessibility: String, toolbarCursorUpButtonAccessibility: String, toolbarCursorDownButtonAccessibility: String, toolbarCursorPreviousButtonAccessibility: String, toolbarCursorNextButtonAccessibility: String, toolbarBoldButtonAccessibility: String, toolbarItalicsButtonAccessibility: String, keyboardCloseTextFormatMenuButtonAccessibility: String, keyboardBoldButtonAccessibility: String, keyboardItalicsButtonAccessibility: String, keyboardUnderlineButtonAccessibility: String, keyboardStrikethroughButtonAccessibility: String, keyboardReferenceButtonAccessibility: String, keyboardLinkButtonAccessibility: String, keyboardListUnorderedButtonAccessibility: String, keyboardListOrderedButtonAccessibility: String, keyboardIndentIncreaseButtonAccessibility: String, keyboardIndentDecreaseButtonAccessibility: String, keyboardSuperscriptButtonAccessibility: String, keyboardSubscriptButtonAccessibility: String, keyboardTemplateButtonAccessibility: String, keyboardCommentButtonAccessibility: String, findTextFieldAccessibility: String, findClearButtonAccessibility: String, findCurrentMatchInfoFormatAccessibility: String, findCurrentMatchInfoZeroResultsAccessibility: String, findCloseButtonAccessibility: String, findNextButtonAccessibility: String, findPreviousButtonAccessibility: String, replaceTextFieldAccessibility: String, replaceClearButtonAccessibility: String, replaceButtonAccessibilityFormat: String, replaceTypeButtonAccessibilityFormat: String, replaceTypeSingleAccessibility: String, replaceTypeAllAccessibility: String) {
public init(keyboardTextFormattingTitle: String, keyboardParagraph: String, keyboardHeading: String, keyboardSubheading1: String, keyboardSubheading2: String, keyboardSubheading3: String, keyboardSubheading4: String, findAndReplaceTitle: String, replaceTypeSingle: String, replaceTypeAll: String, replaceTextfieldPlaceholder: String, replaceTypeContextMenuTitle: String, toolbarOpenTextFormatMenuButtonAccessibility: String, toolbarReferenceButtonAccessibility: String, toolbarLinkButtonAccessibility: String, toolbarTemplateButtonAccessibility: String, toolbarImageButtonAccessibility: String, toolbarFindButtonAccessibility: String, toolbarExpandButtonAccessibility: String, toolbarListUnorderedButtonAccessibility: String, toolbarListOrderedButtonAccessibility: String, toolbarIndentIncreaseButtonAccessibility: String, toolbarIndentDecreaseButtonAccessibility: String, toolbarCursorUpButtonAccessibility: String, toolbarCursorDownButtonAccessibility: String, toolbarCursorPreviousButtonAccessibility: String, toolbarCursorNextButtonAccessibility: String, toolbarBoldButtonAccessibility: String, toolbarItalicsButtonAccessibility: String, keyboardCloseTextFormatMenuButtonAccessibility: String, keyboardBoldButtonAccessibility: String, keyboardItalicsButtonAccessibility: String, keyboardUnderlineButtonAccessibility: String, keyboardStrikethroughButtonAccessibility: String, keyboardReferenceButtonAccessibility: String, keyboardLinkButtonAccessibility: String, keyboardListUnorderedButtonAccessibility: String, keyboardListOrderedButtonAccessibility: String, keyboardIndentIncreaseButtonAccessibility: String, keyboardIndentDecreaseButtonAccessibility: String, keyboardSuperscriptButtonAccessibility: String, keyboardSubscriptButtonAccessibility: String, keyboardTemplateButtonAccessibility: String, keyboardCommentButtonAccessibility: String, wikitextEditorAccessibility: String, wikitextEditorLoadingAccessibility: String, findTextFieldAccessibility: String, findClearButtonAccessibility: String, findCurrentMatchInfoFormatAccessibility: String, findCurrentMatchInfoZeroResultsAccessibility: String, findCloseButtonAccessibility: String, findNextButtonAccessibility: String, findPreviousButtonAccessibility: String, replaceTextFieldAccessibility: String, replaceClearButtonAccessibility: String, replaceButtonAccessibilityFormat: String, replaceTypeButtonAccessibilityFormat: String, replaceTypeSingleAccessibility: String, replaceTypeAllAccessibility: String) {
self.keyboardTextFormattingTitle = keyboardTextFormattingTitle
self.keyboardParagraph = keyboardParagraph
self.keyboardHeading = keyboardHeading
Expand Down Expand Up @@ -111,6 +113,8 @@ public struct WKSourceEditorLocalizedStrings {
self.keyboardSubscriptButtonAccessibility = keyboardSubscriptButtonAccessibility
self.keyboardTemplateButtonAccessibility = keyboardTemplateButtonAccessibility
self.keyboardCommentButtonAccessibility = keyboardCommentButtonAccessibility
self.wikitextEditorAccessibility = wikitextEditorAccessibility
self.wikitextEditorLoadingAccessibility = wikitextEditorLoadingAccessibility
self.findTextFieldAccessibility = findTextFieldAccessibility
self.findClearButtonAccessibility = findClearButtonAccessibility
self.findCurrentMatchInfoFormatAccessibility = findCurrentMatchInfoFormatAccessibility
Expand Down
Expand Up @@ -13,6 +13,15 @@ fileprivate var needsTextKit2: Bool {
}
}

class WKSourceEditorTextView: UITextView {
override func accessibilityActivate() -> Bool {

UIAccessibility.post(notification: .announcement, argument: WKSourceEditorLocalizedStrings.current.wikitextEditorLoadingAccessibility)

return super.accessibilityActivate()
}
}

@objc final class WKSourceEditorSelectionState: NSObject {
let isBold: Bool
let isItalics: Bool
Expand Down Expand Up @@ -101,7 +110,7 @@ final class WKSourceEditorTextFrameworkMediator: NSObject {
let textView: UITextView
if needsTextKit2 {
if #available(iOS 16, *) {
textView = UITextView(usingTextLayoutManager: true)
textView = WKSourceEditorTextView(usingTextLayoutManager: true)
textKit2Storage = textView.textLayoutManager?.textContentManager as? NSTextContentStorage
} else {
fatalError("iOS 15 cannot handle TextKit2")
Expand All @@ -118,7 +127,7 @@ final class WKSourceEditorTextFrameworkMediator: NSObject {
layoutManager.addTextContainer(container)
textKit1Storage?.addLayoutManager(layoutManager)

textView = UITextView(frame: .zero, textContainer: container)
textView = WKSourceEditorTextView(frame: .zero, textContainer: container)
textKit2Storage = nil
}

Expand Down
Expand Up @@ -149,6 +149,7 @@ public class WKSourceEditorViewController: WKComponentViewController {
textView.delegate = self
textFrameworkMediator.delegate = self
textView.isEditable = !viewModel.needsReadOnly
textView.accessibilityLabel = WKSourceEditorLocalizedStrings.current.wikitextEditorAccessibility

view.addSubview(textView)
updateColorsAndFonts()
Expand Down
4 changes: 3 additions & 1 deletion Components/Tests/ComponentsTests/WKSourceEditorTests.swift
Expand Up @@ -104,7 +104,9 @@ extension WKSourceEditorLocalizedStrings {
keyboardSuperscriptButtonAccessibility: "",
keyboardSubscriptButtonAccessibility: "",
keyboardTemplateButtonAccessibility: "",
keyboardCommentButtonAccessibility: "",
keyboardCommentButtonAccessibility: "",
wikitextEditorAccessibility: "",
wikitextEditorLoadingAccessibility: "",
findTextFieldAccessibility: "",
findClearButtonAccessibility: "",
findCurrentMatchInfoFormatAccessibility: "",
Expand Down
2 changes: 2 additions & 0 deletions WMF Framework/CommonStrings.swift
Expand Up @@ -606,6 +606,8 @@ public class CommonStrings: NSObject {

public static let editorKeyboardButtonCloseTextFormatMenuAccessiblityLabel = WMFLocalizedString("editor-keyboard-close-text-format-menu-accessibility", value: "Close text formatting menu", comment: "Accessibility label for close keyboard button on the editor. This button closes the keyboard text formatting menu.")

public static let editorWikitextTextViewAccessibility = WMFLocalizedString("editor-wikitext-textview-accessibility", value: "Wiki text editor", comment: "Accessibility label for the wikitext editor textview.")
public static let editorWikitextLoadingAccessibility = WMFLocalizedString("editor-wikitext-loading-accessibility", value: "Loading editor text", comment: "Accessibility announcement when the editor textview is activated. This will be spoken with VoiceOver if loading takes a while.")
public static let editorFindTextFieldAccessibilityLabel = WMFLocalizedString("editor-find-textfield-accessibility", value: "Find", comment: "Accessibility label for the find textfield on the editor")
public static let editorFindClearButtonAccessibilityLabel = WMFLocalizedString("editor-find-clear-button-accessibility", value: "Clear find", comment: "Accessibility label for the clear find button on the editor. This button clears the text in the find textfield.")
public static let editorFindCurrentMatchInfoFormatAccessibilityLabel = WMFLocalizedString("editor-find-current-match-info-accessibility", value: "%1$@ total matches found. Highlighted match number %2$@", comment: "Accessibility text for the match results informational label. %1$@ is replaced by the total number of matches found. %2$@ is replaced by which number match is currently highlighted.")
Expand Down
10 changes: 10 additions & 0 deletions Wikipedia/Code/EditNoticesView.swift
Expand Up @@ -229,6 +229,8 @@ final class EditNoticesView: SetupView {
footerStack.topAnchor.constraint(equalTo: footerContainer.topAnchor, constant: 16),
footerStack.bottomAnchor.constraint(equalTo: footerContainer.safeAreaLayoutGuide.bottomAnchor, constant: -16)
])

changeTextViewVoiceOverVisibility(isVisible: false)
}

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
Expand All @@ -239,6 +241,14 @@ final class EditNoticesView: SetupView {
}

// MARK: - Public

func changeTextViewVoiceOverVisibility(isVisible: Bool) {
if !isVisible {
accessibilityElements = [doneButton, editNoticesTitle, editNoticesSubtitle, footerSwitchLabel, toggleSwitch]
} else {
accessibilityElements = [doneButton, editNoticesTitle, editNoticesSubtitle, textView, footerSwitchLabel, toggleSwitch]
}
}

func configure(viewModel: EditNoticesViewModel, theme: Theme) {
let attributedNoticeString = NSMutableAttributedString()
Expand Down
11 changes: 11 additions & 0 deletions Wikipedia/Code/EditNoticesViewController.swift
Expand Up @@ -38,6 +38,17 @@ class EditNoticesViewController: ThemeableViewController, RMessageSuppressing {
editNoticesView.toggleSwitch.isOn = UserDefaults.standard.wmf_alwaysDisplayEditNotices
editNoticesView.textView.delegate = self
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

UIAccessibility.post(notification: .screenChanged, argument: editNoticesView.editNoticesTitle)

DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.editNoticesView.changeTextViewVoiceOverVisibility(isVisible: true)
UIAccessibility.post(notification: .layoutChanged, argument: nil)
}
}

// MARK: - Actions

Expand Down
6 changes: 6 additions & 0 deletions Wikipedia/Code/InsertMediaSettingsViewController.swift
Expand Up @@ -209,6 +209,12 @@ final class InsertMediaSettingsViewController: ViewController {
tableView.tableHeaderView = imageView
tableView.tableFooterView = buttonView
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

UIAccessibility.post(notification: .screenChanged, argument: nil)
}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
Expand Down
3 changes: 3 additions & 0 deletions Wikipedia/Code/PageEditorViewController.swift
Expand Up @@ -428,6 +428,8 @@ final class PageEditorViewController: UIViewController {
keyboardSubscriptButtonAccessibility: CommonStrings.editorKeyboardButtonSubscriptAccessiblityLabel,
keyboardTemplateButtonAccessibility: CommonStrings.editorKeyboardButtonTemplateAccessiblityLabel,
keyboardCommentButtonAccessibility: CommonStrings.editorKeyboardButtonCommentAccessiblityLabel,
wikitextEditorAccessibility: CommonStrings.editorWikitextTextViewAccessibility,
wikitextEditorLoadingAccessibility: CommonStrings.editorWikitextLoadingAccessibility,
findTextFieldAccessibility: CommonStrings.editorFindTextFieldAccessibilityLabel,
findClearButtonAccessibility: CommonStrings.editorFindClearButtonAccessibilityLabel,
findCurrentMatchInfoFormatAccessibility: CommonStrings.editorFindCurrentMatchInfoFormatAccessibilityLabel,
Expand Down Expand Up @@ -615,6 +617,7 @@ extension PageEditorViewController: WKSourceEditorViewControllerDelegate {
}

func sourceEditorViewControllerDidTapImage() {
sourceEditor.removeFocus()
let insertMediaViewController = InsertMediaViewController(articleTitle: pageURL.wmf_title, siteURL: pageURL.wmf_site)
insertMediaViewController.delegate = self
insertMediaViewController.apply(theme: theme)
Expand Down
2 changes: 2 additions & 0 deletions Wikipedia/Localizations/en.lproj/Localizable.strings
Expand Up @@ -398,6 +398,8 @@
"editor-toolbar-reference-accessibility" = "Reference text formatting";
"editor-toolbar-show-more-accessibility" = "Show more formatting options";
"editor-toolbar-template-accessibility" = "Template text formatting";
"editor-wikitext-loading-accessibility" = "Loading editor text";
"editor-wikitext-textview-accessibility" = "Wiki text editor";
"empty-diff-compare-title" = "No differences between revisions";
"empty-diff-single-title" = "No viewable changes made";
"empty-insert-media-title" = "Select a file from Wikimedia Commons";
Expand Down
2 changes: 2 additions & 0 deletions Wikipedia/Localizations/qqq.lproj/Localizable.strings
Expand Up @@ -398,6 +398,8 @@
"editor-toolbar-reference-accessibility" = "Accessibility label for reference toolbar button on the editor.";
"editor-toolbar-show-more-accessibility" = "Accessibility label for expand button on the formatting toolbar in editor. This button reveals more formatting toolbar buttons.";
"editor-toolbar-template-accessibility" = "Accessibility label for template toolbar button on the editor.";
"editor-wikitext-loading-accessibility" = "Accessibility announcement when the editor textview is activated. This will be spoken with VoiceOver if loading takes a while.";
"editor-wikitext-textview-accessibility" = "Accessibility label for the wikitext editor textview.";
"empty-diff-compare-title" = "Text for placeholder label visible when diff comparision between revisions is empty.";
"empty-diff-single-title" = "Text for placeholder label visible when diff returned for single revision is empty.";
"empty-insert-media-title" = "Text for placeholder label visible when no file was selected or uploaded";
Expand Down
Binary file modified Wikipedia/iOS Native Localizations/en.lproj/Localizable.strings
Binary file not shown.
4 changes: 3 additions & 1 deletion WikipediaUITests/UITestHelperViewController.swift
Expand Up @@ -84,7 +84,9 @@ public class UITestHelperViewController: WKCanvasViewController {
keyboardSuperscriptButtonAccessibility: CommonStrings.editorKeyboardButtonSuperscriptAccessiblityLabel,
keyboardSubscriptButtonAccessibility: CommonStrings.editorKeyboardButtonSubscriptAccessiblityLabel,
keyboardTemplateButtonAccessibility: CommonStrings.editorKeyboardButtonTemplateAccessiblityLabel,
keyboardCommentButtonAccessibility: CommonStrings.editorKeyboardButtonCommentAccessiblityLabel,
keyboardCommentButtonAccessibility: CommonStrings.editorKeyboardButtonCommentAccessiblityLabel,
wikitextEditorAccessibility: CommonStrings.editorWikitextTextViewAccessibility,
wikitextEditorLoadingAccessibility: CommonStrings.editorWikitextLoadingAccessibility,
findTextFieldAccessibility: CommonStrings.editorFindTextFieldAccessibilityLabel,
findClearButtonAccessibility: CommonStrings.editorFindClearButtonAccessibilityLabel,
findCurrentMatchInfoFormatAccessibility: CommonStrings.editorFindCurrentMatchInfoFormatAccessibilityLabel,
Expand Down