create a box that if tapped it plays a sound, double press it to display a volume slider to change its volume, use swiftui in swift

Here's a sample code snippet that demonstrates how to create a view that plays a sound on tap and displays a volume slider on double-tap:

main.swift
import SwiftUI
import AVFoundation

struct SoundView: View {
    let soundName: String
    let volume: Binding<Double>
    
    private var player: AVAudioPlayer?
    
    @State private var tapCount = 0
    
    var body: some View {
        let tap = TapGesture(count: tapCount == 1 ? 2 : 1)
            .onEnded { _ in
                if self.tapCount == 1 {
                    self.showVolumeSlider()
                } else {
                    self.playSound()
                }
            }
        
        return Rectangle()
            .fill(Color.blue)
            .frame(width: 100, height: 100)
            .highPriorityGesture(tap)
    }
    
    private func playSound() {
        guard let url = Bundle.main.url(forResource: soundName, withExtension: "mp3"), let volumeValue = try? volume.wrappedValue, volumeValue > 0 else {
            return
        }
        
        do {
            player = try AVAudioPlayer(contentsOf: url)
            player?.volume = Float(volumeValue)
            player?.play()
        } catch {
            print("Unable to play sound: \(error.localizedDescription)")
        }
    }
    
    private func showVolumeSlider() {
        let volumeTap = TapGesture(count: 1)
            .onEnded { _ in
                self.tapCount = 1
            }
        
        let drag = DragGesture(minimumDistance: 0)
            .onChanged { value in
                let delta = value.translation.height / 100.0
                var newVolume = volume.wrappedValue - delta
                newVolume = max(min(newVolume, 1), 0)
                volume.wrappedValue = newVolume
            }
            .onEnded { _ in
                self.tapCount = 0
            }
        
        let longPress = LongPressGesture(minimumDuration: 1)
            .onEnded { _ in
                withAnimation {
                    self.tapCount = 0
                }
            }
        
        let gesture = longPress.sequenced(before: volumeTap).simultaneously(with: drag)
        
        Rectangle()
            .fill(Color.blue)
            .frame(width: 100, height: 200)
            .highPriorityGesture(gesture)
            .overlay(
                Slider(value: volume)
                    .padding()
            )
    }
}

struct ContentView: View {
    @State private var volume = 1.0
    
    var body: some View {
        SoundView(soundName: "sound", volume: $volume)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
2532 chars
92 lines

Explanation:

The SoundView struct represents the view for a single sound. It takes two parameters: the name of the sound file to play, and a binding to the volume value.

The view uses AVAudioPlayer to play the sound file when tapped. When double-tapped, it displays a rectangle with a volume slider. The slider's binding is the same as the one passed to the view, so changing the slider value updates the parent view's state.

To implement this behavior, we use gesture recognizers to detect taps and double-taps. The tapCount state variable keeps track of how many times the view has been tapped. If it is 1, the next tap should display the volume slider.

The showVolumeSlider() function creates a new view with a blue rectangle and a volume slider. It combines multiple gesture recognizers so that the user can drag the slider to change the volume, tap the top half of the rectangle to dismiss the slider, or long-press to reset the tap count to zero.

Finally, the ContentView struct contains a single sound view, passing in the sound name and a binding to the volume.

gistlibby LogSnag