ios – How to correctly implement UISlider in the AudioPlayerViewController for changing value of progress?

0
50

[ad_1]

This is the AudioPlayerViewController and it has a UISlider for changing the progress of the playing audio. I remember that this worked before. Now it throws an error that the sender is unrecognized. I recently upgraded to a new MacBook Pro M1, and newer Xcode accordingly, may it be a reason? Friends appreciate any comment, any hint, any help.

This is the appearance of the AudioPlayerViewController
This is the screenShot of the error

import UIKit
import AVFoundation
import MediaPlayer
import AVKit

class AudioPlayerViewController: UIViewController {
                
    public var position: Int = 0
    public var paragraphs: [Audio] = []
    public var mainImage = UIImage(named: "placeHolderImage")
   
@IBOutlet var holder: UIView!
var player: AVPlayer?
    var playerItem: AVPlayerItem?
    var isSeekInProgress = false
    var chaseTime = CMTime.zero
    fileprivate let seekDuration: Float64 = 15
    var playerCurrentItemStatus: AVPlayerItem.Status = .unknown

                // User Interface elements
private let albumImageView: UIImageView = {
let imageView = UIImageView()
    imageView.layer.shadowOpacity = 0.3
    imageView.layer.shadowRadius = 3
    imageView.layer.shadowOffset = CGSize(width: 6, height: 6)
    imageView.layer.shadowRadius = 8
imageView.contentMode = .scaleAspectFill
return imageView
}()
    
private let paragraphNumberLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.font = .systemFont(ofSize: 16, weight: .light)
label.numberOfLines = 0 // allow line wrap
label.textColor = UIColor(named: "PlayerColors")
return label
}()
                
private let albumNameLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.font = .systemFont(ofSize: 18, weight: .bold)
label.numberOfLines = 0 // allow line wrap
label.textColor = UIColor(named: "PlayerColors")
return label
}()
                
private let songNameLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
    label.font = .systemFont(ofSize: 16, weight: .ultraLight)
label.numberOfLines = 0 // allow line wrap
label.textColor = UIColor(named: "PlayerColors")
return label
}()
    
private let elapsedTimeLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.font = .systemFont(ofSize: 12, weight: .light)
label.textColor = UIColor(named: "PlayerColors")
label.text = "00:00"
label.numberOfLines = 0
return label
}()
        
private let remainingTimeLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.font = .systemFont(ofSize: 12, weight: .light)
label.textColor = UIColor(named: "PlayerColors")
label.text = "00:00"
label.numberOfLines = 0
return label
}()
        
private let playbackSlider: UISlider = {
    
let v = UISlider()
v.addTarget(AudioPlayerViewController.self, action: #selector(progressScrubbed(_:)), for: .valueChanged)
v.minimumTrackTintColor = UIColor.lightGray
v.maximumTrackTintColor = UIColor.darkGray
v.thumbTintColor = UIColor(named: "PlayerColors")
v.minimumValue = 0
v.isContinuous = false
return v
}()
   
    
    

let playPauseButton = UIButton()
    
    
                

override func viewDidLoad() {
    super.viewDidLoad()
                    
    let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGesture(gesture:)))
    self.playbackSlider.addGestureRecognizer(panGesture)
                    

