Код для моего плеера находится здесь:
Код: Выделить всё
import Foundation
import AVFoundation
import SwiftUI
import Combine
class Player: NSObject, ObservableObject, AVAudioPlayerDelegate {
var player: AVAudioPlayer?
let objectWillChange = PassthroughSubject()
var isPlaying = false {
didSet {
objectWillChange.send()
}
}
@Published var currentTime: TimeInterval = 0
private var timer: AnyCancellable?
init(soundURL: URL) throws {
super.init()
if FileManager().fileExists(atPath: soundURL.path) {
do {
self.player = try AVAudioPlayer(contentsOf: soundURL)
player?.prepareToPlay()
self.player?.delegate = self
} catch {
throw Errors.FailedToPlayURL
}
} else {
print("URL not valid!")
}
}
func play() {
self.player?.play()
isPlaying = true
startTimer()
}
func pause() {
if isPlaying {
self.player?.pause()
isPlaying = false
stopTimer()
}
}
func stop() {
player?.stop()
isPlaying = false
resetPlayback()
}
func seek(to time: TimeInterval) {
player?.currentTime = time
currentTime = time
}
var duration: TimeInterval {
player?.duration ?? 0
}
private func startTimer() {
timer = Timer.publish(every: 0.1, on: .main, in: .common)
.autoconnect()
.sink { [weak self] _ in
guard let self = self else { return }
self.currentTime = self.player?.currentTime ?? 0
self.objectWillChange.send()
}
}
private func stopTimer() {
timer?.cancel()
timer = nil
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
if flag {
isPlaying = false
stopTimer()
resetPlayback()
}
}
private func resetPlayback() {
currentTime = 0
player?.currentTime = 0
objectWillChange.send()
}
}
Код: Выделить всё
import Foundation
import Combine
class PlayerViewModel: ObservableObject {
@Published var currentTime: TimeInterval = 0
var player: Player
private var cancellables = Set()
private var seekingSubject = PassthroughSubject()
init(player: Player) {
self.player = player
self.player.objectWillChange
.sink { [weak self] in
self?.currentTime = self?.player.currentTime ?? 0
}
.store(in: &cancellables)
seekingSubject
.throttle(for: .milliseconds(100), scheduler: RunLoop.main, latest: true)
.sink { [weak self] time in
self?.player.seek(to: time)
}
.store(in: &cancellables)
}
func play() {
player.play()
}
func pause() {
player.pause()
}
func stop() {
player.stop()
}
func seek(to time: TimeInterval) {
seekingSubject.send(time)
}
var duration: TimeInterval {
player.duration
}
}
Код: Выделить всё
import SwiftUI
import AVFoundation
struct PlayerView: View {
@State var soundURL: URL
@Binding var openedGroup: URL?
@State private var isOpened: Bool = false
@StateObject private var viewModel: PlayerViewModel
@State private var sliderValue: TimeInterval = 0
init(soundURL: URL, openedGroup: Binding) {
self._soundURL = State(initialValue: soundURL)
self._openedGroup = openedGroup
let player = try? Player(soundURL: soundURL)
self._viewModel = StateObject(wrappedValue: PlayerViewModel(player: player!))
}
var body: some View {
DisclosureGroup(isExpanded: Binding(
get: { self.openedGroup == self.soundURL },
set: { newValue in
if newValue {
self.openedGroup = self.soundURL
} else if self.openedGroup == self.soundURL {
self.openedGroup = nil
viewModel.stop()
}
}
)) {
VStack {
Slider(value: $sliderValue, in: 0...viewModel.duration, onEditingChanged: { editing in
if editing {
viewModel.pause()
} else {
viewModel.seek(to: sliderValue)
viewModel.play()
}
})
.padding()
.onChange(of: viewModel.currentTime) {
sliderValue = viewModel.currentTime
}
HStack {
Text(timeString(from: viewModel.currentTime))
Spacer()
Text(timeString(from: viewModel.duration))
}
.padding(.horizontal)
HStack {
Spacer()
Image(systemName: viewModel.player.isPlaying ? "pause.fill" : "play.fill")
.onTapGesture {
if viewModel.player.isPlaying {
viewModel.pause()
} else {
viewModel.play()
}
}
Spacer()
}
Spacer()
FileNameButtonView(soundURL: soundURL)
}
} label: {
Text(soundURL.lastPathComponent)
}
}
private func timeString(from timeInterval: TimeInterval) -> String {
let minutes = Int(timeInterval) / 60
let seconds = Int(timeInterval) % 60
return String(format: "%02d:%02d", minutes, seconds)
}
}
Спасибо за любую помощь!< /п>
Подробнее здесь: https://stackoverflow.com/questions/786 ... ice-record