如何使用自定义控件管理 AVPlayerViewController 中的焦点处理?
How to manage focus handling in AVPlayerViewController with custom controls?
我尝试向 tvOS 电影应用程序的 AVPlayerViewController
添加一个跳过介绍按钮(如 Netflix)。我将它作为子视图添加到 contentOverlayView
,当我让它出现时,我强制使用 preferredFocusEnvironments
给它焦点。一切安好...
直到用户导航到其他地方(例如搜索栏或视频资产信息视图)。然后该按钮失去焦点(预期)并且永远无法通过用户交互再次获得焦点。
我试过:
- 添加一个
UIFocusGuide()
- 将我的按钮直接放在
AVPlayerViewController
的视图上
- 添加自己的子视图,其中包含
AVPlayerViewController
视图的按钮
- 添加自己的子视图,其中包含
contentOverlayView
的按钮
- 在同一个子视图上我的按钮旁边、下方、上方添加几个其他按钮(针对上述每种情况)
最后一种方法显示其他按钮中的 none 可以通过用户交互获得焦点,所以看起来,出于同样的原因,跳过介绍按钮无法被用户聚焦.但这是什么原因呢?
将自定义交互元素添加到 AVPlayerViewController
的正确做法是什么?
有人有什么想法吗?
尝试回答我的主要问题“向 AVPlayerViewController 添加自定义交互元素的正确做法是什么?” 我自己:
自从我在 1.5 年前发布这个问题以来,我并没有对跳过介绍功能的实现进行太多更改。因此,无论何时用户使用遥控器,按钮都会失去焦点,我唯一改变的是,每当这种情况发生时,我都会隐藏按钮,通过实现 didUpdateFocus(in:with:)
,类似于:
if let previouslyFocusedView = context.previouslyFocusedView {
if previouslyFocusedView == skipIntroButton {
changeSkipIntroButtonVisibility(to: 0.0)//animates alpha value
}
}
(我不是很清楚,为什么我不把它设置成isHidden = true
)
然而,与此同时,我不得不为我们的播放器实现更复杂的覆盖,即“开始下一个视频/观看积分”,带有预告片、一些按钮、countdown/progress 栏等等.对于上述问题,很明显,我不能采用 contentOverlayView
方法。
所以我决定以“传统方式”实现它,在玩家的视图之上呈现一个完整的 UIViewController
,如下所示:
func showNextVideoOverlay() {
guard let nextVideoTeaser = nextVideoTeaser else { return }
let nextVideoOverlay = NextVideoAnnouncementViewController(withTeaser: nextVideoTeaser, player: player)
nextVideoOverlay.nextVideoAnnouncementDelegate = self
nextVideoOverlay.modalPresentationStyle = .overFullScreen
present(nextVideoOverlay, animated: true, completion: nil)
}
当然NextVideoAnnouncementViewController
是透明的,看视频还是可以的。我发现这种直接的方法非常有效,我真的不知道,为什么我在实施 skip intro 时没有考虑过它。
我在 QA 的同事发现了一件棘手的事情,你应该知道(我认为,我记得,这在不同的设备和不同的遥控器以及不同的 tvOS 版本上是不同的 - 试试吧):
重叠的视图控制器阻止了来自遥控器的大部分命令,您可以分别在视图控制器内导航而不影响播放器 - play/pause 按钮除外。那个会暂停播放器,但不可能从那里恢复。这是因为 playing (av)player 总是听这个按钮,而 paused 则不会。所以我也必须实现这样的事情:
func addRemoteButtonRecognizer() {
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(playPauseButtonPressed))
tapRecognizer.allowedPressTypes = [NSNumber(value: UIPress.PressType.playPause.rawValue)]
self.view.addGestureRecognizer(tapRecognizer)
}
@objc func playPauseButtonPressed(sender: AnyObject) {
nextVideoAnnouncementDelegate?.remotePlayButtonPressed()
}
func remotePlayButtonPressed() {
if player?.playerStatus == .paused {
player?.play()
} else {
player?.pause()
}
}
我希望这对那些来到这里却找不到其他答案的人有所帮助。
我尝试向 tvOS 电影应用程序的 AVPlayerViewController
添加一个跳过介绍按钮(如 Netflix)。我将它作为子视图添加到 contentOverlayView
,当我让它出现时,我强制使用 preferredFocusEnvironments
给它焦点。一切安好...
直到用户导航到其他地方(例如搜索栏或视频资产信息视图)。然后该按钮失去焦点(预期)并且永远无法通过用户交互再次获得焦点。
我试过:
- 添加一个
UIFocusGuide()
- 将我的按钮直接放在
AVPlayerViewController
的视图上 - 添加自己的子视图,其中包含
AVPlayerViewController
视图的按钮 - 添加自己的子视图,其中包含
contentOverlayView
的按钮
- 在同一个子视图上我的按钮旁边、下方、上方添加几个其他按钮(针对上述每种情况)
最后一种方法显示其他按钮中的 none 可以通过用户交互获得焦点,所以看起来,出于同样的原因,跳过介绍按钮无法被用户聚焦.但这是什么原因呢?
将自定义交互元素添加到 AVPlayerViewController
的正确做法是什么?
有人有什么想法吗?
尝试回答我的主要问题“向 AVPlayerViewController 添加自定义交互元素的正确做法是什么?” 我自己:
自从我在 1.5 年前发布这个问题以来,我并没有对跳过介绍功能的实现进行太多更改。因此,无论何时用户使用遥控器,按钮都会失去焦点,我唯一改变的是,每当这种情况发生时,我都会隐藏按钮,通过实现 didUpdateFocus(in:with:)
,类似于:
if let previouslyFocusedView = context.previouslyFocusedView {
if previouslyFocusedView == skipIntroButton {
changeSkipIntroButtonVisibility(to: 0.0)//animates alpha value
}
}
(我不是很清楚,为什么我不把它设置成isHidden = true
)
然而,与此同时,我不得不为我们的播放器实现更复杂的覆盖,即“开始下一个视频/观看积分”,带有预告片、一些按钮、countdown/progress 栏等等.对于上述问题,很明显,我不能采用 contentOverlayView
方法。
所以我决定以“传统方式”实现它,在玩家的视图之上呈现一个完整的 UIViewController
,如下所示:
func showNextVideoOverlay() {
guard let nextVideoTeaser = nextVideoTeaser else { return }
let nextVideoOverlay = NextVideoAnnouncementViewController(withTeaser: nextVideoTeaser, player: player)
nextVideoOverlay.nextVideoAnnouncementDelegate = self
nextVideoOverlay.modalPresentationStyle = .overFullScreen
present(nextVideoOverlay, animated: true, completion: nil)
}
当然NextVideoAnnouncementViewController
是透明的,看视频还是可以的。我发现这种直接的方法非常有效,我真的不知道,为什么我在实施 skip intro 时没有考虑过它。
我在 QA 的同事发现了一件棘手的事情,你应该知道(我认为,我记得,这在不同的设备和不同的遥控器以及不同的 tvOS 版本上是不同的 - 试试吧):
重叠的视图控制器阻止了来自遥控器的大部分命令,您可以分别在视图控制器内导航而不影响播放器 - play/pause 按钮除外。那个会暂停播放器,但不可能从那里恢复。这是因为 playing (av)player 总是听这个按钮,而 paused 则不会。所以我也必须实现这样的事情:
func addRemoteButtonRecognizer() {
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(playPauseButtonPressed))
tapRecognizer.allowedPressTypes = [NSNumber(value: UIPress.PressType.playPause.rawValue)]
self.view.addGestureRecognizer(tapRecognizer)
}
@objc func playPauseButtonPressed(sender: AnyObject) {
nextVideoAnnouncementDelegate?.remotePlayButtonPressed()
}
func remotePlayButtonPressed() {
if player?.playerStatus == .paused {
player?.play()
} else {
player?.pause()
}
}
我希望这对那些来到这里却找不到其他答案的人有所帮助。