@@ -87,13 +87,20 @@ public final class PUIPlayerView: NSView {
8787 public var mediaTitle : String ?
8888 public var mediaIsLiveStream : Bool = false
8989
90- var pictureContainer : PUIPictureContainerViewController !
91-
9290 public init ( player: AVPlayer ) {
9391 self . player = player
92+ self . pipController = AVPictureInPictureController ( contentSource: . init( playerLayer: playerLayer) )
9493
9594 super. init ( frame: . zero)
9695
96+ pipPossibleObservation = pipController. observe (
97+ \AVPictureInPictureController . isPictureInPicturePossible, options: [ . initial, . new]
98+ ) { [ weak self] _, change in
99+ // Update the PiP button's enabled state.
100+ self ? . pipButton. isEnabled = change. newValue ?? false
101+ }
102+
103+ pipController. delegate = self
97104 wantsLayer = true
98105 layer = PUIBoringLayer ( )
99106 layer? . backgroundColor = NSColor . black. cgColor
@@ -217,7 +224,7 @@ public final class PUIPlayerView: NSView {
217224 return player? . currentItem? . asset
218225 }
219226
220- private var playerLayer = PUIBoringPlayerLayer ( )
227+ private let playerLayer = PUIBoringPlayerLayer ( )
221228
222229 private func setupPlayer( ) {
223230 elapsedTimeLabel. stringValue = elapsedTimeInitialValue
@@ -229,15 +236,6 @@ public final class PUIPlayerView: NSView {
229236 playerLayer. player = player
230237 playerLayer. videoGravity = . resizeAspect
231238
232- if pictureContainer == nil {
233- pictureContainer = PUIPictureContainerViewController ( playerLayer: playerLayer)
234- pictureContainer. delegate = self
235- pictureContainer. view. frame = bounds
236- pictureContainer. view. autoresizingMask = [ . width, . height]
237-
238- addSubview ( pictureContainer. view)
239- }
240-
241239 player. addObserver ( self , forKeyPath: #keyPath( AVPlayer . status) , options: [ . initial, . new] , context: nil )
242240 player. addObserver ( self , forKeyPath: #keyPath( AVPlayer . volume) , options: [ . initial, . new] , context: nil )
243241 player. addObserver ( self , forKeyPath: #keyPath( AVPlayer . rate) , options: [ . initial, . new] , context: nil )
@@ -341,8 +339,6 @@ public final class PUIPlayerView: NSView {
341339 }
342340
343341 fileprivate func updatePlayingState( ) {
344- // pipController?.setPlaying(isPlaying)
345-
346342 if isPlaying {
347343 playButton. image = . PUIPause
348344 } else {
@@ -651,6 +647,7 @@ public final class PUIPlayerView: NSView {
651647 b. target = self
652648 b. action = #selector( togglePip)
653649 b. toolTip = " Toggle picture in picture "
650+ b. isEnabled = false
654651
655652 return b
656653 } ( )
@@ -662,6 +659,17 @@ public final class PUIPlayerView: NSView {
662659 private func setupControls( ) {
663660 externalStatusController. view. isHidden = true
664661 externalStatusController. view. translatesAutoresizingMaskIntoConstraints = false
662+ let playerView = NSView ( )
663+ playerView. translatesAutoresizingMaskIntoConstraints = false
664+ playerView. wantsLayer = true
665+ playerView. layer = playerLayer
666+ playerLayer. backgroundColor = . clear
667+ addSubview ( playerView)
668+ playerView. leadingAnchor. constraint ( equalTo: leadingAnchor) . isActive = true
669+ playerView. trailingAnchor. constraint ( equalTo: trailingAnchor) . isActive = true
670+ playerView. topAnchor. constraint ( equalTo: topAnchor) . isActive = true
671+ playerView. bottomAnchor. constraint ( equalTo: bottomAnchor) . isActive = true
672+
665673 addSubview ( externalStatusController. view)
666674 externalStatusController. view. leadingAnchor. constraint ( equalTo: leadingAnchor) . isActive = true
667675 externalStatusController. view. trailingAnchor. constraint ( equalTo: trailingAnchor) . isActive = true
@@ -1020,9 +1028,9 @@ public final class PUIPlayerView: NSView {
10201028
10211029 @IBAction public func togglePip( _ sender: NSView ? ) {
10221030 if isInPictureInPictureMode {
1023- pipController? . stopPictureInPicture ( )
1031+ pipController. stopPictureInPicture ( )
10241032 } else {
1025- enterPictureInPictureMode ( )
1033+ pipController . startPictureInPicture ( )
10261034 }
10271035 }
10281036
@@ -1268,24 +1276,8 @@ public final class PUIPlayerView: NSView {
12681276 }
12691277 }
12701278
1271- fileprivate var pipController : AVPictureInPictureController ?
1272-
1273- fileprivate func enterPictureInPictureMode( ) {
1274- delegate? . playerViewWillEnterPictureInPictureMode ( self )
1275-
1276- snapshotPlayer { [ weak self] image in
1277- self ? . externalStatusController. snapshot = image
1278- }
1279-
1280- pipController = AVPictureInPictureController ( playerLayer: playerLayer)
1281- pipController? . delegate = self
1282- // pipController?.setPlaying(isPlaying)
1283- // pipController?.aspectRatio = currentPresentationSize ?? NSSize(width: 640, height: 360)
1284- // pipController?.view.layer?.backgroundColor = NSColor.black.cgColor
1285-
1286- // pipController?.presentAsPicture(inPicture: pictureContainer)
1287- pipController? . startPictureInPicture ( )
1288- }
1279+ fileprivate let pipController : AVPictureInPictureController
1280+ private var pipPossibleObservation : Any ?
12891281
12901282 // MARK: - Visibility management
12911283
@@ -1638,65 +1630,9 @@ extension PUIPlayerView: PUIExternalPlaybackConsumer {
16381630
16391631// MARK: - PiP delegate
16401632
1641- extension PUIPlayerView : PUIPictureContainerViewControllerDelegate , AVPictureInPictureControllerDelegate {
1642-
1643- // public func pipActionStop(_ pip: PIPViewController) {
1644- // pause(pip)
1645- //
1646- // }
1647- //
1648- // public func pipActionReturn(_ pip: PIPViewController) {
1649- // delegate?.playerViewWillExitPictureInPictureModelegate?.playerViewWillExitPictureInPictureMode(self, reason: .exitButton)de(self, reason: .returnButton)
1650- //
1651- // if !NSApp.isActive {
1652- // NSApp.activate(ignoringOtherApps: true)
1653- // }
1654- //
1655- // if let window = lastKnownWindow {
1656- // window.makeKeyAndOrderFront(pip)
1657- //
1658- // if window.isMiniaturized {
1659- // window.deminiaturize(nil)
1660- // }
1661- // }
1662- // }
1663-
1664- // public func pipActionPause(_ pip: PIPViewController) {
1665- // pause(pip)
1666- // }
1667- //
1668- // public func pipActionPlay(_ pip: PIPViewController) {
1669- // play(pip)
1670- // }
1671-
1672- public func pictureInPictureController( _ pictureInPictureController: AVPictureInPictureController , failedToStartPictureInPictureWithError error: Error ) {
1673- isInPictureInPictureMode = false
1674- pipController = nil
1675- }
1676-
1677- public func pictureInPictureControllerDidStopPictureInPicture( _ pictureInPictureController: AVPictureInPictureController ) {
1678- print ( " Player Rate: \( player? . rate) " )
1679- isInPictureInPictureMode = false
1680- pipController = nil
1681- }
1682-
1683- public func pictureInPictureControllerDidStartPictureInPicture( _ pictureInPictureController: AVPictureInPictureController ) {
1684- isInPictureInPictureMode = true
1685- }
1686-
1687- // public func pipDidClose(_ pip: PIPViewController) {
1688- // pictureContainer.view.frame = bounds
1689- //
1690- // addSubview(pictureContainer.view, positioned: .below, relativeTo: scrimContainerView)
1691- //
1692- // isInPictureInPictureMode = false
1693- // pipController = nil
1694- // }
1695-
1696- public func pictureInPictureControllerWillStopPictureInPicture( _ pictureInPictureController: AVPictureInPictureController ) {
1697- delegate? . playerViewWillExitPictureInPictureMode ( self , reason: . returnButton)
1698- print ( " Player Rate: \( player? . rate) " )
1633+ extension PUIPlayerView : /*PUIPictureContainerViewControllerDelegate,*/ AVPictureInPictureControllerDelegate {
16991634
1635+ public func pictureInPictureController( _ pictureInPictureController: AVPictureInPictureController , restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping ( Bool ) -> Void ) {
17001636 if !NSApp. isActive {
17011637 NSApp . activate ( ignoringOtherApps: true )
17021638 }
@@ -1708,26 +1644,30 @@ extension PUIPlayerView: PUIPictureContainerViewControllerDelegate, AVPictureInP
17081644 window. deminiaturize ( nil )
17091645 }
17101646 }
1647+ completionHandler ( true )
17111648 }
17121649
1713- // public func pipWillClose(_ pip: PIPViewController) {
1714- // pip.replacementRect = frame
1715- // pip.replacementView = self
1716- // pip.replacementWindow = lastKnownWindow
1717- // }
1718-
1719- func pictureContainerViewSuperviewDidChange( to superview: NSView ? ) {
1720- guard let superview = superview else { return }
1650+ public func pictureInPictureController( _ pictureInPictureController: AVPictureInPictureController , failedToStartPictureInPictureWithError error: Error ) {
1651+ isInPictureInPictureMode = false
1652+ }
17211653
1722- pictureContainer. view. frame = superview. bounds
1654+ public func pictureInPictureControllerDidStopPictureInPicture( _ pictureInPictureController: AVPictureInPictureController ) {
1655+ isInPictureInPictureMode = false
1656+ }
17231657
1724- if superview == self , pipController != nil {
1725- if pictureContainer. presentingViewController == pipController {
1726- pipController? . stopPictureInPicture ( )
1727- }
1658+ public func pictureInPictureControllerWillStartPictureInPicture( _ pictureInPictureController: AVPictureInPictureController ) {
1659+ delegate? . playerViewWillEnterPictureInPictureMode ( self )
17281660
1729- pipController = nil
1661+ snapshotPlayer { [ weak self] image in
1662+ self ? . externalStatusController. snapshot = image
17301663 }
17311664 }
17321665
1666+ public func pictureInPictureControllerDidStartPictureInPicture( _ pictureInPictureController: AVPictureInPictureController ) {
1667+ isInPictureInPictureMode = true
1668+ }
1669+
1670+ public func pictureInPictureControllerWillStopPictureInPicture( _ pictureInPictureController: AVPictureInPictureController ) {
1671+ delegate? . playerViewWillExitPictureInPictureMode ( self , reason: . returnButton)
1672+ }
17331673}
0 commit comments