UISlider控制AVAudioPlayer

时间:2010-04-16 17:06:33

标签: iphone objective-c avaudioplayer uislider

我正在尝试在我的应用中实现一个小功能。我目前正在播放声音作为AVAudioPlayers并且工作正常。我想补充的是用UISlider控制声音的位置(currentTime):有一种简单的方法吗?

我查看了一个Apple项目,但它非常混乱....你有样品或建议吗?

感谢大家提前

6 个答案:

答案 0 :(得分:48)

应该不是问题 - 只需将滑块设置为连续,并在加载声音文件后将最大值设置为播放器的持续时间。

修改

我刚刚做了这件事,它对我有用......

- (IBAction)slide {
    player.currentTime = slider.value;
}

- (void)updateTime:(NSTimer *)timer {
    slider.value = player.currentTime;
}

- (IBAction)play:(id)sender {
    NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"sound.caf" ofType:nil]];
    NSError *error;
    player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
    if (!player) NSLog(@"Error: %@", error);
    [player prepareToPlay];
    slider.maximumValue = [player duration];
    slider.value = 0.0;

    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];  
    [player play];
}

滑块在IB中配置,以及开始播放的按钮。

Swift 3.0更新:

var player: AVAudioPlayer!
var sliderr: UISlider!

@IBAction func play(_ sender: Any) {
    var url = URL(fileURLWithPath: Bundle.main.path(forResource: "sound.caf", ofType: nil)!)
    var error: Error?
    do {
        player = try AVAudioPlayer(contentsOf: url)
    }
    catch let error {
    }
    if player == nil {
        print("Error: \(error)")
    }
    player.prepareToPlay()
    sliderr.maximumValue = Float(player.duration)
    sliderr.value = 0.0
    Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.updateTime), userInfo: nil, repeats: true)
    player.play()
}

func updateTime(_ timer: Timer) {
    sliderr.value = Float(player.currentTime)
}

@IBAction func slide(_ slider: UISlider) {
    player.currentTime = TimeInterval(slider.value)
}

答案 1 :(得分:16)

要延伸paull的答案,你可以将滑块设置为连续的音频播放器的duration的最大值,然后添加你的一些对象(可能是视图控制器)作为滑块的目标UIControlEventValueChanged事件;当您收到操作消息时,您可以将AVAudioPlayer的{​​{1}}属性设置为滑块的值。 您可能还想使用currentTime更新滑块的值,因为音频播放器会播放; NSTimer是最简单的方法。

答案 2 :(得分:7)

我需要调整上面的答案才能让它发挥作用。问题是使用

slider.maximumValue = [player duration];
slider.value = player.currentTime;
player.currentTime = slider.value;

不能正常工作,因为滑块需要浮动,玩家currentTime和dration返回CMTime。为了使这些工作,我改编他们阅读:

slider.maximumValue = CMTimeGetSeconds([player duration]);
slider.value = CMTimeGetSeconds(player.currentTime);
player.currentTime = CMTimeMakeWithSeconds((int)slider.value,1);

答案 3 :(得分:0)

我在播放音频文件,显示开始/结束时间以及使用UISlider控制歌曲时遇到的问题。

  1. 如果不将音频下载到temp文件夹中,则不能直接播放音频。
  2. UISlider在较低的iOS版本(即12.4 / 13.1)中的主线程上崩溃了
  3. UISlider的平滑滚动。
  4. 计算和更新歌曲的开始/结束时间。

此答案需要一些编辑,但毫无疑问会起作用。

  //UISlider init
  lazy var slider: UISlider = {
    let progress = UISlider()
    progress.minimumValue = 0.0
    progress.maximumValue = 100.0
    progress.tintColor = UIColor.init(named: "ApplicationColor")
    return progress

}()

 var audioPlayer : AVAudioPlayer?
   //First I've downloaded the audio and then playing it.
  override func viewWillAppear(_ animated: Bool) {

    super.viewWillAppear(animated)


    timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(trackAudio), userInfo: nil, repeats: true)

    if let audioURLString = audioURL{
        let urlstring = URL(string: audioURLString)!
        downloadFromURL(url: urlstring) { (localURL, response, error) in
            if let localURL = localURL{
                self.playAudioFile(url: localURL)
            }

        }
    }
}

 override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    stopTimer()
}


