swiftUI:AVAudioPlayer 的奇怪行为

swiftUI: strange behavior with AVAudioPlayer

我想为一个特别为盲人设计的应用程序构建一个引导屏幕。所以页面应该播放一个小的 mp3 文件。

但是,如果我实现附件中的代码,这种行为对我来说似乎很奇怪。 如果我从第 1 页更改为第 2 页,第一个 mp3 文件停止,第二个开始。如我所愿。 如果我进一步转到第 3 页,第 2 页的 mp3 将继续,第 3 页的 mp3 也会开始。两种声音相互叠加。

不明白为什么.onDisappear有时候好像没有被执行...

有时 Xcode 抛出一个 线程 1:致命错误:在隐式展开可选值时意外发现 nil --> 根据 jnpdx 的提示解决了 - 谢谢

在 .onDisappear 部分。

这是我的问题的一个小视频。看到视频可能更容易理解我的意思...

https://www.eckeonline.de/RPReplay_Final1643832563.MP4

import SwiftUI
import AVKit

struct ManualView: View {
    
    let titleText = ["Willkommen", "Kategorien", "Neuigkeiten","Beispiel", "star.fill","cart", "star.fill","cart", "star.fill"]
    let titleImage = ["character.book.closed.fill", "list.dash", "megaphone.fill","moon.zzz.fill", "star.fill","cart", "star.fill","cart", "star.fill"]
    let soundFile = ["WelcomePage","CategoryPage", "NewsPage", "ExamplePage","WelcomePage", "WelcomePage", "WelcomePage","WelcomePage", "WelcomePage", "WelcomePage"]
    
    
    var body: some View {
        TabView {
            
            
            ForEach(0..<4) { value in
                OnboardingPage(soundFile: soundFile[value], title: titleText[value] , image: titleImage[value])
                    .tag(value)
            }
        }
        .tabViewStyle(.page(indexDisplayMode: .always))
    }
}



struct OnboardingPage: View {
    
    var soundFile: String
    let title: String
    let image: String
    @State var audioPlayer: AVAudioPlayer!
    
    var body: some View {
        
        VStack(spacing: 25) {
            
           //  PlayMP3View(soundFile: soundFile)
            Text(title)
                .padding(.horizontal, 20)
                .font(.system(size: 200))
                .lineLimit(1)
                .minimumScaleFactor(0.25)
            //  .font(.largeTitle)
                .foregroundColor(.primary)
                .accessibility(hidden: true)
            Spacer()
            Image(systemName: image)
                .font(.system(size: 200))
                .foregroundColor(.primary)
                .accessibility(hidden: true)
            
            Spacer()
        }
        .onAppear() {

            if let sound = Bundle.main.path(forResource: soundFile, ofType: "mp3") {
                self.audioPlayer = try! AVAudioPlayer(contentsOf: URL(fileURLWithPath: sound))
                self.audioPlayer.play()
            }
        }
        .onDisappear() {
            self.audioPlayer.pause()
        }
    }
    
}

任何时候你使用 !,如果该值最终为零,你就有崩溃的风险。这里最简单的解决方案是使 audioPlayer 成为可选的。

其次,关于重叠音频的问题,标签可能没有真正卸载(也没有调用 onDisappear)。除了使用 onAppearonDisappear,您还可以使用 onChange 并观察 TabView 的选择(为简洁起见,删除了一些 AudioPlayer 代码):

struct ManualView: View {
    
    let titleText = ["Willkommen", "Kategorien", "Neuigkeiten","Beispiel", "star.fill","cart", "star.fill","cart", "star.fill"]
    let titleImage = ["character.book.closed.fill", "list.dash", "megaphone.fill","moon.zzz.fill", "star.fill","cart", "star.fill","cart", "star.fill"]
    let soundFile = ["WelcomePage","CategoryPage", "NewsPage", "ExamplePage","WelcomePage", "WelcomePage", "WelcomePage","WelcomePage", "WelcomePage", "WelcomePage"]
    
    @State private var selection = -1
    
    var body: some View {
        TabView(selection: $selection) {
            ForEach(0..<4) { value in
                OnboardingPage(soundFile: soundFile[value],
                               title: titleText[value],
                               image: titleImage[value],
                               isActive: selection == value)
                    .tag(value)
            }
        }
        .tabViewStyle(.page(indexDisplayMode: .always))
        .onAppear {
            selection = 0 //trick to get onChange triggered on first screen
        }
    }
}

struct OnboardingPage: View {
    
    var soundFile: String
    let title: String
    let image: String
    var isActive : Bool
    
    @State var audioPlayer: AVAudioPlayer?
    
    var body: some View {
        VStack(spacing: 25) {
            Text(title)
        }
        .onChange(of: isActive) { newValue in
            if newValue {
                print("Play \(title)")
                audioPlayer?.play()
            } else {
                print("Stop \(title)")
                audioPlayer?.stop()
            }
        }
    }
    
}