0

So my app contains a uitableview/uitableviewcell and inside the uitableview/uitableviewcell is an AVplayer. I want to be able to pause the video player when the button is tapped. When I made the AV player a global variable, I was able to pause and play the video, but the video wasn't being replaced by the next video when I scrolled.

// video player
  let videoURL = URL(string: uVideoUrl)!
  let player = AVPlayer(url: videoURL)
  let playerLayer = AVPlayerLayer(player: player)
  playerLayer.frame = CGRect(x: cell.backgroundImg.frame.origin.x, y: cell.backgroundImg.frame.origin.y, width: cell.backgroundImg.frame.width - 5, height: cell.backgroundImg.frame.height)
  cell.layer.addSublayer(playerLayer)


// play button
  let playButton = UIButton(frame: CGRect(x: 15, y: 75, width: 30, height: 30))
  playButton.setImage(UIImage(named: "playimg.png"), for: .normal)

  ** playButton.addTarget(self, action: #selector(playVideo(player:player)), for: .touchUpInside) **
  cell.addSubview(playButton)

Here's the function to play the video.

func playVideo(player:AVPlayer) {
    player.play()
}

The text/code with ** is the code I can't figure out how to let me pass in the player for me to play the video.

Ashwini Chougale
  • 1,093
  • 10
  • 26

3 Answers3

0

You can't pass parameters on selectors but there are a few ways to do this.

Here's one way by using Notification(swift 3), NSNotification(swift 2.3, etc)

class ViewController: UIViewController {
    var player: AVPlayer!

    override func viewDidLoad() {
        super.viewDidLoad()

        let videoURL = URL(string: uVideoUrl)!
        player = AVPlayer(url: videoURL) // initialize player that is on the class level scope
        let playerLayer = AVPlayerLayer(player: player)
        playerLayer.frame = CGRect(x: cell.backgroundImg.frame.origin.x, y: cell.backgroundImg.frame.origin.y, width: cell.backgroundImg.frame.width - 5, height: cell.backgroundImg.frame.height)
        cell.layer.addSublayer(playerLayer)

        let playButton = UIButton(frame: CGRect(x: 15, y: 75, width: 30, height: 30))
        playButton.setImage(UIImage(named: "playimg.png"), for: .normal)
    }

    func playButtonClicked(sender: UIButton) {
        let notifName = NSNotification.Name(rawValue: "SOMENOTIFICATIONNAMETHATISUNIQUE")
        NotificationCenter.default.post(name: notifName, object: nil, userInfo: ["player": player])
    }
}


class SomeViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let notifName = NSNotification.Name(rawValue: "SOMENOTIFICATIONNAMETHATISUNIQUE")
        NotificationCenter.default.addObserver(self, selector: #selector(self.playClicked(notification:)), name: notifName, object: nil)
    }

    func playClicked(notification: Notification) {
        if let player = notification.userInfo!["player"] as? AVPlayer {
            // do stuff with player
        }
    }
}

What happens here is that your player is now passed like a parameter using the Notification's userInfo

Zonily Jame
  • 5,053
  • 3
  • 30
  • 56
0

You could add additional data to your UIButton using the objective C associated object system. The following code adds an additional variable player to UIButton. I've done this in the past with Int's. Haven't tried it with such a complex object as AVPlayer but don't see any reason why this wouldn't be possible.

private var playerAssociationKey: UInt8 = 0

extension UIButton {
    var player: Int! {
        get {
            return objc_getAssociatedObject(self, &playerAssociationKey) as? Int
        }
        set(newValue) {
            objc_setAssociatedObject(self, &playerAssociationKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
        }
    }
}

See How to have stored properties in Swift, the same way I had on Objective-C? for more details.

Community
  • 1
  • 1
adamfowlerphoto
  • 2,708
  • 1
  • 11
  • 24
0

The button always sends itself as the argument of its action message. You can't make it send the player as the argument.

Based on the code in your question, it looks like each cell has an AVPlayer and a button. So why not just have the button send a message to the player directly? Use an extension on AVPlayer to define a play/pause toggle method:

extension AVPlayer {
    func togglePlayPause(_ sender: AnyObject?) {
        // Normal playback rate is 1. Paused rate is 0.
        self.rate = 1 - self.rate
    }
}

Then tell your button to send that message to the player like this:

playButton.addTarget(player, action: #selector(AVPlayer.togglePlayPause(_:)), for: .touchUpInside)
rob mayoff
  • 375,296
  • 67
  • 796
  • 848