// Stop TimeInterval After View disappear
func stopTimer() {
    if timer != nil {
        timer?.invalidate()
        audioPlayer?.stop()
        audioPlayer = nil
        timer = nil
    }
}
@objc func sliderSelected(_ sender : UISlider){

    if audioPlayer != nil{
        if !isPlaying{
            self.audioPlayer?.play()
            playButton.setImage(UIImage.init(named: "AudioPause"), for: .normal)
            isPlaying = true
        }else{
            self.audioPlayer?.currentTime = TimeInterval(Float(sender.value) * Float(self.audioPlayer!.duration) / 100.0)
            if (sender.value / 100.0 == 1.0){

                //Do something if audio ends while dragging the UISlider.

            }
        }


    }

}
 func downloadFromURL(url:URL,completion: @escaping((_ downladedURL: URL?,_ response :URLResponse?,_ error: Error?) -> Void)){
    var downloadTask:URLSessionDownloadTask
    downloadTask = URLSession.shared.downloadTask(with: url) {(URL, response, error) in
        if let url = URL{
            completion(url,nil,nil)
        }else if let response = response{
            completion(nil,response,nil)
        }
        if let error = error{
            completion(nil,nil,error)
        }


    }

    downloadTask.resume()
}


func playAudioFile(url:URL){
    do{

        self.audioPlayer = try AVAudioPlayer(contentsOf: url)
        self.audioPlayer?.prepareToPlay()
        self.audioPlayer?.delegate = self
        self.audioPlayer?.play()

   let audioDuration = audioPlayer?.duration
        let audioDurationSeconds = audioDuration
        minutes = Int(audioDurationSeconds!/60);
        seconds =  Int(audioDurationSeconds!.truncatingRemainder(dividingBy: 60))
    } catch{
        print("AVAudioPlayer init failed")
    }
}

@objc func trackAudio() {

    if audioPlayer != nil{
        DispatchQueue.main.async {
            print("HI")
            let normalizedTime = Float(self.audioPlayer!.currentTime * 100.0 / self.audioPlayer!.duration)
            self.slider.setValue(normalizedTime, animated: true)
            let currentTime = self.audioPlayer?.currentTime
            self.currentMinutes = Int(currentTime!/60);
            self.currentSeconds =  Int(currentTime!.truncatingRemainder(dividingBy: 60))
            self.startTimeLabel.text =  String(format: "%02i:%02i", self.currentMinutes, self.currentSeconds)
            self.endTimeLabel.text = String(format: "%02i:%02i", self.minutes, self.seconds)
        }
    }





}

答案 4 :(得分:0)

如果有人在 UI 滑块上寻找简单的 TouchDown 和 TouchUp,那么结果很简单:

    slider.addTarget(self, action: #selector(changeVlaue(_:)), for: .valueChanged)
    slider.addTarget(self, action: #selector(sliderTapped), for: .touchDown)
    slider.addTarget(self, action: #selector(sliderUntouched), for: .touchUpInside)

答案 5 :(得分:0)

如果您在拖动之间不需要任何数据,那么您只需设置: mySlider.isContinuous = false

否则,请尝试以下代码来控制触摸的每个阶段。

// audio slider bar
    private lazy var slider: UISlider = {
        let slider = UISlider()
        slider.translatesAutoresizingMaskIntoConstraints = false
        slider.minimumTrackTintColor = .red
        slider.maximumTrackTintColor = .white
        slider.setThumbImage(UIImage(named: "sliderThumb"), for: .normal)
        
        slider.addTarget(self, action: #selector(onSliderValChanged(slider:event:)), for: .valueChanged)
//        slider.isContinuous = false
        
        return slider
    }()
    
    @objc func onSliderValChanged(slider: UISlider, event: UIEvent) {
        guard let player = AudioPlayer.shared.player else { return }
        if let touchEvent = event.allTouches?.first {
            switch touchEvent.phase {
            case .began:
                // handle drag began
                // I would stop the timer when drag begin
                timer.invalidate()
            case .moved:
                // handle drag moved
                // Update label's text for current playing time
            case .ended:
                // update the player's currTime and re-create the timer when drag is done.
                player.currentTime = TimeInterval(slider.value)
                timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTime(_:)), userInfo: nil, repeats: true)
            default:
                break
            }
        }
    }