iOS Swift钢琴键盘多音实现:如何同时处理多点触摸事件
解决iOS钢琴键盘多音同时触发的问题
嘿,我来帮你搞定这个多音同时播放的问题!从你的日志和代码来看,核心问题在于touchesBegan里的同步操作拖慢了后续触摸事件的处理——虽然AVAudioPlayer.play()本身是异步的,但你的日志、蓝牙发送等同步代码会阻塞主线程,导致第二个触摸事件要等第一个的所有操作完成后才开始执行。以下是具体的解决方案:
优化思路
- 区分UI操作与耗时操作:UI相关的状态更新必须留在主线程,但蓝牙发送这类可能耗时的操作要放到后台队列,避免阻塞主线程的触摸事件处理。
- 确保音频播放器独立:每个琴键对应独立的
AVAudioPlayer实例,不能复用,否则会导致前一个音频被中断。 - 异步启动非核心操作:让音频播放和UI状态更新优先执行,耗时操作异步延后处理。
修改后的代码示例
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { for touch in touches { let point = touch.location(in: self.stackView) guard let hitView = self.stackView.hitTest(point, with: event) else { continue } let tag = hitView.tag NSLog("%d first", tag) // 核心操作立即执行(音频播放是异步启动的,不会阻塞) self.pushChanged(senderTag: tag, add: true) self.keyPushed(senderTag: tag, userPushed: true) self.keyboardTouchStateChanged(senderTag: tag, add: true) // 把蓝牙发送这类耗时操作放到后台队列,不阻塞主线程 if self.bluetoothTag == "1" { DispatchQueue.global(qos: .userInitiated).async { [weak self] in guard let self = self else { return } let data = String(tag) self.bService.send(colorName: data) } } NSLog("%d last", tag) } } func keyPushed(senderTag:Int, userPushed:Bool = false){ // 确保每个senderTag对应独立的AVAudioPlayer实例 guard let player = audioPlayers[senderTag] else { return } player.currentTime = 0 player.play() // play()本身是异步的,会立即返回,不会等待音频播放完成 }
关键说明
- 日志的先后顺序不代表音频播放的启动顺序:
play()方法会立即启动音频播放并返回,所以即使日志里40的last在52的first之前,两个音频其实已经同时开始播放了。 - 蓝牙操作异步化:把蓝牙发送放到后台队列后,主线程不会被这个操作阻塞,能立刻处理下一个触摸事件。
- 播放器独立性:务必确认
audioPlayers数组中每个索引对应一个全新的AVAudioPlayer实例,每个实例加载对应的音频文件,这样多个音频才能同时发声,不会互相干扰。
内容的提问来源于stack exchange,提问作者加納大地
相关产品推荐
相关产品推荐

