Play audio on touch down action of button in swiftui

Viewed 859

I want to play a audio when a button is touched down(as soon as it is clicked) not on touch release in SwiftUI. How can I implement this?

My code looks something like this:

struct PressedButtonStyle: ButtonStyle {
    let touchDown: () -> ()
    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .foregroundColor(configuration.isPressed ? Color.gray : Color.blue)
            .background(configuration.isPressed ? self.handlePressed() : Color.clear)
    }

    private func handlePressed() -> Color {
        touchDown()
        return Color.clear
    }
}
 

struct DemoPressedButton: View {
    @State var audioPlayer: AVAudioPlayer!

    var body: some View {
        Button("Demo") {
            print(">> tap up")
        }
        .buttonStyle(PressedButtonStyle {
            print(">> tap down")
            let sound = Bundle.main.path(forResource: "filename", ofType: "wav")
            self.audioPlayer = try! AVAudioPlayer(contentsOf: URL(fileURLWithPath: sound!)) // receives warning about changing state while updating view
            self.audioPlayer.play() // breaks 
        })
    }
}

The code breaks when calling self.audioPlayer.play() . The custom touchdown code is from this link: SwiftUI button action as soon as button is clicked not on click release

1 Answers

Here is a demo of possible approach. Tested with Xcode 11.4 / iOS 13.4

Note: you should keep reference to AVAudioPlayer while it is playing and better track its state, so this is more appropriate to do in some helper class (like view model)

class PlayViewModel {
    private var audioPlayer: AVAudioPlayer!
    func play() {
        let sound = Bundle.main.path(forResource: "filename", ofType: "wav")
        self.audioPlayer = try! AVAudioPlayer(contentsOf: URL(fileURLWithPath: sound!))
        self.audioPlayer.play()
    }
}

struct DemoPressedButton: View {
    let vm = PlayViewModel()

    var body: some View {
        Button("Demo") {
            print(">> tap up")
        }
        .buttonStyle(PressedButtonStyle {
            print(">> tap down")
            self.vm.play()
        })
    }
}
Related