VoiceOver читает метку доступности дважды, когда фокусируется не на UILabel titleView

Я создал игровую инфраструктуру когда-то, чтобы работать на Android и Desktop, часть рабочего стола, которая обрабатывает звук, может быть использована как вдохновение для того, что вам нужно.

https: // github. com / hamilton-lima / jaga / blob / master / jaga% 20desktop / src-desktop / com / athanazio / jaga / desktop / sound / Sound.java

Вот код для справки .

package com.athanazio.jaga.desktop.sound;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;

public class Sound {

    AudioInputStream in;

    AudioFormat decodedFormat;

    AudioInputStream din;

    AudioFormat baseFormat;

    SourceDataLine line;

    private boolean loop;

    private BufferedInputStream stream;

    // private ByteArrayInputStream stream;

    /**
     * recreate the stream
     * 
     */
    public void reset() {
        try {
            stream.reset();
            in = AudioSystem.getAudioInputStream(stream);
            din = AudioSystem.getAudioInputStream(decodedFormat, in);
            line = getLine(decodedFormat);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void close() {
        try {
            line.close();
            din.close();
            in.close();
        } catch (IOException e) {
        }
    }

    Sound(String filename, boolean loop) {
        this(filename);
        this.loop = loop;
    }

    Sound(String filename) {
        this.loop = false;
        try {
            InputStream raw = Object.class.getResourceAsStream(filename);
            stream = new BufferedInputStream(raw);

            // ByteArrayOutputStream out = new ByteArrayOutputStream();
            // byte[] buffer = new byte[1024];
            // int read = raw.read(buffer);
            // while( read > 0 ) {
            // out.write(buffer, 0, read);
            // read = raw.read(buffer);
            // }
            // stream = new ByteArrayInputStream(out.toByteArray());

            in = AudioSystem.getAudioInputStream(stream);
            din = null;

            if (in != null) {
                baseFormat = in.getFormat();

                decodedFormat = new AudioFormat(
                        AudioFormat.Encoding.PCM_SIGNED, baseFormat
                                .getSampleRate(), 16, baseFormat.getChannels(),
                        baseFormat.getChannels() * 2, baseFormat
                                .getSampleRate(), false);

                din = AudioSystem.getAudioInputStream(decodedFormat, in);
                line = getLine(decodedFormat);
            }
        } catch (UnsupportedAudioFileException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
    }

    private SourceDataLine getLine(AudioFormat audioFormat)
            throws LineUnavailableException {
        SourceDataLine res = null;
        DataLine.Info info = new DataLine.Info(SourceDataLine.class,
                audioFormat);
        res = (SourceDataLine) AudioSystem.getLine(info);
        res.open(audioFormat);
        return res;
    }

    public void play() {

        try {
            boolean firstTime = true;
            while (firstTime || loop) {

                firstTime = false;
                byte[] data = new byte[4096];

                if (line != null) {

                    line.start();
                    int nBytesRead = 0;

                    while (nBytesRead != -1) {
                        nBytesRead = din.read(data, 0, data.length);
                        if (nBytesRead != -1)
                            line.write(data, 0, nBytesRead);
                    }

                    line.drain();
                    line.stop();
                    line.close();

                    reset();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

2
задан rzulkoski 2 March 2019 в 03:09
поделиться

1 ответ

Причина, по которой вы читаете заголовок во второй раз, кроется в вашем коде.

В вашем viewDidLoad вы устанавливаете метку доступности стека, которую VoiceOver автоматически считывает, чтобы сообщить пользователю об изменении.

Далее, вы уведомляете об этом с помощью поста в viewDidAppear, который VoiceOver естественным образом считывает.

Чтобы избежать такого поведения, просто удалите stackView.accessibilityLabel = label.text в вашей функции setupNavigationItem и добавьте этот фрагмент в вашу личную ленивую метку var init:

if (self.view.subviews.contains(stackView)) {
        stackView.accessibilityLabel = label.text
}

Обновление stackView.accessibilityLabel таким способом не ' t вызвать VoiceOver, чтобы проинформировать пользователя и получить цель.

Однако я не рекомендую зачитывать заголовок как первый элемент новой страницы, если вы не измените порядок представленных элементов.

Пользователи VoiceOver, естественно, не догадаются, что перед заголовком присутствует другой элемент:

  • Они могут не найти способ вернуться на предыдущую страницу.
  • Они могут быть потеряны, если получат первый элемент страницы простым нажатием на 4 пальца , потому что они получат кнопку «Назад», а не заголовок.

Технически, ваша проблема решена с помощью кода выше , но концептуально я предлагаю переупорядочить ваши элементы, если вы все еще хотите представить заголовок в качестве первого элемента.

==========

РЕДАКТИРОВАТЬ (обходной путь)

О технической проблеме, вы правы в своем комментарии, это решение выше работает благодаря чтению этикетки VoiceOver.

Я отправил решение в вашу ветку git, которую вы дали в своем первоначальном посте.

Проблема касается UIStackView, который я не могу объяснить в этом случае и не могу решить ни как есть.

Чтобы достичь вашей цели, я создал UIAccessibilityELement для стека, который может быть идеально доступен и доступен без двойного чтения с пост-уведомлением.

Я сделал это, потому что я не мог программно получить новый размер стека, когда надписи находятся ... может быть, создание подкласса UIStackView и получение его layoutSubviews может быть уловкой?

Это решение должен работать как обходной путь , но я не знаю причину, почему это поведение появляется с UIStackview.

==========

РЕДАКТИРОВАТЬ (решение)

Проблема в том, как titleView из navigationItem создано. Лучший способ достичь вашей цели - это:

  • Инициализировать ваш titleView как простой UIView, кадр которого совпадает с видом стека.
  • Добавьте стековое представление как подпредставление после определения его фрейма и его свойств доступности.

Следуйте приведенным ниже инструкциям в вашем коде:

  • Добавьте черту .header в свойство стека:

    private lazy var stackView: UIStackView = {
        let stackView = UIStackView(frame: .zero)
        stackView.axis = .vertical
        stackView.alignment = .center
        stackView.distribution = .equalSpacing
        stackView.isAccessibilityElement = true
        stackView.accessibilityTraits = .header
        return stackView
    }()
    
  • [ 1144] Измените регистр стека в разделе кода «switch ... case ...», как показано ниже:

    case .stackView:
        label.text = "UIStackView"
        label.sizeToFit()
        stackView.addArrangedSubview(label)
    
        label2.text = subtitle
        label2.sizeToFit()
        stackView.addArrangedSubview(label2)
    
        stackView.frame.size.width = max(label.frame.width, label2.frame.width)
        stackView.frame.size.height = label.frame.height + label2.frame.height
    
        stackView.accessibilityLabel = label.text?.appending(", \(label2.text!)")
    
        navigationItem.titleView = UIView(frame: stackView.frame)
        navigationItem.titleView?.addSubview(stackView)
    }
    

Теперь postNotification считывает ваш StackView только один раз в качестве первого элемента вашего экрана.

0
ответ дан XLE_22 2 March 2019 в 03:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: