DMCApps
DMCApps

Reputation: 2168

youtube iFrame in iOS application causes half screen to go black after full screen

I have a WKWebView in my UIViewController and I'm embedding a youtube video (using an iFrame) into the html along with text. When I launch the video it performs as intended, it opens in full screen, I can rotate either landscape orientations, plays fine etc. The problem lies when I close the video. My application is locked to portrait and when returning from the video the application is half black screen, half my application and the view that is still half my application looks to be in landscape (too larger for the width).

My application is locked portrait within the Info.plist for the whole application. I am fine with the video rotating it just causes this interesting outcome.

1. Launch WKWebView with youtube iframe
2. Click to launch video (video plays full screen)
3. Rotate device to either landscape rotations.
4. Close the player
This is where you notice that half the application is black and the other half looks to be portrait orientation in landscape layout.

When I inspect the views before and after it mimics as if the app has rotated to landscape mode but the device is in portrait. (View starts at 414x862. After viewing and rotating with the video and returning to the view it shows as 862x414)

I'm not really sure what's going on here. Thoughts?

Thanks in advance

Upvotes: 2

Views: 648

Answers (1)

DMCApps
DMCApps

Reputation: 2168

I was able to find a workaround/hack/solution to this. I kept my application locked to portrait but overrode the AppDelegate method for allowed orientations.

class AppDelegate: UIResponder, UIApplicationDelegate {

    ...

    func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        return OrientationManager.allowedOrientations
    }

    ...

}

I created an OrientationManager which was just a helper class to allow me to force rotations and alter the allowed orientations.

class OrientationManager {
    /// The currently allowed orientations of the application (default: .portrait)
    static var allowedOrientations: UIInterfaceOrientationMask = .portrait

    /**
     * Method to force the current orientation of the device.
     *
     * - Parameter orientation: The orientation to change to.
     *
     * - Warning: This method uses setValue(_, forKey:) which accesses values that may change in the future
     */
    static func forceOrientation(_ orientation: UIInterfaceOrientation) {
        let orientationRawValue = orientation.rawValue
        UIDevice.current.setValue(orientationRawValue, forKey: "orientation")
        UINavigationController.attemptRotationToDeviceOrientation()
    }
}

The last thing I needed to do was figure out when the video was going in and out of full screen. I found some notifications that seemed to reliably fire when the video goes in and out of full screen. When this happens I enable the ability for the view to rotate, which allows it to go to Landscape behind the full screen video. Once the video is closed my view is now (unfortunately) showing in landscape which I can then force the orientation and re-lock it to portrait.

In the UIViewController with the webview:

class WebViewWithVideoViewController: UIViewController {

    ...
    // MARK: Properties
    var videoDidFullScreenNotification: NSObjectProtocol?
    var videoDidMinimizeNotification: NSObjectProtocol?
    ...

    // MARK: Lifecycle
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        self.beginObserving()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        self.endObserving()
    }

    // MARK: Observers

    func beginObserving() {
        // This is a solution to the video causing a half black screen when the device is rotated to landscape.
        // The outline of the solution is that when the video is put into full screen mode, we update the
        // available orientations to include both landscape directions. Upon the video being closed,
        // we force the device orientation back to portrait and then lock it back to portrait only.
        // This seems to work because the UIViewController is actually in landscape once complete
        // and then it animates back to the portrait orientation and seems to keep the proper ratios
        // and sizes.
        if self.videoDidFullScreenNotification == nil {
            self.videoDidFullScreenNotification = NotificationCenter.default.addObserver(
                forName: UIWindow.didBecomeVisibleNotification,
                object: self.view.window,
                queue: nil
            ) { notification in
                OrientationManager.allowedOrientations = .allButUpsideDown
            }
        }

        if self.videoDidMinimizeNotification == nil {
            self.videoDidMinimizeNotification = NotificationCenter.default.addObserver(
                forName: UIWindow.didBecomeHiddenNotification,
                object: self.view.window,
                queue: nil
            ) { notification in
                OrientationManager.forceOrientation(.portrait)
                OrientationManager.allowedOrientations = .portrait
            }
        }
    }

    func endObserving() {
        if let videoDidFullScreenNotification = self.videoDidFullScreenNotification {
            NotificationCenter.default.removeObserver(videoDidFullScreenNotification)
        }

        if let videoDidMinimizeNotification = self.videoDidMinimizeNotification {
            NotificationCenter.default.removeObserver(videoDidMinimizeNotification)
        }
    }

    ...
}

This seemed to have solved the screen showing half black after going in and out of full screen with embedded videos. Unfortunately there is a slight animation when you return from the full screen video, but it's a small sacrifice for a very weird bug.

Hope this helps anyone else with this (or similar issues) and happy coding!

Upvotes: 1

Related Questions