youtube-ios-player-helperで複数再生する問題

はじめに

youtube-ios-player-helperは公式ライブラリですが長らくメンテナンスがされていません。

YTPlayerViewをTableViewやCollectionViewなどのcellに配置している場合動画を読み込んでいる最中に別のcellの動画をタップすると重複して動画が開いてしまい、音も個別の動画が2重、3重で再生されてしまいます。

重なって動画再生されているので1つ閉じても別の動画というような状況になります。

失敗

失敗1

YTPlayerViewのインスタンスが各セルにいるので再生された段階で別のcellにNotificationを飛ばし止めてしまえば良いと考えました。

Play状態になったらフラグを切り替えてbuffering中のものを止めてしまえば良いと考えました。

//Cell内での定義 〜一部省略〜

  override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(youtubePlayerPlayStart(_:)),
                                               name: .youtubePlayerPlayStart,
                                               object: nil)
    }

  @objc private func youtubePlayerPlayStart(_ notification: Notification?) {
        if isPlayerBuffering {
            playerView.stopVideo()
        }
    }
}

extension HogeYoutubeCell: YTPlayerViewDelegate {

    func playerView(_ playerView: YTPlayerView, didChangeTo state: YTPlayerState) {
     if state == .buffering {
            isPlayerBuffering = true
        }
        if state == .playing {
            isPlayerBuffering = true
            NotificationCenter.default.post(name: .youtubePlayerPlayStart, object: nil)
        }
}

extension Notification.Name {
    static var youtubePlayerPlayStart = Notification.Name("youtubePlayerPlayStart")
}

しかし、タイミングが悪くほぼ同時にstateがplayになった場合はstopで止まった状態で動画の重複が発生し、黒い画面の下で流れてたりする状況が発生しました。

しかもこの黒い画面はbufferingを途中で止めてしまっているので再起不可能です😇

失敗2

youtube-ios-player-helperの仕様でstopしてもpouseしても画面が開きっぱなしなのであればVC側で先頭のVCをdismissしてしまえば良いと考えました。

しかしこれも失敗に終わりました、dismissをした際にYTPlayerViewが悪さをしていて戻ってきたVCが操作不能になります。

そして2つ以上play状態になっている時に1つになるまでdismissするみたいな処理を入れると中途半端にYTPlayerViewが残ってレイアウトが崩れます。そしてもちろんVCの操作はできなくなります。

対処法

解決策を調べても一致する例が見つからなかったんで、そもそもどこかのcellでplayVideo()が呼ばれたらそれ以降はendedが帰ってくるまでplayVideoメソッドを呼び出すのを禁止しました。

//状態を管理するやつ
struct SharedYTPlayerState {
    private init() {}
    static var isPlaying: Bool = false
}

//VC側
cell.ytPlayerHandler = { playing in
   SharedYTPlayerState.isPlaying = playing
}


//cell側
//イベント発火しているところ
if !SharedYTPlayerState.isPlaying {
    playerView.playVideo()
}

//ライブラリのdelegate
func playerView(_ playerView: YTPlayerView, didChangeTo state: YTPlayerState) {
    switch state {
    case .buffering:
        ytPlayerHandler?(true)

    case .paused, .ended:
        ytPlayerHandler?(false)

    default:
        break
    }

}