Search code examples
iosswiftiboutlet

How to pass IBOutlet between two classes?


I have a class for managing AVPlayer and ViewController showing AVPlayer. In ViewController I have 3 IBOutlets:

@IBOutlet weak var playButton: UIButton!
@IBOutlet weak var progressBar: UIProgressView!
@IBOutlet weak var timerLabel: UILabel!

The problem is I can't figure out how to pass IBOutlets from ViewController to my player managing class. I always get nil and

fatal error: unexpectedly found nil while unwrapping an Optional value

when initialising progressBar, timerLabel or playButton.

Here is some code from my player managing class:

import UIKit
import AVFoundation

class RecordsAudioPlayer: NSObject
{
    var player = AVPlayer()
    var timerUpdateAudioProgressView = Timer()
    var timerUpdateTime = Timer()
    var isPlaying: Bool = false

...

func updateAudioProgressView()
    {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc = storyboard.instantiateViewController(withIdentifier: "callRecords") as! CallRecordsViewController

        let progressBar = vc.progressBar! 
        print("method fired: updateAudioProgressView ", NSDate.init(timeIntervalSinceNow: 0).description)
        if isPlaying
        {
            let currentPlayerItem = player.currentItem
            let duration = Float((currentPlayerItem?.asset.duration.value)!)/Float((currentPlayerItem?.asset.duration.timescale)!)
            let currentTime = Float(player.currentTime().value)/Float(player.currentTime().timescale)
            progressBar.setProgress(Float(currentTime/duration), animated: false)
        }
    }

    func updateTime()
    {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc = storyboard.instantiateViewController(withIdentifier: "callRecords") as! CallRecordsViewController
        let timerLabel: UILabel! = vc.timerLabel! //***I get exception here
        print("method fired: updateTime ", NSDate.init(timeIntervalSinceNow: 0).description)
        let currentTime = Int64(player.currentTime().value)/Int64(player.currentTime().timescale)
        let minutes = currentTime / 60
        let seconds = currentTime - minutes * 60
        timerLabel.text = String(format: "%02d : %02d", Int(minutes), Int(seconds))
    }

Solution

  • In this case delegates came for our rescue. Create delegate methods for your RecordsAudioPlayer class. And use them to update progress bar and timer label.

    protocol RecordsAudioPlayerDelegate
    {
      func audioplayerProgressUpdated(time:CGFloat) // change the method as your wish
    }
    
    class RecordsAudioPlayer: NSObject
    {
      var delegate:RecordsAudioPlayerDelegate?
      var player = AVPlayer()
      var timerUpdateAudioProgressView = Timer()
      var timerUpdateTime = Timer()
      var isPlaying: Bool = false
        ...
        ...
        ...
    
      //Call the delegate method from where you get the player current time
      self.delegate.audioplayerProgressUpdated(playerTime)
    
      //For controlling play/pause using play button 
      func playButtonClicked()
      {
        //play/pause player based on player status
      }
    }
    

    And inside your ViewController while/after creating RecordsAudioPlayer object set delegate as self

    class YourViewController : UIViewController, RecordsAudioPlayerDelegate
    {
      var recordsAudioPlayer : RecordsAudioPlayer!
    
      // set delegate where you create object for RecordsAudioPlayer
      self.recordsAudioPlayer.delegate = self
    
      //Now implement the delegate function
      func audioplayerProgressUpdated(time:CGFloat)
      {
       //update progress view and timer label
      }
    
      //And inside from the play button action method 
      @IBAction func playButtonClicked(sender: UIButton)
      {
       self.recordsAudioPlayer.playButtonClicked()
      }
    
    }