几秒钟后音频文件停止播放

时间:2020-12-21 03:46:29

标签: ios swift audio swiftui

我有一个音频文件(其长度 > 60 秒),它在我的 2 个视图(如下所示)的 onAppear() 方法中播放。

当我在 GamesList.swift 中播放音频文件时,它会播放整个长度。但是如果我在 ActiveGame.swift 中玩它,它会在几秒钟后中断。我不知道这些视图之间有什么区别,但对后者不起作用。

GamesList.swift(成功播放整个文件)

struct GamesList: View {
    @State var showCreateGameSheet = false
    let diameter: CGFloat = 25.0
    @State var games: [Game] = []
    @EnvironmentObject var gameManager: GameManager
    @State var stayPressed = false
    @ObservedObject var soundManager = SoundManager()
    
    var body: some View {
        NavigationView {
            VStack {
                // Create game
                HStack {
                    Spacer()
                    Button(action: { showCreateGameSheet.toggle() }){
                        Text("+ Create game")
                            .font(.custom("Seravek-Medium", size: 20))
                            .foregroundColor(Color.white)
                    }.buttonStyle(GTButton(color: Color("primary"), stayPressed: stayPressed))
                }
                .frame(
                    maxWidth: .infinity,
                    maxHeight: 80,
                    alignment: .top
                )
                ScrollView(.vertical, showsIndicators: true) {
                    ForEach(self.games.sorted(by: { $0.players.count > $1.players.count }), id: \.id){ game in
                        AvailableGame(game: game)
                            .environmentObject(gameManager)
                        Divider()
                    }
                } // Scrollview
            } // VStack
            .padding(25)
//            .navigationBarHidden(true)
            .navigationTitle("Games").foregroundColor(Color("primary"))
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    HStack {
                        Button(action: {
                            self.games = []
                            Database().fetchGames(){ game in
                                if let game = game {
                                    self.games.append(contentsOf: game)
                                }
                            }
                        }){
                            Image(systemName: "arrow.clockwise")
                                .resizable()
                                .aspectRatio(contentMode: .fill)
                                .frame(width: diameter, height: diameter)
                                .foregroundColor(.gray)
                                .padding(.horizontal, 30)
                        }
                        NavigationLink(destination: Settings()){
                            Image("settings")
                                .resizable()
                                .aspectRatio(contentMode: .fill)
                                .frame(width: diameter, height: diameter)
                                .foregroundColor(.gray)
                        }
                    }.padding(.top, 10)
                }
            }
        } // NavigationView
        .sheet(isPresented: $showCreateGameSheet, onDismiss: {  }) {
            CreateGame()
                .environmentObject(self.gameManager)
        }
        .onAppear {
            self.soundManager.playSound(name: "duringGame.aiff", loop: false)
            Database().fetchGames(){ game in
                if let game = game {
                    self.games.append(contentsOf: game)
                }
            }
        }
        .accentColor(Color("primary"))
    }
} 

ActiveGame.swift(几秒钟后中断)

struct ActiveGame: View {
    @EnvironmentObject var gameManager: GameManager
    let diameter: CGFloat = 50.0
    @State var image = Image(systemName: "rectangle")
    @ObservedObject var soundManager = SoundManager()
    
    var body: some View {
        ZStack {
            PlayerBlock()
                .padding(.horizontal, 10)
                .position(x: (UIScreen.main.bounds.size.width / 2), y: 30)
            VStack(spacing: 15) {
                ZStack { // Question and answers block
                    if self.gameManager.game?.question == nil {
                        ProgressView()
                            .progressViewStyle(CircularProgressViewStyle())
    //                        .scaleEffect(2.0, anchor: .center)
                    }
                    VStack {
                        Text("Round \(self.gameManager.game?.currentRound ?? 1)")
                            .font(.system(size: 22))
                            .foregroundColor(Color.gray)
                            .multilineTextAlignment(.center)
                            .padding(.bottom, 5)
                        // QUESTION
                        Text("\(self.gameManager.game?.question?.text ?? "")")
                            .font(.custom("Seravek-Bold", size: 28))
                            .foregroundColor(Color("primary"))
                            .multilineTextAlignment(.center)
                            .padding(.horizontal, 30)
                            .fixedSize(horizontal: false, vertical: true)
                        // QUESTION IMAGE
                        (self.gameManager.cachedQuestionImage ?? Image(systemName: "rectangle"))
                            .resizable()
                            .scaledToFit()
                            .aspectRatio(contentMode: .fill)
                            .frame(height: 200)
            //                .frame(width: 280, height: 180)
                            .clipShape(RoundedRectangle(cornerRadius: 10))
                            .padding(.horizontal, 30)
                            .opacity(self.gameManager.cachedQuestionImage == nil ? 0.0 : 1.0) // Hide image while it is loading
                            .onTapGesture {
                                self.gameManager.deleteGame()
                            }
                        // ANSWERS
                        if 1 > 0 {
                            MultipleChoice(options: self.gameManager.game?.question?.options ?? [])
                        } else if (self.gameManager.game?.question?.type == QuestionType.textInput){
                            TextInput()
                        }
                    }.opacity(self.gameManager.game?.question == nil ? 0.0 : 1.0)
                    .disabled(self.gameManager.game?.question == nil)
                    // Hide and disable the question block when the next question is loading
                }
            }.transition(.fadeTransition)
            .padding(.top, 40)
        }
        .onAppear {
            print("onAppear")
            self.soundManager.playSound(name: "duringGame.aiff", loop: false)
        }
        .onDisappear {
            print("onDisappear")
            self.soundManager.player?.stop()
        }
    }
}

SoundManager.swift - 这是播放音频的视图模型

class SoundManager: ObservableObject {
    @Published var player: AVAudioPlayer?
    
    func playSound(name: String, loop: Bool){
        let path = Bundle.main.path(forResource: name, ofType: nil)
        let url = URL(fileURLWithPath: path!)
        print("Play URL from name: \(name)")
        do {
            player = try AVAudioPlayer(contentsOf: url)
            if loop {
                player?.numberOfLoops = -1 // Loop forever
            }
            player?.play()
            print("Played sound") // Both views print this when the audio plays
        } catch {
            print("Error playing \(name) sound")
        }
    }
}

知道是什么问题吗? SoundManager 在两个视图中都作为观察对象存储,因此只要视图仍然存在,它就应该存在。在 ActiveGame 中,onDisappear() 不会被调用,因此视图仍然存在并且应该一直播放音频直到结束。

1 个答案:

答案 0 :(得分:2)

我要解决的第一件事是将您的 @ObservedObject 包装器更改为 @StateObject 包装器。如果视图在播放声音的过程中的某个时刻更新,这将防止解除分配。让我知道这是否有效...