前段时间我正在为我的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的文档 ↑
那就试试!在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↑
并且通过查看定义,也不难发现,MPVolumeView是继承UIView(其实既然作为视图,基本上100%可以确定继承与于UIView了,其实也不用查看定义):
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来说,个人感觉还有很长的路要走。慢慢发展吧。