【问题标题】:How to force WKWebView to ignore hardware silent switch on iOS?如何强制 WKWebView 忽略 iOS 上的硬件静音开关?
【发布时间】:2019-06-05 12:18:57
【问题描述】:

这里的要求是播放各种网络声音,不管硬件静音开关如何,静音和非静音设备必须在应用程序处于前台时在 HTML 页面中继续播放声音。 deprecated UIWebView 的解决方案非常简单

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

WKWebView 如何实现相同的行为?

【问题讨论】:

    标签: ios swift wkwebview


    【解决方案1】:

    更新:添加了同样适用于 iOS 14(和 15)的新 hack! (反映在代码中,更多细节见底部)。
    既然我有解决这个重要问题的方法,我想分享一下:

    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)
        let configuration = WKWebViewConfiguration()
        configuration.allowsInlineMediaPlayback = true
        configuration.mediaTypesRequiringUserActionForPlayback = []
        wkWebView = WKWebView(frame: .zero, configuration: configuration)
    }
    
    @objc func willResignActive() {
        disableIgnoreSilentSwitch(wkWebView)
    }
    
    @objc func didBecomeActive() {
        //Always creates new js Audio object to ensure the audio session behaves correctly
        forceIgnoreSilentHardwareSwitch(wkWebView, initialSetup: false)
    }   
    
    
    

    最重要的是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').muted=true;"
        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.play();" +
                "s.loop=true;" +
                "document.body.appendChild(s);"
        } else {
            //Restore sound hack
            jsInject = "document.getElementById('wkwebviewAudio').muted=false;"
        }
        webView.evaluateJavaScript(jsInject, completionHandler: nil)
    }
    
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        //As a result the WKWebView ignores the silent switch
        forceIgnoreSilentHardwareSwitch(webView, initialSetup: true)
    }
    

    有趣的是,这里提到了一个相关的 Safari 问题:IOS WebAudio only works on headphones @Spencer Evans 的解决方法看起来与我的非常相似。

    但是,当我尝试应用他较短的 base64 静音声音时,它不适用于 WKWebView,因此我提供了我自己在 iOS12 上测试的最小静音声音。

    为什么有效?

    播放 <audio><video> 元素(在解决方法中恰好是无声静音)会将 WKWebView 音频会话类别从 AVAudioSessionCategoryAmbient 更改为 AVAudioSessionCategoryPlayback。这将一直有效,直到下一个加载请求将其重置。

    在应用程序进入后台之前一切都很好。但是在随后的前景化中,事情将以两种可能的方式中断:

    • 用户需要点击才能重新出现声音
    • 很少有用户输入会有所帮助,WKWebView 会进入半冻结状态

    反驳^ 黑客被disableIgnoreSilentSwitch(wkWebView) 恢复,后来又被forceIgnoreSilentHardwareSwitch(wkWebView, initialSetup: false) 重新启用

    由于WKWebView 核心在外部进程中运行,因此无法像UIWebView 共享(使用我们的应用程序)AVAudioSession 那样访问它。

    已验证
    iOS 11.4
    iOS 12.4.1
    iOS 13.3
    iOS 14.1
    iOS 14.5.1
    iOS 14.8
    iOS 15.0

    iOS 14 更新
    在 iOS 14 中情况变得非常糟糕,过时的音频标签 .src=null 技巧停止工作。从技术上讲,.src=null 确实可以在很短的时间内工作(可以在初始设置期间使用.src 恢复黑客攻击)。然而,一旦静音循环播放,它就变得无用了。

    新技巧依赖于.mute,它奇迹般地适用于包括 iOS14 在内的所有 iOS 版本(但仅在直接访问 documentById 而不是 var 时)。锁定屏幕时也没有媒体中心。这需要大量研究,但我们得到了它。

    【讨论】:

    • 我无法实现您的代码。这部分应该去哪里? let configuration = WKWebViewConfiguration() configuration.allowsInlineMediaPlayback = true configuration.mediaTypesRequiringUserActionForPlayback = [] let localWebView = WKWebView(frame: .zero, configuration: configuration)
    • @bablack 你可以把它放在viewDidLoad中,看你的需要。
    • ok thx 为什么让 localWebView = WKWebView(frame: .zero, configuration: configuration) ?以及如何使用 webView 功能?...对不起初学者..
    • 这些是超出这个问题的单独问题。如果您在使用 WKWebView 时遇到问题,请查看关于 SO 的现有问题,也许还有一些教程。如果这无助于在 SO 上提出一个新问题,详细描述您的特定问题/挑战。
    • 这是一个了不起的(尽管有点老套)解决方案!谢谢!
    猜你喜欢
    • 2011-05-22
    • 1970-01-01
    • 2013-05-26
    • 1970-01-01
    • 2011-09-14
    • 1970-01-01
    • 2021-03-18
    • 2019-01-09
    • 2010-10-11
    相关资源
    最近更新 更多