如何使用自定义控件管理 AVPlayerViewController 中的焦点处理?

How to manage focus handling in AVPlayerViewController with custom controls?

我尝试向 tvOS 电影应用程序的 AVPlayerViewController 添加一个跳过介绍按钮(如 Netflix)。我将它作为子视图添加到 contentOverlayView,当我让它出现时,我强制使用 preferredFocusEnvironments 给它焦点。一切安好... 直到用户导航到其他地方(例如搜索栏或视频资产信息视图)。然后该按钮失去焦点(预期)并且永远无法通过用户交互再次获得焦点。 我试过:

最后一种方法显示其他按钮中的 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()
    }
}

我希望这对那些来到这里却找不到其他答案的人有所帮助。