web-dev-qa-db-ja.com

UIAccessibility.post(notification:.announcement、argument: "arg")がナレーションでアナウンスされないのはなぜですか?

IOSでVoiceOverを使用している場合、UIAccessibility.post(notification:argument:)を呼び出してフィールドエラーをアナウンスしても、実際にはエラーがアナウンスされません。

送信ボタンがあり、ボタンにフォーカスを合わせると、ボイスオーバーでボタンのタイトルが期待どおりに読み取られます。ボタンを押すと、ナレーションでタイトルが再度読み取られます。送信ボタンが押されたとき、私はいくつかの検証を行っており、フィールドエラーがあるときは、次のように呼び出してアナウンスしようとしています。

if UIAccessibility.isVoiceOverRunning {
    UIAccessibility.post(notification: .announcement, argument: "my field error")
}

興味深いことに、デバッガーのブレークポイントで停止すると、アナウンスが発生します。ブレークポイントで停止しないと、アナウンスは発生しません。

通知はメインスレッドに投稿されており、NotificationCenter.defaultのような場合は、投稿されたのと同じスレッドで処理されると思います。すでにメインスレッド上にあるにもかかわらず、メインキューに呼び出しをディスパッチしようとしましたが、それも機能していないようです。

私が考えることができる唯一のことは、ボイスオーバーが送信ボタンのタイトルを読み終える前に通知が投稿されて観察され、アナウンス通知が現在のボイスオーバーを中断しないということです。

これについての助けを本当にいただければ幸いです。

6
brandenesmith

_UIAccessibility.announcementDidFinishNotification_のオブザーバーとして登録し、アナウンスと成功ステータスをuserInfoディクショナリから引き出す再試行メカニズムを使用して、これを機能させることができます。

成功ステータスがfalseで、アナウンスが先ほど送信したものと同じである場合は、通知を再度投稿します。これは、アナウンスが成功するまで繰り返し発生します。

このアプローチには、登録解除の必要性、別のオブジェクトが同じアナウンスを投稿した場合にどうなるか(これは実際には発生しないはずですが、理論的には発生する可能性があります)、最後のアナウンスを追跡する必要があるなど、明らかに複数の問題があります送信など.

コードは次のようになります。

_private var _errors: [String] = []
private var _lastAnnouncement: String = ""

init() {
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(announcementFinished(_:)),
        name: UIAccessibility.announcementDidFinishNotification,
        object: nil
    )
}

func showErrors() {
    if !_errors.isEmpty {
        view.errorLabel.text = _errors.first!
        view.errorLabel.isHidden = false

        if UIAccessibility.isVoiceOverRunning {
            _lastAnnouncement = _errors.first!
            UIAccessibility.post(notification: .announcement, argument: _errors.first!)
        }
    } else {
        view.errorLabel.text = ""
        view.errorLabel.isHidden = true
    }
}

@objc func announcementFinished(_ sender: Notification) {
    guard let announcement = sender.userInfo![UIAccessibility.announcementStringValueUserInfoKey] as? String else { return }
    guard let success = sender.userInfo![UIAccessibility.announcementWasSuccessfulUserInfoKey] as? Bool else { return }

    if !success && announcement == _lastAnnouncement {
        _lastAnnouncement = _errors.first!
        UIAccessibility.post(notification: .announcement, argument: _errors.first!)
    }
}
_

問題は、UIAccessibility.post(notification: .announcement, argument: _errors.first!)への最初の呼び出しが常に(ブレークポイントで停止されない限り)常に行われるため、この再試行メカニズムが常に使用されることです。最初の投稿が常に失敗する理由はまだわかりません。

1
brandenesmith

フィールドエラーが表示されている間にシステムが引き継ぐ必要があり、この場合、カスタムされたVoiceOver通知がキャンセルされるために問題が発生する可能性があります。

現在の状況を理解するのに役立つ可能性のある複数のVoiceOver通知のキューイングに関する問題について answer を書きました。

通知は遅延しているため、ブレークポイントで機能し、この時間中にシステムが機能します。通知とシステムの機能の間に重複はありません。

簡単な解決策は、通知を送信する前に短い遅延を実装することです

再試行メカニズムはスマートであり、多くのシステムテイクオーバーが発生した場合に、数回の再試行のループ内で改善される可能性があります。

2
XLE_22