我有一个音频文件(其长度 > 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()
不会被调用,因此视图仍然存在并且应该一直播放音频直到结束。
答案 0 :(得分:2)
我要解决的第一件事是将您的 @ObservedObject 包装器更改为 @StateObject 包装器。如果视图在播放声音的过程中的某个时刻更新,这将防止解除分配。让我知道这是否有效...