Как заставить WKWebView игнорировать аппаратный бесшумный переключатель на iOS? - PullRequest
0 голосов
/ 05 июня 2019

Задача заключается в том, чтобы воспроизводить все виды веб-звуков независимо от аппаратного переключателя тишины, как на приглушенных, так и не приглушенных устройствах. должен продолжать воспроизводить звук на страницах HTML, пока приложение находится на заднем плане.Решение для устарело UIWebView довольно просто

let localWebView = UIWebView(frame: .zero)
localWebView.allowsInlineMediaPlayback = true
localWebView.mediaPlaybackRequiresUserAction = false

Как такое же поведение может быть достигнуто для WKWebView?

1 Ответ

4 голосов
/ 05 июня 2019

Поскольку у меня есть решение этой нетривиальной проблемы, я хотел бы поделиться им:

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive),
                                               name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(willResignActive),
                                               name: NSNotification.Name.UIApplicationWillResignActive, object: nil)

}

@objc func willResignActive() {
    if let wkWebView = wkWebView {
       disableIgnoreSilentSwitch(wkWebView)
    }
}

@objc func didBecomeActive() {
if let wkWebView = wkWebView {
    //Always creates new js Audio object to ensure the audio session behaves correctly
    forceIgnoreSilentHardwareSwitch(wkWebView, initialSetup: false)
    }
}   

let configuration = WKWebViewConfiguration()
configuration.allowsInlineMediaPlayback = true
configuration.mediaTypesRequiringUserActionForPlayback = []
let localWebView = WKWebView(frame: .zero, configuration: configuration)

И что самое важное в WKNavigationDelegate:

private func disableIgnoreSilentSwitch(_ webView: WKWebView) {
    //Nullifying the js Audio object src is critical to restore the audio sound session to consistent state for app background/foreground cycle
    let jsInject = "document.getElementById('wkwebviewAudio').src=null;"
    webView.evaluateJavaScript(jsInject, completionHandler: nil)
}

private func forceIgnoreSilentHardwareSwitch(_ webView: WKWebView, initialSetup: Bool) {
    //after some trial and error this seems to be minimal silence sound that still plays
    let silenceMono56kbps100msBase64Mp3 = "data:audio/mp3;base64,//tAxAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAAAFAAAESAAzMzMzMzMzMzMzMzMzMzMzMzMzZmZmZmZmZmZmZmZmZmZmZmZmZmaZmZmZmZmZmZmZmZmZmZmZmZmZmczMzMzMzMzMzMzMzMzMzMzMzMzM//////////////////////////8AAAA5TEFNRTMuMTAwAZYAAAAAAAAAABQ4JAMGQgAAOAAABEhNIZS0AAAAAAD/+0DEAAPH3Yz0AAR8CPqyIEABp6AxjG/4x/XiInE4lfQDFwIIRE+uBgZoW4RL0OLMDFn6E5v+/u5ehf76bu7/6bu5+gAiIQGAABQIUJ0QolFghEn/9PhZQpcUTpXMjo0OGzRCZXyKxoIQzB2KhCtGobpT9TRVj/3Pmfp+f8X7Pu1B04sTnc3s0XhOlXoGVCMNo9X//9/r6a10TZEY5DsxqvO7mO5qFvpFCmKIjhpSItGsUYcRO//7QsQRgEiljQIAgLFJAbIhNBCa+JmorCbOi5q9nVd2dKnusTMQg4MFUlD6DQ4OFijwGAijRMfLbHG4nLVTjydyPlJTj8pfPflf9/5GD950A5e+jsrmNZSjSirjs1R7hnkia8vr//l/7Nb+crvr9Ok5ZJOylUKRxf/P9Zn0j2P4pJYXyKkeuy5wUYtdmOu6uobEtFqhIJViLEKIjGxchGev/L3Y0O3bwrIOszTBAZ7Ih28EUaSOZf/7QsQfg8fpjQIADN0JHbGgQBAZ8T//y//t/7d/2+f5m7MdCeo/9tdkMtGLbt1tqnabRroO1Qfvh20yEbei8nfDXP7btW7f9/uO9tbe5IvHQbLlxpf3DkAk0ojYcv///5/u3/7PTfGjPEPUvt5D6f+/3Lea4lz4tc4TnM/mFPrmalWbboeNiNyeyr+vufttZuvrVrt/WYv3T74JFo8qEDiJqJrmDTs///v99xDku2xG02jjunrICP/7QsQtA8kpkQAAgNMA/7FgQAGnobgfghgqA+uXwWQ3XFmGimSbe2X3ksY//KzK1a2k6cnNWOPJnPWUsYbKqkh8RJzrVf///P///////4vyhLKHLrCb5nIrYIUss4cthigL1lQ1wwNAc6C1pf1TIKRSkt+a//z+yLVcwlXKSqeSuCVQFLng2h4AFAFgTkH+Z/8jTX/zr//zsJV/5f//5UX/0ZNCNCCaf5lTCTRkaEdhNP//n/KUjf/7QsQ5AEhdiwAAjN7I6jGddBCO+WGTQ1mXrYatSAgaykxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg=="
    //Plays 100ms silence once the web page has loaded through HTML5 Audio element (through Javascript)
    //which as a side effect will switch WKWebView AudioSession to AVAudioSessionCategoryPlayback

    var jsInject: String
    if initialSetup {
        jsInject = "var s=new Audio('\(silenceMono56kbps100msBase64Mp3)');s.id='wkwebviewAudio';s.controls=false;s.loop=true;s.play();document.body.appendChild(s)"
    } else {
        jsInject = "var s=document.getElementById('wkwebviewAudio');s.src=null;s.parentNode.removeChild(s);s=null;s=new Audio('\(silenceMono56kbps100msBase64Mp3)');s.id='wkwebviewAudio';s.controls=false;s.loop=true;s.play();document.body.appendChild(s)"
    }
    webView.evaluateJavaScript(jsInject, completionHandler: nil)
}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    //As a result the WKWebView ignores the silent switch for games
    forceIgnoreSilentHardwareSwitch(webView, initialSetup: true)
}

Интересно, чтоПроблема Safari упоминается здесь: IOS WebAudio работает только на наушниках , где обходной путь @Spencer Evans выглядит очень похоже на мой.

Однако, когда я попытался применить его более короткий звук тишины base64, он неработать на WKWebView, поэтому я предоставляю свой собственный минимальный звук тишины, протестированный на iOS12.

Почему это работает?

Воспроизведение <audio> или <video> элемент (который в обходном случае оказывается не слышимым тишиной) изменяет WKWebView категорию аудио-сеанса с AVAudioSessionCategoryAmbient на AVAudioSessionCategoryPlayback.Это будет действовать до тех пор, пока следующий запрос на загрузку не сбросит его.

Это все замечательно, пока приложение не станет фоновым.Но при последующих приоритетах все будет сломано двумя возможными способами:

  • пользователю нужно нажать, чтобы звуки снова появились
  • редко ввод пользователя не поможет, и WKWebView приземляется в полузамерзшем состоянии

К счетчик , что ^ взломать с помощью disableIgnoreSilentSwitch(wkWebView) и позже включить с помощью forceIgnoreSilentHardwareSwitch(wkWebView, initialSetup: false)

Поскольку ядро ​​WKWebView работает во внешнем процессек нему нельзя получить доступ, как UIWebView совместно (с нашим приложением) AVAudioSession может быть.

Проверено для iOS 11.4 - 13 бета

...