У меня есть аудиофайл (его длина> 60 секунд), который воспроизводится методом onAppear()
2 моих просмотров (показан ниже).
Когда я воспроизвожу аудиофайл в 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()
не вызывается, поэтому представление все еще живо и должно воспроизводить звук до конца.
Первое, что я бы исправил, — это изменение вашей оболочки @ObservedObject на оболочку @StateObject. Это предотвратит освобождение, если представление обновится в какой-то момент в процессе воспроизведения звука. Дай мне знать, если это работает...
Без проблем! Рад, что смог помочь
Вау, это сработало... Большое спасибо. Таким образом, звуковой менеджер должен был быть уничтожен/освобожден при обновлении представления.