角色会话长按说话向上取消录音
This commit is contained in:
parent
04b2131efc
commit
8b1727fcaa
|
|
@ -28,6 +28,7 @@ class IMVoiceHoldView: UIView {
|
|||
var audioDuration: Int = 0
|
||||
var counting: Int = 0
|
||||
private var isRecording: Bool = false
|
||||
private var isInCancelArea: Bool = false // 是否在取消区域
|
||||
public var audioPathUrl: URL?
|
||||
|
||||
var recordFinishedAction: ((_ url: URL?) -> Void)?
|
||||
|
|
@ -129,7 +130,8 @@ class IMVoiceHoldView: UIView {
|
|||
stackView.spacing = 5.0
|
||||
stackView.distribution = .fill
|
||||
stackView.alignment = .fill
|
||||
insertSubview(stackView, belowSubview: overlayBg)
|
||||
// insertSubview(stackView, belowSubview: overlayBg)
|
||||
insertSubview(stackView, aboveSubview: overlayBg)
|
||||
stackView.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.bottom.equalTo(voiceIconDecorationView.snp.top).offset(-15)
|
||||
|
|
@ -155,6 +157,23 @@ class IMVoiceHoldView: UIView {
|
|||
private func setupData() {
|
||||
recordTool = AudioRecordTool()
|
||||
}
|
||||
|
||||
// MARK: - 更新取消状态
|
||||
func updateCancelState(isInCancelArea: Bool) {
|
||||
guard self.isInCancelArea != isInCancelArea else { return }
|
||||
|
||||
self.isInCancelArea = isInCancelArea
|
||||
|
||||
if isInCancelArea {
|
||||
// 进入取消区域
|
||||
voiceIconDecorationView.image = UIImage(named: "role_chat_voice_cancel")
|
||||
tipLabel.text = "Release to cancel"
|
||||
} else {
|
||||
// 退出取消区域
|
||||
voiceIconDecorationView.image = UIImage(named: "role_chat_voice_normal")
|
||||
tipLabel.text = "Release to send"
|
||||
}
|
||||
}
|
||||
|
||||
private func setupEvent() {
|
||||
recordTool.timerChangedBlock = { [weak self] counting in
|
||||
|
|
@ -178,32 +197,59 @@ class IMVoiceHoldView: UIView {
|
|||
isRecording = on
|
||||
|
||||
if on {
|
||||
// 重置取消状态
|
||||
isInCancelArea = false
|
||||
voiceIconDecorationView.image = UIImage(named: "role_chat_voice_normal")
|
||||
tipLabel.text = "Release to send"
|
||||
|
||||
wave.play()
|
||||
recordTool.startRecord { [weak self] audioURL in
|
||||
self?.audioPathUrl = audioURL
|
||||
dlog("☁️🎤audioURL: \(String(describing: audioURL))")
|
||||
|
||||
if let count = self?.counting, count < 10{
|
||||
Hud.toast(str: "语音时间太短")
|
||||
return
|
||||
guard let self = self else { return }
|
||||
|
||||
// 检查是否在取消区域(录音停止时)
|
||||
// 如果不在取消区域,才发送录音
|
||||
if !self.isInCancelArea {
|
||||
if self.counting < 10 {
|
||||
Hud.toast(str: "语音时间太短")
|
||||
return
|
||||
}
|
||||
self.recordFinishedAction?(audioURL)
|
||||
} else {
|
||||
// 在取消区域,不发送录音
|
||||
dlog("☁️🎤录音已取消(在取消区域)")
|
||||
}
|
||||
self?.recordFinishedAction?(audioURL)
|
||||
}
|
||||
} else {
|
||||
wave.stop()
|
||||
stopRecord()
|
||||
// 如果松手时在取消区域,取消录音
|
||||
if isInCancelArea {
|
||||
cancelRecord()
|
||||
} else {
|
||||
stopRecord()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
func stopRecord() {
|
||||
// 停止录音,会触发 startRecord 的回调
|
||||
recordTool.stopRecord()
|
||||
// if counting < 10{
|
||||
// Hud.toast(str: "语音时间太短")
|
||||
// return
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
func cancelRecord() {
|
||||
// 取消录音:停止录音但不发送
|
||||
// 设置取消标志,确保回调中不会发送录音
|
||||
recordTool.stopRecord()
|
||||
audioPathUrl = nil
|
||||
dlog("☁️🎤录音已取消")
|
||||
}
|
||||
|
||||
// MARK: - Other
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ protocol SessionInputOperateViewDelegate: AnyObject {
|
|||
func operateTapMoreAction()
|
||||
func operateTapHelpAction()
|
||||
func operateTapInputFieldAction()
|
||||
// 拖动手势位置变化
|
||||
func operateVoiceDragAction(location: CGPoint)
|
||||
}
|
||||
|
||||
enum InputOperateState {
|
||||
|
|
@ -223,7 +225,7 @@ class SessionInputOperateView: UIView {
|
|||
|
||||
// 添加长按手势
|
||||
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
|
||||
longPress.minimumPressDuration = 0.2 // 长按时间,默认 0.5 秒
|
||||
longPress.minimumPressDuration = 0.2 // 长按时间,默认 0.2 秒
|
||||
voiceHoldView.addGestureRecognizer(longPress)
|
||||
|
||||
$state.sink { [weak self] state in
|
||||
|
|
@ -311,7 +313,8 @@ class SessionInputOperateView: UIView {
|
|||
case .began:
|
||||
delegate?.operateVoiceAction(on: true)
|
||||
case .changed:
|
||||
break
|
||||
// 处理拖动,检测是否超出 voiceIconDecorationView 顶部
|
||||
handleDragGesture(gesture)
|
||||
case .ended:
|
||||
delegate?.operateVoiceAction(on: false)
|
||||
case .cancelled, .failed:
|
||||
|
|
@ -320,6 +323,15 @@ class SessionInputOperateView: UIView {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func handleDragGesture(_ gesture: UILongPressGestureRecognizer) {
|
||||
// 获取手势在 window 中的位置
|
||||
guard let window = self.window else { return }
|
||||
let location = gesture.location(in: window)
|
||||
|
||||
// 通知 delegate 拖动位置变化
|
||||
delegate?.operateVoiceDragAction(location: location)
|
||||
}
|
||||
}
|
||||
|
||||
extension SessionInputOperateView: UITextFieldDelegate {
|
||||
|
|
|
|||
|
|
@ -255,6 +255,25 @@ extension SessionController: SessionInputOperateViewDelegate{
|
|||
voiceHoldView.record(on: on)
|
||||
}
|
||||
|
||||
func operateVoiceDragAction(location: CGPoint) {
|
||||
// 确保 voiceHoldView 已经布局
|
||||
voiceHoldView.layoutIfNeeded()
|
||||
|
||||
// 将 window 坐标转换为 voiceHoldView 中的坐标
|
||||
let locationInVoiceHoldView = voiceHoldView.convert(location, from: nil)
|
||||
|
||||
// 获取 voiceIconDecorationView 在 voiceHoldView 中的 frame
|
||||
// 由于 voiceIconDecorationView 是 voiceHoldView 的子视图,frame 已经是相对于 voiceHoldView 的
|
||||
let decorationViewFrame = voiceHoldView.voiceIconDecorationView.frame
|
||||
|
||||
// 判断手势是否超出 voiceIconDecorationView 顶部
|
||||
// 如果手势的 y 坐标小于 decorationView 的顶部,说明在取消区域
|
||||
let isInCancelArea = locationInVoiceHoldView.y < decorationViewFrame.origin.y
|
||||
|
||||
// 更新取消状态
|
||||
voiceHoldView.updateCancelState(isInCancelArea: isInCancelArea)
|
||||
}
|
||||
|
||||
func operateTapMoreAction() {
|
||||
view.endEditing(true)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue