I want to make a metronome app, but I think it’s been framed to some extent using GPT. By the way, no matter how many times I ask GPT, GPT won’t solve it and there’s no problem with the code. But my metronome still doesn’t work. Can anyone help?
This is my Metronome Model file.
struct Metronome {
var tempo: Int
}
and this is my Metronome View Model file
import Foundation
import Combine
import AVFoundation
class MetronomeViewModel: ObservableObject {
static let minTempo: Int = 1
static let maxTempo: Int = 300
static var defaultTempo: Int = 60
@Published var tempo: Int = defaultTempo {
didSet {
tempo = max(Self.minTempo, min(Self.maxTempo, tempo)) // Tempo를 1~300
restartTimer() // 변경된 Tempo로 BPM 조정
}
}
@Published var activeCircle: Int = 3
private var timer: Timer?
private var audioPlayer: AVAudioPlayer?
init() {
startTimer()
}
// Start metronome function
func startTimer() {
timer?.invalidate()
let interval = 60.0 / Double(tempo)
timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in
self?.updateMetronome()
}
}
// Restart metronome function
func restartTimer() {
timer?.invalidate()
startTimer()
}
private func updateMetronome() {
DispatchQueue.main.async { [weak self] in
self?.activeCircle = (self?.activeCircle ?? 0 + 1) % 4
self?.playSound()
}
}
// sound play funcion
func playSound() {
guard let url = Bundle.main.url(forResource: "Metronome_Demo_1", withExtension: "wav") else { return }
audioPlayer?.stop()
audioPlayer = nil
do {
audioPlayer = try AVAudioPlayer(contentsOf: url) // load sound file
audioPlayer?.prepareToPlay()
audioPlayer?.play()
} catch {
print("Can't play sounds. error : (error)")
}
}
deinit {
timer?.invalidate()
}
func increaseTempo() {
tempo += 1
}
func decreaseTempo() {
tempo -= 1
}
static func setDefaultTempo(_ newTempo: Int) { // setDefaultTempo if user want.
defaultTempo = newTempo
}
}
and this is my Metronome View file.
import SwiftUI
struct MetronomeView: View {
@ObservedObject var viewModel: MetronomeViewModel
var body: some View {
GeometryReader { geometry in
VStack {
Spacer()
// metronome circle draw
HStack {
ForEach(0..<4) { index in
Circle()
.fill(index == (viewModel.activeCircle + 1) % 4 ? Color.basecolor : Color.gray)
.frame(width: 50, height: 50)
.animation(.easeInOut(duration: 0.1), value: viewModel.activeCircle)
.padding(15)
}
}
Spacer()
VStack {
// tempo control area
HStack {
// "-" button
Button(action: {
viewModel.decreaseTempo()
}) {
Image(systemName: "minus")
.foregroundColor(.white)
.padding()
}
// Now tempo info
Text("(viewModel.tempo)")
.font(.largeTitle)
.foregroundColor(.white)
.padding()
// "+" button
Button(action: {
viewModel.increaseTempo()
}) {
Image(systemName: "plus")
.foregroundColor(.white)
.padding()
}
}
.padding()
.frame(maxWidth: .infinity)
.gesture(DragGesture().onChanged { value in
let translation = value.translation.width
if abs(translation) > 10 {
if translation > 10 {
viewModel.increaseTempo()
} else if translation < -10 {
viewModel.decreaseTempo()
}
}
})
// start practice button
Button(action: {
viewModel.startTimer()
}) {
Image(systemName: "play.fill")
.resizable()
.frame(width: 40, height: 40)
.padding(30)
.foregroundColor(.basecolor)
.background(Color.white)
.clipShape(Circle())
.shadow(radius: 3)
}
.padding()
}
.frame(maxWidth: .infinity, maxHeight: 300)
.background(Color.basecolor)
}
.onAppear {
viewModel.startTimer() // ensure the timer starts when the view appears
}
}
}
}
struct MetronomeView_Previews: PreviewProvider {
static var previews: some View {
MetronomeView(viewModel: MetronomeViewModel())
}
}
Anyone please help me guys
Recognized by Mobile Development Collective
New contributor
MINKONG _ is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.