let audioSession = AVAudioSession.sharedInstance()
    do {
        try audioSession.setCategory(AVAudioSession.Category.playback)
            }
                catch{
                        print(error)
                    }
                }
                
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        

        
        
                
        if holder.subviews.count == 0 {
    configure()
    }
}
                func configure() {
                    // set up player
                    let song = paragraphs[position]
                    
                    let url = URL(string: song.trackURL)
                    let playerItem: AVPlayerItem = AVPlayerItem(url: url!)
                    do {
                        try AVAudioSession.sharedInstance().setMode(.default)
                        try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
                        guard url != nil else {
                            print("urls string is nil")
                            return
                        }
   
                        player = AVPlayer(playerItem: playerItem)
                        
                        
                        let duration : CMTime = playerItem.asset.duration
                        
                        let seconds : Float64 = CMTimeGetSeconds(duration)
                        
                        remainingTimeLabel.text = self.stringFromTimeInterval(interval: seconds)
                        
                        let currentDuration : CMTime = playerItem.currentTime()
                        
                        let currentSeconds : Float64 = CMTimeGetSeconds(currentDuration)
                        
                        elapsedTimeLabel.text = self.stringFromTimeInterval(interval: currentSeconds)

                        playbackSlider.maximumValue = Float(seconds)
                       
                        
                        player!.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: 1), queue: DispatchQueue.main) { (CMTime) -> Void in
                            
                            if self.player!.currentItem?.status == .readyToPlay {
                                let time : Float64 = CMTimeGetSeconds(self.player!.currentTime());
                                self.playbackSlider.value = Float(time)
                                
                                self.elapsedTimeLabel.text = self.stringFromTimeInterval(interval: time)
                            }
                            
                            let playbackLikelyToKeepUp = self.player?.currentItem?.isPlaybackLikelyToKeepUp
                            if playbackLikelyToKeepUp == false{
                                print("IsBuffering")
                                self.playPauseButton.isHidden = true
                               
                            } else {
                                // stop the activity indicator
                                print("Buffering completed")
                                
                                self.playPauseButton.isHidden = false
                                
                            }
                        }
                        
                        
                        playbackSlider.addTarget(self, action: #selector(AudioPlayerViewController.progressScrubbed(_:)), for: .valueChanged)
                        self.view.addSubview(playbackSlider)
                        //subroutine used to keep track of current location of time in audio file
                        guard let player = player else {
                            print("player is nil")
                            return
                        }
                        player.play()
                        
                    }
                    catch {
                        print("error accured")
                    }
                    // set up user interface elements
                    
                    //album cover
