Я создал игровую инфраструктуру когда-то, чтобы работать на Android и Desktop, часть рабочего стола, которая обрабатывает звук, может быть использована как вдохновение для того, что вам нужно.
Вот код для справки .
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();
}
}
}
Причина, по которой вы читаете заголовок во второй раз, кроется в вашем коде.
В вашем 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, естественно, не догадаются, что перед заголовком присутствует другой элемент:
Технически, ваша проблема решена с помощью кода выше , но концептуально я предлагаю переупорядочить ваши элементы, если вы все еще хотите представить заголовок в качестве первого элемента.
==========
РЕДАКТИРОВАТЬ (обходной путь)
О технической проблеме, вы правы в своем комментарии, это решение выше работает благодаря чтению этикетки VoiceOver.
Я отправил решение в вашу ветку git, которую вы дали в своем первоначальном посте.
Проблема касается UIStackView, который я не могу объяснить в этом случае и не могу решить ни как есть.
Чтобы достичь вашей цели, я создал UIAccessibilityELement
для стека, который может быть идеально доступен и доступен без двойного чтения с пост-уведомлением.
Я сделал это, потому что я не мог программно получить новый размер стека, когда надписи находятся ... может быть, создание подкласса UIStackView и получение его layoutSubviews
может быть уловкой?
Это решение должен работать как обходной путь , но я не знаю причину, почему это поведение появляется с UIStackview.
==========
РЕДАКТИРОВАТЬ (решение)
Проблема в том, как titleView
из navigationItem
создано. Лучший способ достичь вашей цели - это:
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
}()
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 только один раз в качестве первого элемента вашего экрана.