0

I am trying to add an observer to my reusable cell, and the problem is that it adds multiple observers. So I am wondering if there is any way around it because I really need this observer.

var player: AVPlayer?

var post: Post? {
    didSet {
      updateView()
    }
}

Pretty much post is an array of videos and this gets called every time a post is set as a row on the table view.

This is why I was not able to add the observer in this method because it would be setting multiple observers.

func updateView() {
    if let videoUrlString = post?.videoURL {
        let videoUrl = URL(string: videoUrlString)
        player = AVPlayer(url: videoUrl!)
        playerLayer = AVPlayerLayer(player: player)
        playerLayer.frame = postVideoView.frame
        playerLayer.frame.size.width = UIScreen.main.bounds.width
        self.postVideoView.layer.addSublayer(playerLayer)
        player?.play()
        NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: nil, using: { (_) in
            DispatchQueue.main.async {
                self.player?.seek(to: kCMTimeZero)
                self.player?.play()
            }
        })
}

    self.updateLike(post: self.post!)
}

So then i tried setting it in the awakeFromNib() method.

override func awakeFromNib() {
    super.awakeFromNib()
    player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)
}

But it wasn't working because the player hadn't been completely initialized yet.

This is my observer function:

 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "currentItem.loadedTimeRanges" {
        if let duration = player?.currentItem?.duration {
            let seconds = CMTimeGetSeconds(duration)

            let secondsText = Int(seconds) % 60
            let minutesText = String(format: "%02d", Int(seconds) / 60)
            videoLengthLabel.text = "\(minutesText):\(secondsText)"
        }
    }
}

How i remove the observer:

func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    let cell = tableView.dequeueReusableCell(withIdentifier: "videoPostCell", for: indexPath) as! HomeTableViewCell
    cell.playerLayer.removeFromSuperlayer()
    cell.player?.pause()
    cell.player?.isMuted = true
    cell.delegate = self
    cell.player?.removeObserver(self, forKeyPath: "currentItem.loadedTimeRanges")
}

1 Answers1

0

I understand your problem, If you have problem to remove observer then you can manage it in below delegate method UITableView.

override func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    if let cell = cell as? CustomCell {
        // Remove your observer here
        // Stop Play Video
    }
}

And keep your cellForRowAtIndexPath as it is. Mean create observer or ETC.

Otherwise below link will be helpful in your case.

1) Add and remove observer from multiple AVPlayerItem on UITableViewCell

2) Play video on UITableViewCell when it is completely visible

3) Embedding videos in a tableview cell

Johnty
  • 195
  • 1
  • 11
  • So where should i add the observer? – Kyle Gustov Aug 03 '17 at 06:05
  • You can add observer in cellForRowAtIndexPath and it will be removed in didEndDisplayingCell – Johnty Aug 03 '17 at 06:06
  • I am now getting a different error that reads this: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled. Is this because I am adding the observer UITableViewDelegate method and handling it inside the TableViewCell? If so how can i fix this? – Kyle Gustov Aug 03 '17 at 06:13
  • Can you write how you are removing observer ? Normally this issue happens when the registered observer deallocates and a KVO event triggers after that. For fixing you need to remove all observers before your object is going to be deallocated. – Johnty Aug 03 '17 at 06:18
  • Remove NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: nil, using: { (_) in DispatchQueue.main.async { self.player?.seek(to: kCMTimeZero) self.player?.play() } }) And Try – Johnty Aug 03 '17 at 06:24
  • In didEndDisplaying method change let cell = tableView.dequeueReusableCell(withIdentifier: "videoPostCell", for: indexPath) as! HomeTableViewCell to let cell = tableView.cellForRowAtIndexPath(indexPath) as! HomeTableViewCell and Put your calleForRowIndexPath Method code – Johnty Aug 03 '17 at 06:33
  • I'm assuming you're saying just change the didEndDisplaying method and leave the cellForRowAt method the same. I did that and it gave me the same error – Kyle Gustov Aug 03 '17 at 06:41