Поскольку у меня есть решение этой нетривиальной проблемы, я хотел бы поделиться им:
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 бета