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
}
}