Search code examples
iosswiftuiwebviewmpmovieplayercontroller

Fullscreen video in UIWebView interferes with UIViewController's input accessory view


I have a view which I set up as input accessory view for view controller the following way:

@IBOutlet private weak var bottomPane: UIView!

override func canBecomeFirstResponder() -> Bool {
    return true
}

override var inputAccessoryView: UIView {
    return bottomPane
}

Everything works just fine until I try to view YouTube video in fullscreen mode (video is loaded in UIWebView). When video enters fullscreen mode, keyboard and my input accessory view disappear (which is normal, I guess), but when I exit fullscreen mode, they do not appear. If I keep the reference to bottomPane weak, it becomes nil and application crashes, if I change it to strong, input accessory view remains hidden until the keyboard appears next time.

Can anybody explain what's going on and how to fix this?


Solution

  • Here's what's going on.

    When user interacts with UIWebView, it becomes first responder and inputAccessoryView provided by view controller disappears (no idea why behavior in this case is different from, say, UITextField). Subclassing UIWebView and overriding inputAccessoryView property does not work (never gets called). So I block interaction with UIWebView until user loads video.

    private func displayVideo(URL: String) {        
        if let video = Video(videoURL: URL) {
            // load video in webView 
            webView.userInteractionEnabled = true
        } else {
            webView.userInteractionEnabled = false
        }
    }
    

    When user loads video, the only way to detect that user has entered/exited fullscreen mode is to listen to UIWindowDidBecomeKeyNotification and UIWindowDidResignKeyNotification and detect when our window loses/gains key status:

    //in view controller:
    private func windowDidBecomeKey(notification: NSNotification!) {
        let isCurrentWindow = (notification.object as! UIWindow) == view.window
    
        if isCurrentWindow {
            // this restores our inputAccessoryView
            becomeFirstResponder()
        }
    }
    
    private func windowDidResignKey(notification: NSNotification!) {
        let isCurrentWindow = (notification.object as! UIWindow) == view.window
    
        if isCurrentWindow {
            // this hides our inputAccessoryView so that it does not obscure video
            resignFirstResponder()
        }
    }
    

    And, of course, since inputAccessoryView can be removed at some point, we should recreate it if needed:

    //in view controller:
    override var inputAccessoryView: UIView {
        if view == nil {
            // load view here
        }
    
        return view
    }