4

I am playing a video using an AVPlayer which is about 320x200 frame in my app. The avplayer also has a custom 'fullscreen' button added as overlay, like the youtube app player. How can I implement it such that when the app is in portrait mode and the user clicks fullscreen button, the video will rotate to fullscreen but in landscape mode? I tried using transform and it works partially, because when its in fullscreen mode, if user switches device to portrait orientation; the avplayer frame changes abruptly. I want it to work like the youtube app i.e. when in fullscreen mode, it should stay so even if user rotates device. Only should return to original size when turn off fullscreen. I don't want to use avplayerviewcontroller due to design of the app. Thanks in advance for help.

King David
  • 139
  • 1
  • 9
  • 1
    When video is playing, and full screen, set the `supportedInterfaceOrientations` to only landscape – William GP May 16 '16 at 09:21
  • This works alongwith the answer given below, can you also see my comments to it for an issue with turn off fullscreen implementation? – King David May 16 '16 at 10:10
  • To clarify: your issue is when you dismiss the fullscreen player - the smaller player doesn't know to keep playing and from where? – William GP May 16 '16 at 10:17
  • If so, try passing variables through an `unwindSegue` as a possible solution – William GP May 16 '16 at 10:29
  • I'm not using storyboard actually. What happens is when the turn off fullscreen button is pressed on the player, the presenting controller dismisses the full screen controller. The player container which was passed to the modal is the container view of the presenting controller. To simplify, VC A has subview SV which contains the player and controls. On fullscreen, VC A presents VC B with SV. VC B sets its view as SV. On fullscreen OFF, VC A dismisses VC B. Now SV in VC A is blank. – King David May 16 '16 at 10:45
  • @WilliamGP Ok I simply readded the video container view as a subview to the presenting view and it works fine.. however the overlay controls no longer shows. Any idea what could be happening? – King David May 17 '16 at 07:28
  • Could be a layer issue - see "Managing the View's Layer" https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSView_Class/#//apple_ref/doc/uid/20000014-SW58 – William GP May 17 '16 at 09:52

2 Answers2

9

Great question! Allowing the AVPLayerLayer to go fullscreen is important. A considerable amount of app configuration for a single view to handle both portrait and landscape, just to display a full screen video? Please.

Transforms and frame manipulation can solve this issue:

extension CGAffineTransform {

    static let ninetyDegreeRotation = CGAffineTransform(rotationAngle: CGFloat(M_PI / 2))
}

extension AVPlayerLayer {

    var fullScreenAnimationDuration: TimeInterval {
        return 0.15
    }

    func minimizeToFrame(_ frame: CGRect) {
        UIView.animate(withDuration: fullScreenAnimationDuration) {
            self.setAffineTransform(.identity)
            self.frame = frame
        }
    }

    func goFullscreen() {
        UIView.animate(withDuration: fullScreenAnimationDuration) {
            self.setAffineTransform(.ninetyDegreeRotation)
            self.frame = UIScreen.main.bounds
        }
    }
}

Setting the frame of the AVPlayerLayer changes it's parent's frame. Save the original frame in your view subclass, to minimize the AVPLayerLayer back to where it was. This allows for autolayout.

IMPORTANT - This only works if the player is in the center of your view subclass.

Incomplete example:

class AVPlayerView: UIView {

    fileprivate var avPlayerLayer: AVPlayerLayer {
        return layer as! AVPlayerLayer
    }

    fileprivate var hasGoneFullScreen = false
    fileprivate var isPlaying = false
    fileprivate var originalFrame = CGRect.zero

    func togglePlayback() {
        if !hasGoneFullScreen {
            originalFrame = frame
            hasGoneFullScreen = true
        }

        isPlaying = !isPlaying
        if isPlaying {
            avPlayerLayer.goFullscreen()
            avPlayerLayer.player?.play()
        } else {
            avPlayerLayer.player?.pause()
            avPlayerLayer.minimizeToFrame(originalFrame)
        }
    }
}
Eric Armstrong
  • 646
  • 6
  • 17
4

In my opinion, you should create a FullScreenViewController, which is forced always supporting landscape. Then present that view controller from your current one when press "fullscreen" button and also pass AVPlayer instance to it, then set frame for AVPlayerLayer and resume playing. After dismissing, it would be back to normal, I mean your "portrait" mode.

HSG
  • 1,224
  • 11
  • 17
  • This is what I have thought as well as a possible solution.. Hoping to see if there's any other way. – King David May 16 '16 at 09:28
  • I tried this solution with FullScreenViewController and passed the container UIView which has the AVPlayerLayer as well as the custom controls UIView. So far so good. However on clicking turn OFF fullscreen, I dismiss the FullScreenViewController, however the player does not play in my presenting view. How can I implement turn off fullscreen in the FullScreenViewController? because I think that since it gets dismissed, its view i.e. the player container view gets deallocated. – King David May 16 '16 at 10:09
  • Some code: `FullScreenViewController *controller = [[FullScreenViewController alloc] init]; controller.view = self.playerContainer; // the container uiview [self presentViewController:controller animated:YES completion:nil];` – King David May 16 '16 at 10:12
  • Ok I simply readded the video container view as a subview to the presenting view and it works fine.. however the overlay controls view no longer shows. Any idea what could be happening? – King David May 17 '16 at 07:27
  • If you are using a custom View to present the video, there is no longer built-in controls supported, you have to create your own UI controls such as control bottom bar and header bar. – HSG May 17 '16 at 07:33
  • Yes I'm doing that. The avplayer is added as sublayer to my videoContainer view and the UI controls are added as subviews to the videoContainer. However the controls dont display after dismiss fullscreen. – King David May 17 '16 at 07:55