albumImageView.frame = CGRect(x: 20,
                              y: 20,
                              width: holder.frame.size.width - 40,
                              height: holder.frame.size.width - 40)
                    
                    albumImageView.image = mainImage
                    

                    
                    
                    holder.addSubview(albumImageView)
                    
                    //Labels Song name, album, artist
                    albumNameLabel.frame = CGRect(x: 20,
                                                 y: holder.frame.size.height - 300,
                                                  width: holder.frame.size.width - 40,
                                                  height: 20)
                    paragraphNumberLabel.frame = CGRect(x: 20,
                                                  y: holder.frame.size.height - 280,
                                                  width: holder.frame.size.width-40,
                                                  height: 20)
                    songNameLabel.frame = CGRect(x: 20,
                                                  y: holder.frame.size.height - 260,
                                                  width: holder.frame.size.width-40,
                                                  height: 20)
                    playbackSlider.frame = CGRect(x: 20,
                                                  y: holder.frame.size.height - 235,
                                                  width: holder.frame.size.width-40,
                                                  height: 40)
                    elapsedTimeLabel.frame = CGRect(x: 25,
                                                    y: holder.frame.size.height - 200,
                                                    width: holder.frame.size.width-40,
                                                    height: 15)
                    remainingTimeLabel.frame = CGRect(x: holder.frame.size.width-60,
                                                      y: holder.frame.size.height - 200,
                                                      width: holder.frame.size.width-20,
                                                      height: 15)
                    songNameLabel.text = song.name
                    albumNameLabel.text = song.albumName
                    paragraphNumberLabel.text = song.paragraphNumber
                    holder.addSubview(songNameLabel)
                    holder.addSubview(albumNameLabel)
                    holder.addSubview(paragraphNumberLabel)
                    holder.addSubview(elapsedTimeLabel)
                    holder.addSubview(remainingTimeLabel)
                    //Player controls
                    let nextButton = UIButton()
                    let backButton = UIButton()
                    let seekForwardButton = UIButton()
                    let seekBackwardButton = UIButton()
                    //frames of buttons
                    playPauseButton.frame = CGRect(x: (holder.frame.size.width - 40) / 2.0,
                                                   y: holder.frame.size.height - 172.5,
                                                   width: 40,
                                                   height: 40)
                    
                    nextButton.frame = CGRect(x: holder.frame.size.width - 70,
                                                   y: holder.frame.size.height - 162.5,
                                                   width: 30,
                                                   height: 20)
                    
                    backButton.frame = CGRect(x: 70 - 30,
                                            y: holder.frame.size.height - 162.5,
                                            width: 30,
                                            height: 20)
                    seekForwardButton.frame = CGRect(x: holder.frame.size.width - 140,
                                                     y: holder.frame.size.height - 167.5,
                                            width: 30,
                                            height: 30)
                    seekBackwardButton.frame = CGRect(x: 110,
                                            y: holder.frame.size.height - 167.5,
                                            width: 30,
                                            height: 30)
                    let volumeView = MPVolumeView(frame: CGRect(x: 20,
                                                                y: holder.frame.size.height - 80,
                                                                width: holder.frame.size.width-40,
                                                                height: 30))
                    
                    
                    
                    
                    
                    holder.addSubview(volumeView)
                    //actions of buttons
                    playPauseButton.addTarget(self, action: #selector(didTapPlayPauseButton), for: .touchUpInside)
                    backButton.addTarget(self, action: #selector(didTapBackButton), for: .touchUpInside)
                    nextButton.addTarget(self, action: #selector(didTapNextButton), for: .touchUpInside)
                    seekForwardButton.addTarget(self, action: #selector(seekForwardButtonTapped), for: .touchUpInside)
                    seekBackwardButton.addTarget(self, action: #selector(seekBackwardButtonTapped), for: .touchUpInside)
                    //styling of buttons
                    playPauseButton.setBackgroundImage(UIImage(systemName: "pause.fill"), for: .normal)
                    nextButton.setBackgroundImage(UIImage(systemName: "forward.fill"), for: .normal)
                    backButton.setBackgroundImage(UIImage(systemName: "backward.fill"), for: .normal)
                    seekForwardButton.setBackgroundImage(UIImage(systemName: "goforward.15"), for: .normal)
                    seekBackwardButton.setBackgroundImage(UIImage(systemName: "gobackward.15"), for: .normal)
   
                    playPauseButton.tintColor = UIColor(named: "PlayerColors")
                    nextButton.tintColor = UIColor(named: "PlayerColors")
                    backButton.tintColor = UIColor(named: "PlayerColors")
                    seekForwardButton.tintColor = UIColor(named: "PlayerColors")
                    seekBackwardButton.tintColor = UIColor(named: "PlayerColors")
                    
                    holder.addSubview(playPauseButton)
                    holder.addSubview(nextButton)
                    holder.addSubview(backButton)
                    holder.addSubview(seekForwardButton)
                    holder.addSubview(seekBackwardButton)
                    
                    
}
    
    
    
    @objc func progressScrubbed(_ playbackSlider:UISlider)
        {
            
            let seconds : Int64 = Int64(playbackSlider.value)
        let targetTime:CMTime = CMTimeMake(value: seconds, timescale: 1)
        
            
            player!.seek(to: targetTime)
            
            if player!.rate == 0
            {
                player?.play()
            }
        }
    
    
    
    @objc func panGesture(gesture:UIPanGestureRecognizer) {
        let currentPoint = gesture.location(in: playbackSlider)
        let percentage = currentPoint.x/playbackSlider.bounds.size.width;
        let delta = Float(percentage) * (playbackSlider.maximumValue - playbackSlider.minimumValue)
        let value = playbackSlider.minimumValue + delta
        playbackSlider.setValue(value, animated: true)


    }
    
    func setupNowPlaying() {
      // Define Now Playing Info
      var nowPlayingInfo = [String : Any]()
      nowPlayingInfo[MPMediaItemPropertyTitle] = "Unstoppable"
      
      if let image = UIImage(named: "placeHolderImage") {
        nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { size in
          return image
        }
      }
        nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = player?.currentTime
        nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = playerItem?.duration
        nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player?.rate
      
      MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
    }
    
    func updateNowPlaying(isPause: Bool) {
      var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo!
        nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = player?.currentTime
      nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = isPause ? 0 : 1
      
        MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
    }
    
    func setupNotifications() {
      let notificationCenter = NotificationCenter.default
      notificationCenter.addObserver(self,
                                     selector: #selector(handleInterruption),
                                     name: AVAudioSession.interruptionNotification,
                                     object: nil)
      notificationCenter.addObserver(self,
                                     selector: #selector(handleRouteChange),
                                     name: AVAudioSession.routeChangeNotification,
                                     object: nil)
    }

    @objc func handleRouteChange(notification: Notification) {
      guard let userInfo = notification.userInfo,
        let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
        let reason = AVAudioSession.RouteChangeReason(rawValue:reasonValue) else {
          return
      }
      switch reason {
      case .newDeviceAvailable:
        let session = AVAudioSession.sharedInstance()
        for output in session.currentRoute.outputs where output.portType == AVAudioSession.Port.headphones {
          print("headphones connected")
          DispatchQueue.main.sync {
            player?.play()
          }
          break
        }
      case .oldDeviceUnavailable:
        if let previousRoute =
          userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? AVAudioSessionRouteDescription {
          for output in previousRoute.outputs where output.portType == AVAudioSession.Port.headphones {
            print("headphones disconnected")
            DispatchQueue.main.sync {
                player?.pause()
            }
            break
          }
        }
      default: ()
      }
    }
    
    @objc func handleInterruption(notification: Notification) {
      guard let userInfo = notification.userInfo,
        let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
        let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
          return
      }
      
      if type == .began {
        print("Interruption began")
        // Interruption began, take appropriate actions
      }
      else if type == .ended {
        if let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt {
          let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
          if options.contains(.shouldResume) {
            // Interruption Ended - playback should resume
            print("Interruption Ended - playback should resume")
            player?.play()
          } else {
            // Interruption Ended - playback should NOT resume
            print("Interruption Ended - playback should NOT resume")
          }
        }
      }
    }
    
    @objc func didTapPlayPauseButton() {
        if player?.timeControlStatus == .playing {
                           //pause
                           player?.pause()
                           
                           
                           //show play button
                           playPauseButton.setBackgroundImage(UIImage(systemName: "play.fill"), for: .normal)
                           //shrink image
                           UIView.animate(withDuration: 0.2, animations: {
                               self.albumImageView.frame = CGRect(x: 50,
                                                             y: 50,
                                                             width: self.holder.frame.size.width - 100,
                                                             height: self.holder.frame.size.width - 100)
                               
                           })
                           
                       } else {
                           //play
                           player?.play()
                           //show pause button
                           playPauseButton.setBackgroundImage(UIImage(systemName: "pause.fill"), for: .normal)
                           
                           //increase image size
                           UIView.animate(withDuration: 0.4, animations: {
                               self.albumImageView.frame = CGRect(x: 20,
                                                             y: 20,
                                                             width: self.holder.frame.size.width - 40,
                                                             height: self.holder.frame.size.width - 40)
                               
        })
    }
}

    private func setupView() {
        setupConstraints()
    }
    
    private func setupConstraints() {
        NSLayoutConstraint.activate([
            holder.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            holder.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            holder.topAnchor.constraint(equalTo: view.topAnchor),
            holder.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        ])
    }

    func stringFromTimeInterval(interval: TimeInterval) -> String {
        
        let interval = Int(interval)
        let seconds = interval % 60
        let minutes = (interval / 60) % 60
        let timeFormatter = NumberFormatter()
        timeFormatter.minimumIntegerDigits = 2
        timeFormatter.minimumFractionDigits = 0
        timeFormatter.roundingMode = .down
        
        guard let minsString = timeFormatter.string(from: NSNumber(value: minutes)),
              let secStr = timeFormatter.string(from: NSNumber(value: seconds)) else {
            return "00:00"
        }
        return "\(minsString):\(secStr)"
    }

    @objc func didTapBackButton() {
                       if position > 0 {
                           position = position - 1
                           player?.pause()
                           for subview in holder.subviews {
                               subview.removeFromSuperview()
                           }
                           configure()
                       }
                   }
                   
    @objc func didTapNextButton() {
        if position < (paragraphs.count - 1) {
    position = position + 1
    player?.pause()
    for subview in holder.subviews {
    subview.removeFromSuperview()
    }
    configure()
    }
                       
}
    
    @objc func seekBackwardButtonTapped(){
        
        if player == nil { return }
        
            let playerCurrentTime = CMTimeGetSeconds(player!.currentTime())
        var newTime = playerCurrentTime - seekDuration
            
        if newTime < 0 { newTime = 0 }
        player?.pause()
        let selectedTime: CMTime = CMTimeMake(value: Int64(newTime * 1000 as Float64), timescale: 1000)
            
        player?.seek(to: selectedTime)
            player?.play()
        }
    
    @objc func seekForwardButtonTapped(){
        
        if player == nil { return }
        if let duration = player!.currentItem?.duration {
            let playerCurrentTime = CMTimeGetSeconds(player!.currentTime())
            let newTime = playerCurrentTime + seekDuration
            
            if newTime < CMTimeGetSeconds(duration)
            {
                let selectedTime: CMTime = CMTimeMake(value: Int64(newTime * 1000 as Float64), timescale: 1000)
                player!.seek(to: selectedTime)
            }
            player?.pause()
            player?.play()
        }
    }
    
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        player?.play()
        UIApplication.shared.isIdleTimerDisabled = true
    }
        
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        player?.pause()
        UIApplication.shared.isIdleTimerDisabled = false
        
    }

    
}

[ad_2]