SwiftUI — Аудио продолжает воспроизводиться при смене представлений

#swift #swiftui #avaudioplayer

#swift #swiftui #avaudioplayer

Вопрос:

У меня возникла проблема, поскольку я обновил свой Xcode… Если пользователь не нажмет паузу в представлении звука. Мой аудиоплеер продолжает воспроизводиться, когда пользователь меняет представления, однако я бы хотел, чтобы звук останавливался, когда пользователь выходит из режима просмотра проигрывателя (ItemDetail) (например, когда пользователь переходит к просмотру содержимого)

Ранее я использовал приведенное ниже в начале представления содержимого, и это сработало, но теперь это не так:

init() { sounds.pauseSounds() }

Я также пробовал это (что тоже не сработало):

}// end of navigation view .onAppear{sounds.pauseSounds()}

Это мой класс звуков:

 class Sounds:ObservableObject {

   var player:AVAudioPlayer?
//   let shared = Sounds()
    

   func playSounds(soundfile: String) {

      if let path = Bundle.main.path(forResource: soundfile, ofType: nil){

          do{

              player = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
            
              player?.prepareToPlay()
              player?.play()

          }catch {
              print("Error can't find sound file or something's not working with the sounds model")
          }
      }
   }
 
// This method used to work at pausing the sound (pre-update)
     func pauseSounds() {
        player?.pause()
    }
    
  
  // over rides the sound defaulting to bluetooth speaker only and sends it to phone speaker if bluetooth is not available (headset/speaker) however if it is available it'll play through that.

   func overRideAudio() {
        
        let audioSession = AVAudioSession.sharedInstance()

           do {
               try audioSession.setCategory(AVAudioSession.Category.playback, mode: .spokenAudio, options: .defaultToSpeaker)
               try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
           } catch {
               print("error.")
           }
           
    }
    
}
 

И это представление проигрывателя:

 struct ItemDetail: View {
    
    @State var isPlaying = false
    
    var item : SessionsItem
    @ObservedObject var soundsPlayer = Sounds()
    
    var body: some View {
        
            HStack {
                
                Button(action: {
                    if self.isPlaying{
                        //  Sounds.player?.pause()
                        self.isPlaying = false
                        soundsPlayer.pauseSounds()
                    }
                    else{
                        self.isPlaying = true
                        soundsPlayer.playSounds(soundfile: "(self.item.name).mp3")
                        soundsPlayer.overRideAudio()
                        
                    }
 

Ответ №1:

Вы создаете другой экземпляр звука, поэтому у вас нет доступа к открытому экземпляру audio’а. Вы можете создать класс Sounds как отдельный экземпляр или передать экземпляр Sounds в detail. Не создавайте новый.

Комментарии:

1. Это сработало (спасибо !!) — в том смысле, что теперь я могу вернуться и приостановить его. Однако, когда я использую: //init() { // Sounds.pauseSounds() // } в представлении содержимого, чтобы отключить все аудио — каким-то образом ничего не воспроизводится, даже в представлениях, в которых я хочу, чтобы аудио воспроизводилось… Почему это должно быть? И есть ли у вас какие-либо другие предложения по подавлению звука в представлении содержимого другим способом?

Ответ №2:

onAppear это ненадежный способ выполнения подобной SwiftUI работы, требующий большой предварительной загрузки (это очень очевидно для a List ). Это зависит от устройства и его возможностей. Иногда экраны «появляются», когда они предварительно загружены, не обязательно, когда пользователь смотрит на него.

На List примере ниже вы можете увидеть это в симуляторе.

На iPhone 8 я получаю 12 onAppear print инструкций в консоли при первоначальной загрузке, а на iPhone 12 Pro Max я получаю 17 print инструкций.

Если ячейки более / менее сложные, вы получаете больше / меньше операторов соответственно. Вы должны найти другой способ сделать это.

 import SwiftUI
struct ListCellAppear: View {
    var idx: Int
    var body: some View {
        HStack{
            Text(idx.description)
            Image(systemName: "checkmark")
        }.onAppear(){
            print(idx.description)
        }
    }
}
struct ListAppear: View {
    var body: some View {
        List(0..<1000){ idx in
            ListCellAppear(idx: idx).frame(height: 300)
        }
    }
}

struct ListAppear_Previews: PreviewProvider {
    static var previews: some View {
        ListAppear()
    }
}
 

Комментарии:

1. Спасибо, это было на самом деле тоже очень информативно! Если бы я мог принять оба ответа, я бы так и сделал.