2
votes

I have a custom view controller that can go fullscreen with a button. it normally is a subview of a view (embedded). My enter fullscreen from embed is this:

private func enterFullScreenFromEmbed() {

    self.proxyView = UIView(frame: self.view.frame)
    self.proxyView?.isHidden = true
    self.proxyView?.autoresizingMask = self.view.autoresizingMask
    self.view.superview?.addSubview(self.proxyView!)

    // Now set the frame to the screen frame
    let frame = self.view.window?.convert(self.view.frame, from: self.proxyView?.superview)
    self.view.window?.addSubview(self.view)
    self.view.frame = frame!

    self.isFullscreen = true

    UIView.animate(withDuration: 0.25) { 
        self.view.frame = self.view.window!.bounds
        self.view.layoutIfNeeded()
        self.setNeedsStatusBarAppearanceUpdate()
    }
}

And exiting from fullscreen:

private func exitFullScreenToEmbed() {

    let frame = self.view.window?.convert(self.view.frame, to: self.proxyView?.superview)
    self.proxyView?.superview?.addSubview(self.view)
    self.view.frame = frame!

    self.isFullscreen = false

    UIView.animate(withDuration: 0.25, animations: {

        self.view.frame = self.proxyView!.frame

        self.view.layoutIfNeeded()
        self.setNeedsStatusBarAppearanceUpdate()
    }) { (_) in
        self.proxyView?.removeFromSuperview()
        self.proxyView = nil
    }
}

This works fine, except that I hide the status bar in the enter fullscreen animation, and show it in the exit fullscreen animation. This causes my top view to jump back into place, without animating.

Note, the isFullscreen variable is what hides the status bar.

override var prefersStatusBarHidden: Bool {
    return isFullscreen
}

override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
    return .slide
}

Here are gifs of the animations:

Horizonal

Vertical

Notice the top of both and the bottom of the landscape one (easier to see on landscape). On the top, the player frame gets instantly set to the old position, and that moves everything down by 20px (or whatever the height of the status bar is)

It has something to do with the hidden status bar. Does anyone have a solution?

1

1 Answers

0
votes

I figured it out. Its a kind of hacky and not fully tested, but here is the solution I came up with.

First, get a variable to save the status bar height (not 0).

var statusBarHeight: CGFloat = UIApplication.shared.statusBarFrame.height != 0 ? UIApplication.shared.statusBarFrame.height : 20.0

Then, set up notifications for a status bar frame change.

NotificationCenter.default.addObserver(self, selector: #selector(SKPlayerViewController.updateLocalStatusBarFrameHeight), name: .UIApplicationDidChangeStatusBarFrame, object: nil)

Now, in the method, update your variable only if the height is not 0. We save the height because the status bar might be not 20 (if in call, etc).

@objc private func updateLocalStatusBarFrameHeight() {
        let height = UIApplication.shared.statusBarFrame.height
        if height > 0 {
            self.statusBarHeight = height
        }
    }

Now, I updated my exitFullScreenToEmbed() to subtract the calculated frames origin by the statusBarHeight only if not landscape (because the status bar is hidden in landscape).

private func exitFullScreenToEmbed() {

    var frame = self.view.window?.convert(self.view.frame, to: self.proxyView?.superview)
    self.proxyView?.superview?.addSubview(self.view)

    if !(UIApplication.shared.statusBarOrientation == .landscapeRight || UIApplication.shared.statusBarOrientation == .landscapeLeft) {
        frame?.origin.y -= self.statusBarHeight
    }

    self.view.frame = frame!

    self.isFullscreen = false

    UIView.animate(withDuration: 0.25, animations: {

        self.view.frame = self.proxyView!.frame

        self.view.layoutIfNeeded()
        self.setNeedsStatusBarAppearanceUpdate()
    }) { (_) in
        self.proxyView?.removeFromSuperview()
        self.proxyView = nil
    }
}

If this helped anyone, you're welcome. I'm just here to share some knowledge :)