2015-04-09 11 views
14

Ho visto alcuni post su come realizzare questo obiettivo in Objective-C ma non sono stato in grado di fare lo stesso tramite Swift.Avanzamento della riproduzione audio come UISlider in Swift

In particolare, non riesco a capire come implementare addPeriodicTimeObserverForInterval nel seguente.

var player : AVAudioPlayer! = nil 

@IBAction func playAudio(sender: AnyObject) { 
    playButton.selected = !(playButton.selected) 
    if playButton.selected { 
     let fileURL = NSURL(string: toPass) 
     player = AVAudioPlayer(contentsOfURL: fileURL, error: nil) 
     player.numberOfLoops = -1 // play indefinitely 
     player.prepareToPlay() 
     player.delegate = self 
     player.play() 
     startTime.text = "\(player.currentTime)" 
     endTime.text = NSString(format: "%.1f", player.duration) 
    } else { 
     player.stop() 
    } 

Qualsiasi assistenza sarebbe gradita.

+3

Se stai cercando di aggiornare il cursore mentre l'audio progredisce, mi piace usare un link di visualizzazione. CADisplayLink sincronizzerà la frequenza di aggiornamento dello schermo e richiamerà un selettore all'aggiornamento come un NSTimer ottimizzato per la grafica. Sono anche molto semplici da usare e ti impediscono di mettere in coda gli UIUpdate se il timer scatta più velocemente rispetto allo schermo. [Questi sono i documenti.] (Https://developer.apple.com/library/prerelease/ios/documentation/QuartzCore/Reference/CADisplayLink_ClassRef/index.html) È possibile impostare il selettore per visualizzare le chiamate di collegamento per controllare lo stato di avanzamento e aggiorna la posizione del tuo cursore. – Dare

+0

@Dare, grazie per quello. Non avevo idea che esistesse. – allocate

risposta

16

Grazie al suggerimento di Dare sopra, ecco come ho realizzato questo:

var updater : CADisplayLink! = nil 

@IBAction func playAudio(sender: AnyObject) { 
    playButton.selected = !(playButton.selected) 
    if playButton.selected { 
     updater = CADisplayLink(target: self, selector: Selector("trackAudio")) 
     updater.frameInterval = 1 
     updater.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSRunLoopCommonModes) 
     let fileURL = NSURL(string: toPass) 
     player = AVAudioPlayer(contentsOfURL: fileURL, error: nil) 
     player.numberOfLoops = -1 // play indefinitely 
     player.prepareToPlay() 
     player.delegate = self 
     player.play() 
     startTime.text = "\(player.currentTime)" 
     theProgressBar.minimumValue = 0 
     theProgressBar.maximumValue = 100 // Percentage 
    } else { 
     player.stop() 
    } 
} 

func trackAudio() { 
    var normalizedTime = Float(player.currentTime * 100.0/player.duration) 
    theProgressBar.value = normalizedTime 
} 

@IBAction func cancelClicked(sender: AnyObject) { 
    player.stop() 
    updater.invalidate() 
    dismissViewControllerAnimated(true, completion: nil) 

} 
+0

Una breve nota: assicurati di invalidare il link del display quando hai finito. Se non lo fai, sparerà all'infinito a prescindere dallo stato del lettore audio fino a quando qualcosa non si blocca. – Dare

+0

@Dare grazie per quello. Ingiustamente pensavo che chiudere il ViewController avrebbe risolto il problema (e mi sbagliavo molto). Codice aggiornato con la dichiarazione globale e la chiamata invalidate(). – allocate

7

Giusto per approfondire il mio commento precedente, questo è come ho implementato e sembra funzionare abbastanza bene. Le correzioni di Swift sono più che benvenute, sono ancora un ragazzo Obj-C per ora.

@IBAction func playAudio(sender: AnyObject) { 

    var playing = false 

    if let currentPlayer = player { 
     playing = player.playing; 
    }else{ 
     return; 
    } 

    if !playing { 
     let filePath = NSBundle.mainBundle().pathForResource("3e6129f2-8d6d-4cf4-a5ec-1b51b6c8e18b", ofType: "wav") 
     if let path = filePath{ 
      let fileURL = NSURL(string: path) 
      player = AVAudioPlayer(contentsOfURL: fileURL, error: nil) 
      player.numberOfLoops = -1 // play indefinitely 
      player.prepareToPlay() 
      player.delegate = self 
      player.play() 

      displayLink = CADisplayLink(target: self, selector: ("updateSliderProgress")) 
      displayLink.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode!) 
     } 

    } else { 
     player.stop() 
     displayLink.invalidate() 
    } 
} 

func updateSliderProgress(){ 
    var progress = player.currentTime/player.duration 
    timeSlider.setValue(Float(progress), animated: false) 
} 

* a impostare intervallo di scorrimento temporale tra 0 e 1 su un storyboard

4

Specificamente per Swift ero in grado di gestire in questo modo:

  1. I impostare il valore massimo della scrubSlider alla durata del file musicale (.mp3) che è stato caricato in questo metodo

    override func viewDidLoad() { 
        super.viewDidLoad() 
    
        do { 
    
         try player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("bach", ofType: "mp3")!)) 
    
         scrubSlider.maximumValue = Float(player.duration) 
    
        } catch { 
         //Error 
        } 
    
        _ = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(ViewController.updateScrubSlider), userInfo: nil, repeats: true) 
    
    } 
    
  2. Il lettore I è stato impostato per riprodurre la musica al momento impostato dal valore dello scrubber.

    @IBAction func scrub(sender: AnyObject) { 
    
        player.currentTime = NSTimeInterval(scrubSlider.value) 
    
    } 
    
0

Sintassi ora è cambiato a Swift 4:

updater = CADisplayLink(target: self, selector: #selector(self.trackAudio)) 
updater.preferredFramesPerSecond = 1 
updater.add(to: RunLoop.current, forMode: RunLoopMode.commonModes) 

E la funzione (ho già impostato il progressSlider.maxValue a player.duration):

@objc func trackAudio() { 
    progressSlider.value = Float(player!.currentTime) 
} 
Problemi correlati