From e334a5a204f4756582869c92b9bbba337be59026 Mon Sep 17 00:00:00 2001 From: Allen Humphreys Date: Wed, 7 Jun 2023 21:59:17 -0400 Subject: [PATCH] Go a bit further in changing over --- .../PUIPictureContainerViewController.swift | 69 -------- PlayerUI/Views/PUIPlayerView.swift | 152 ++++++------------ WWDC.xcodeproj/project.pbxproj | 4 - 3 files changed, 46 insertions(+), 179 deletions(-) delete mode 100644 PlayerUI/Controllers/PUIPictureContainerViewController.swift diff --git a/PlayerUI/Controllers/PUIPictureContainerViewController.swift b/PlayerUI/Controllers/PUIPictureContainerViewController.swift deleted file mode 100644 index 18296284..00000000 --- a/PlayerUI/Controllers/PUIPictureContainerViewController.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// PUIPictureContainerViewController.swift -// PlayerUI -// -// Created by Guilherme Rambo on 13/05/17. -// Copyright © 2017 Guilherme Rambo. All rights reserved. -// - -import Cocoa -import AVFoundation - -// swiftlint:disable:next type_name -protocol PUIPictureContainerViewControllerDelegate: AnyObject { - - func pictureContainerViewSuperviewDidChange(to superview: NSView?) - -} - -final class PUIPictureContainerViewController: NSViewController { - - weak var delegate: PUIPictureContainerViewControllerDelegate? - - let playerLayer: AVPlayerLayer - - init(playerLayer: AVPlayerLayer) { - self.playerLayer = playerLayer - - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func loadView() { - view = NSView() - view.wantsLayer = true - view.layer = PUIBoringLayer() - view.layer?.backgroundColor = NSColor.black.cgColor - - view.layer?.addSublayer(playerLayer) - - view.addObserver(self, forKeyPath: #keyPath(NSView.superview), options: [.new], context: nil) - } - - override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { - guard let path = keyPath else { return } - switch path { - case #keyPath(NSView.superview): - viewDidMoveToSuperview() - default: - super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) - } - } - - func viewDidMoveToSuperview() { - delegate?.pictureContainerViewSuperviewDidChange(to: view.superview) - } - - override func viewDidLayout() { - super.viewDidLayout() - - playerLayer.frame = view.bounds - } - - deinit { - view.removeObserver(self, forKeyPath: #keyPath(NSView.superview)) - } -} diff --git a/PlayerUI/Views/PUIPlayerView.swift b/PlayerUI/Views/PUIPlayerView.swift index 56d08991..a2cfed51 100644 --- a/PlayerUI/Views/PUIPlayerView.swift +++ b/PlayerUI/Views/PUIPlayerView.swift @@ -87,13 +87,20 @@ public final class PUIPlayerView: NSView { public var mediaTitle: String? public var mediaIsLiveStream: Bool = false - var pictureContainer: PUIPictureContainerViewController! - public init(player: AVPlayer) { self.player = player + self.pipController = AVPictureInPictureController(contentSource: .init(playerLayer: playerLayer)) super.init(frame: .zero) + pipPossibleObservation = pipController.observe( + \AVPictureInPictureController.isPictureInPicturePossible, options: [.initial, .new] + ) { [weak self] _, change in + // Update the PiP button's enabled state. + self?.pipButton.isEnabled = change.newValue ?? false + } + + pipController.delegate = self wantsLayer = true layer = PUIBoringLayer() layer?.backgroundColor = NSColor.black.cgColor @@ -217,7 +224,7 @@ public final class PUIPlayerView: NSView { return player?.currentItem?.asset } - private var playerLayer = PUIBoringPlayerLayer() + private let playerLayer = PUIBoringPlayerLayer() private func setupPlayer() { elapsedTimeLabel.stringValue = elapsedTimeInitialValue @@ -229,15 +236,6 @@ public final class PUIPlayerView: NSView { playerLayer.player = player playerLayer.videoGravity = .resizeAspect - if pictureContainer == nil { - pictureContainer = PUIPictureContainerViewController(playerLayer: playerLayer) - pictureContainer.delegate = self - pictureContainer.view.frame = bounds - pictureContainer.view.autoresizingMask = [.width, .height] - - addSubview(pictureContainer.view) - } - player.addObserver(self, forKeyPath: #keyPath(AVPlayer.status), options: [.initial, .new], context: nil) player.addObserver(self, forKeyPath: #keyPath(AVPlayer.volume), options: [.initial, .new], context: nil) player.addObserver(self, forKeyPath: #keyPath(AVPlayer.rate), options: [.initial, .new], context: nil) @@ -341,8 +339,6 @@ public final class PUIPlayerView: NSView { } fileprivate func updatePlayingState() { -// pipController?.setPlaying(isPlaying) - if isPlaying { playButton.image = .PUIPause } else { @@ -651,6 +647,7 @@ public final class PUIPlayerView: NSView { b.target = self b.action = #selector(togglePip) b.toolTip = "Toggle picture in picture" + b.isEnabled = false return b }() @@ -662,6 +659,17 @@ public final class PUIPlayerView: NSView { private func setupControls() { externalStatusController.view.isHidden = true externalStatusController.view.translatesAutoresizingMaskIntoConstraints = false + let playerView = NSView() + playerView.translatesAutoresizingMaskIntoConstraints = false + playerView.wantsLayer = true + playerView.layer = playerLayer + playerLayer.backgroundColor = .clear + addSubview(playerView) + playerView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + playerView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + playerView.topAnchor.constraint(equalTo: topAnchor).isActive = true + playerView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + addSubview(externalStatusController.view) externalStatusController.view.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true externalStatusController.view.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true @@ -1020,9 +1028,9 @@ public final class PUIPlayerView: NSView { @IBAction public func togglePip(_ sender: NSView?) { if isInPictureInPictureMode { - pipController?.stopPictureInPicture() + pipController.stopPictureInPicture() } else { - enterPictureInPictureMode() + pipController.startPictureInPicture() } } @@ -1246,24 +1254,8 @@ public final class PUIPlayerView: NSView { } } - fileprivate var pipController: AVPictureInPictureController? - - fileprivate func enterPictureInPictureMode() { - delegate?.playerViewWillEnterPictureInPictureMode(self) - - snapshotPlayer { [weak self] image in - self?.externalStatusController.snapshot = image - } - - pipController = AVPictureInPictureController(playerLayer: playerLayer) - pipController?.delegate = self -// pipController?.setPlaying(isPlaying) -// pipController?.aspectRatio = currentPresentationSize ?? NSSize(width: 640, height: 360) -// pipController?.view.layer?.backgroundColor = NSColor.black.cgColor - -// pipController?.presentAsPicture(inPicture: pictureContainer) - pipController?.startPictureInPicture() - } + fileprivate let pipController: AVPictureInPictureController + private var pipPossibleObservation: Any? // MARK: - Visibility management @@ -1616,65 +1608,9 @@ extension PUIPlayerView: PUIExternalPlaybackConsumer { // MARK: - PiP delegate -extension PUIPlayerView: PUIPictureContainerViewControllerDelegate, AVPictureInPictureControllerDelegate { - -// public func pipActionStop(_ pip: PIPViewController) { -// pause(pip) -// -// } -// -// public func pipActionReturn(_ pip: PIPViewController) { -// delegate?.playerViewWillExitPictureInPictureModelegate?.playerViewWillExitPictureInPictureMode(self, reason: .exitButton)de(self, reason: .returnButton) -// -// if !NSApp.isActive { -// NSApp.activate(ignoringOtherApps: true) -// } -// -// if let window = lastKnownWindow { -// window.makeKeyAndOrderFront(pip) -// -// if window.isMiniaturized { -// window.deminiaturize(nil) -// } -// } -// } - -// public func pipActionPause(_ pip: PIPViewController) { -// pause(pip) -// } -// -// public func pipActionPlay(_ pip: PIPViewController) { -// play(pip) -// } - - public func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) { - isInPictureInPictureMode = false - pipController = nil - } - - public func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - print("Player Rate: \(player?.rate)") - isInPictureInPictureMode = false - pipController = nil - } - - public func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - isInPictureInPictureMode = true - } - -// public func pipDidClose(_ pip: PIPViewController) { -// pictureContainer.view.frame = bounds -// -// addSubview(pictureContainer.view, positioned: .below, relativeTo: scrimContainerView) -// -// isInPictureInPictureMode = false -// pipController = nil -// } - - public func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - delegate?.playerViewWillExitPictureInPictureMode(self, reason: .returnButton) - print("Player Rate: \(player?.rate)") +extension PUIPlayerView: /*PUIPictureContainerViewControllerDelegate,*/ AVPictureInPictureControllerDelegate { + public func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) { if !NSApp.isActive { NSApp.activate(ignoringOtherApps: true) } @@ -1686,26 +1622,30 @@ extension PUIPlayerView: PUIPictureContainerViewControllerDelegate, AVPictureInP window.deminiaturize(nil) } } + completionHandler(true) } -// public func pipWillClose(_ pip: PIPViewController) { -// pip.replacementRect = frame -// pip.replacementView = self -// pip.replacementWindow = lastKnownWindow -// } - - func pictureContainerViewSuperviewDidChange(to superview: NSView?) { - guard let superview = superview else { return } + public func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) { + isInPictureInPictureMode = false + } - pictureContainer.view.frame = superview.bounds + public func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + isInPictureInPictureMode = false + } - if superview == self, pipController != nil { - if pictureContainer.presentingViewController == pipController { - pipController?.stopPictureInPicture() - } + public func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + delegate?.playerViewWillEnterPictureInPictureMode(self) - pipController = nil + snapshotPlayer { [weak self] image in + self?.externalStatusController.snapshot = image } } + public func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + isInPictureInPictureMode = true + } + + public func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + delegate?.playerViewWillExitPictureInPictureMode(self, reason: .returnButton) + } } diff --git a/WWDC.xcodeproj/project.pbxproj b/WWDC.xcodeproj/project.pbxproj index c80beff3..79450fdb 100644 --- a/WWDC.xcodeproj/project.pbxproj +++ b/WWDC.xcodeproj/project.pbxproj @@ -147,7 +147,6 @@ DDF7219D1ECA12780054C503 /* PlayerUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDF721961ECA12780054C503 /* PlayerUI.framework */; }; DDF7219E1ECA12780054C503 /* PlayerUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DDF721961ECA12780054C503 /* PlayerUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; DDF721C71ECA12A40054C503 /* PUIExternalPlaybackStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF721A51ECA12A40054C503 /* PUIExternalPlaybackStatusViewController.swift */; }; - DDF721C81ECA12A40054C503 /* PUIPictureContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF721A61ECA12A40054C503 /* PUIPictureContainerViewController.swift */; }; DDF721C91ECA12A40054C503 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF721A81ECA12A40054C503 /* Colors.swift */; }; DDF721CA1ECA12A40054C503 /* Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF721A91ECA12A40054C503 /* Images.swift */; }; DDF721CB1ECA12A40054C503 /* Speeds.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF721AA1ECA12A40054C503 /* Speeds.swift */; }; @@ -414,7 +413,6 @@ DDF721981ECA12780054C503 /* PlayerUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlayerUI.h; sourceTree = ""; }; DDF721991ECA12780054C503 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DDF721A51ECA12A40054C503 /* PUIExternalPlaybackStatusViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PUIExternalPlaybackStatusViewController.swift; sourceTree = ""; }; - DDF721A61ECA12A40054C503 /* PUIPictureContainerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PUIPictureContainerViewController.swift; sourceTree = ""; }; DDF721A81ECA12A40054C503 /* Colors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; DDF721A91ECA12A40054C503 /* Images.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Images.swift; sourceTree = ""; }; DDF721AA1ECA12A40054C503 /* Speeds.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Speeds.swift; sourceTree = ""; }; @@ -986,7 +984,6 @@ isa = PBXGroup; children = ( DDF721A51ECA12A40054C503 /* PUIExternalPlaybackStatusViewController.swift */, - DDF721A61ECA12A40054C503 /* PUIPictureContainerViewController.swift */, DDC6781E1EDB8EDA00A4E19C /* PUIAnnotationWindowController.swift */, ); path = Controllers; @@ -1545,7 +1542,6 @@ buildActionMask = 2147483647; files = ( DDF721D11ECA12A40054C503 /* PUITimelineAnnotation.swift in Sources */, - DDF721C81ECA12A40054C503 /* PUIPictureContainerViewController.swift in Sources */, DDF721D51ECA12A40054C503 /* AVPlayer+Validation.swift in Sources */, DDF721CA1ECA12A40054C503 /* Images.swift in Sources */, 4DA83FE222AC3F2F0062DB8B /* PUIVibrantBackgroundButton.swift in Sources */,