技术探索:修改iOS系统音量与监听音量键按下的“方法”

0 阅读3分钟

前段时间我正在为我的App添加音量键监听的功能。但是浏览了很多文章,可还是没有找到一个符合我App需求的方案。并且很多都是基于UIKit框架下的方法,对于SwiftUI却很少。

原文发表于我的博客,欢迎关注。转载请注明出处并标注原文链接

本文中的代码均已整理并打包成Package,你可以添加到你的项目中并通过相关modifier直接使用

Github:github.com/guoPhineas/…

多年来的“遗憾”

其实这个功能,我早在几年前就想加入了。当时App使用的还是UIKit,并且我技术比较“菜”,再加上当时确实没有在网上找到能实现监听音量键的文章和代码。后来使用SwiftUI进行了重构,但当时找到的代码也还是无法实现效果。

我记得最早找到的文章使用的是NotificationCenter中的一个方法来监听事件,但确实没有效果。现在再搜索也找不到当时的文章了。后续找到了一个AVKit中的监听方法,虽然能够实时监听音量的变化,但是我按下音量键的同时音量值也变化了,而且它还不支持修改音量。

后来因为学业,也就没再开发了。直到最近几个月又有用户在App Store留言,想要添加这个功能,我也又把它提上了日程(虽说几年前就有人在App Store上留言这个功能了🤣)。

不过最近几年WWDC上有一个适用于Camera的按键监听事件,但好像不能用于非相机App上😢(等着研究一下)

探索过程

监听与获取、修改系统音量

后来,我改变了一下思路。监听按键按下的本质就是监听音量值的变化,同时还能把调整的音量改回来(修改音量)。后来,我找到了MPVolumeView

MPVolumeView是在UIKit下的一个视图,它向用户提供了一个Slider和一个Button,允许用户调整音量和媒体输出。同时这个View还常被用于自定义或隐藏系统自带的音量变化指示器。

MPVolumeView的文档

MPVolumeView的文档 ↑

那就试试!在SwiftUI中,我们可以使用UIViewRepresentable来桥接UIKit的视图:

struct MPVolumeViewRepresentable: UIViewRepresentable {

    func makeUIView(context: Context) -> MPVolumeView {
        let mpView = MPVolumeView()
        return mpView
    }
    
    func updateUIView(_ uiView: MPVolumeView, context: Context) {

    }
}

运行之后不难发现,它确实提供了一个Slider,并且拖动Slider将实时更改系统音量。再对View进行Debug后,也可以发现Slider继承于UISlider。只要我们在UIKit中获取到这个Slider那就很好办了:

Debug

Debug↑

并且通过查看定义,也不难发现,MPVolumeView是继承UIView(其实既然作为视图,基本上100%可以确定继承与于UIView了,其实也不用查看定义):

MPVolumeView的定义

MPVolumeView的定义↑

这样我们就可以通过遍历它的子视图,取出继承UISlider的View,再添加修改代理事件即可:

for subview in mpView.subviews {
    if let slider = subview as? UISlider {
        slider.addTarget(
            context.coordinator,
            action: #selector(Coordinator.valueChanged(_:)),
            for: .valueChanged
        )
        context.coordinator.slider = slider
        break
    }
}

这样,当系统音量变化(或我们拖动修改)时,它会自动调用valueChanged(_:)这个方法。并且我们要修改音量时,也可以直接修改Slider的value

音量键监听

有了音量的监听和修改,我们就可以“间接”监听音量键按下了。

在监听方法中,我们可以将新修改的值与旧值做对比,从而确定是哪个按钮被按下。

这里不再赘述了,感兴趣可以看一下仓库源码,当然也可以直接使用:github.com/guoPhineas/… ,欢迎点击Star支持一下!

后记

SwiftUI自2019年到现在已经有不少改观了,但相较于UIKit来说,个人感觉还有很长的路要走。慢慢发展吧。

原文发表于我的博客,欢迎关注。转载请注明出处并标注原文链接