AVContentProposalとは
tvOS10から登場した、次のエピソードの訴求をクールかつシンプルに実装することができるAVKitのAPIです。
問題
以前こういう記事を書きまして、AVContentProposalちゃんと動かないじゃん!という問題提起をしました。transitionアニメーション中にMENUを押して戻ると表示がおかしくなります。シミュレータではオートレイアウトのエラーが出てクラッシュします。
tvOS11で試したところ、依然として問題が継続していました。
2017-11-18 21:53:54.683980+0900 TVOSTest[11885:456841] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors and because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.'
ワークアラウンドを実装できましたので共有します。
ワークアラウンド
原因はAVPlayerViewControllerがすでにプレイヤーがいなくなっていることにContentProposalViewControllerが気づかないのが原因なので、
- 気づかせてあげる
- 状況がハッキリするまではボタンなどUIを表示しない
の2点が解決方法です。要するに、地道にフラグ管理するということになります。
class ContentProposalViewController: AVContentProposalViewController {
var shouldDismiss: Bool = false {
didSet { dismissIfNeeded() }
}
@IBOutlet private var button: UIButton! {
didSet { button.alpha = 0.0 }
}
@IBOutlet private var textView: UITextView! {
didSet { textView.alpha = 0.0 }
}
override var preferredFocusEnvironments: [UIFocusEnvironment] {
return [button]
}
override var preferredPlayerViewFrame: CGRect {
return CGRect(x: 432, y: 20, width: 1056, height: 594)
}
@IBAction private func button(sender: UIButton!) {
dismissContentProposal(for: .accept, animated: true, completion: nil)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !shouldDismiss {
UIView.animate(withDuration: 0.2) {
self.button.alpha = 1.0
self.textView.alpha = 1.0
}
} else {
dismissIfNeeded()
}
}
private func dismissIfNeeded() {
if shouldDismiss {
DispatchQueue.main.async { [weak self] in
self?.button?.alpha = 0.0
self?.textView?.alpha = 0.0
self?.dismissContentProposal(for: .reject, animated: false, completion: nil)
}
}
}
deinit {
print(#function)
}
}
これで、MENU押して戻った時に見た目がおかしくなることはなくなりました。シミュレータでもクラッシュしません🎉
まとめ
「AVContentProposalは本当に便利なのか?」
上記のようなワークアラウンドをすれば、とっても便利です!
以前作った以下のサンプルアプリを更新しておきましたのでご興味ある方はご覧ください。
https://github.com/toshi0383/tvOS-10-Sampler