角色会话长按说话向上取消录音

This commit is contained in:
mh 2025-11-06 11:50:29 +08:00
parent 04b2131efc
commit 8b1727fcaa
3 changed files with 86 additions and 9 deletions

View File

@ -28,6 +28,7 @@ class IMVoiceHoldView: UIView {
var audioDuration: Int = 0 var audioDuration: Int = 0
var counting: Int = 0 var counting: Int = 0
private var isRecording: Bool = false private var isRecording: Bool = false
private var isInCancelArea: Bool = false //
public var audioPathUrl: URL? public var audioPathUrl: URL?
var recordFinishedAction: ((_ url: URL?) -> Void)? var recordFinishedAction: ((_ url: URL?) -> Void)?
@ -129,7 +130,8 @@ class IMVoiceHoldView: UIView {
stackView.spacing = 5.0 stackView.spacing = 5.0
stackView.distribution = .fill stackView.distribution = .fill
stackView.alignment = .fill stackView.alignment = .fill
insertSubview(stackView, belowSubview: overlayBg) // insertSubview(stackView, belowSubview: overlayBg)
insertSubview(stackView, aboveSubview: overlayBg)
stackView.snp.makeConstraints { make in stackView.snp.makeConstraints { make in
make.centerX.equalToSuperview() make.centerX.equalToSuperview()
make.bottom.equalTo(voiceIconDecorationView.snp.top).offset(-15) make.bottom.equalTo(voiceIconDecorationView.snp.top).offset(-15)
@ -156,6 +158,23 @@ class IMVoiceHoldView: UIView {
recordTool = AudioRecordTool() 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() { private func setupEvent() {
recordTool.timerChangedBlock = { [weak self] counting in recordTool.timerChangedBlock = { [weak self] counting in
self?.counting = counting self?.counting = counting
@ -178,32 +197,59 @@ class IMVoiceHoldView: UIView {
isRecording = on isRecording = on
if on { if on {
//
isInCancelArea = false
voiceIconDecorationView.image = UIImage(named: "role_chat_voice_normal")
tipLabel.text = "Release to send"
wave.play() wave.play()
recordTool.startRecord { [weak self] audioURL in recordTool.startRecord { [weak self] audioURL in
self?.audioPathUrl = audioURL self?.audioPathUrl = audioURL
dlog("🎤audioURL: \(String(describing: audioURL))") dlog("🎤audioURL: \(String(describing: audioURL))")
if let count = self?.counting, count < 10{ guard let self = self else { return }
//
//
if !self.isInCancelArea {
if self.counting < 10 {
Hud.toast(str: "语音时间太短") Hud.toast(str: "语音时间太短")
return return
} }
self?.recordFinishedAction?(audioURL) self.recordFinishedAction?(audioURL)
} else {
//
dlog("☁️🎤录音已取消(在取消区域)")
}
} }
} else { } else {
wave.stop() wave.stop()
//
if isInCancelArea {
cancelRecord()
} else {
stopRecord() stopRecord()
} }
} }
}
// MARK: - Functions // MARK: - Functions
func stopRecord() { func stopRecord() {
// startRecord
recordTool.stopRecord() recordTool.stopRecord()
// if counting < 10{ // if counting < 10{
// Hud.toast(str: "") // Hud.toast(str: "")
// return // return
// } // }
}
func cancelRecord() {
//
//
recordTool.stopRecord()
audioPathUrl = nil
dlog("☁️🎤录音已取消")
} }
// MARK: - Other // MARK: - Other

View File

@ -14,6 +14,8 @@ protocol SessionInputOperateViewDelegate: AnyObject {
func operateTapMoreAction() func operateTapMoreAction()
func operateTapHelpAction() func operateTapHelpAction()
func operateTapInputFieldAction() func operateTapInputFieldAction()
//
func operateVoiceDragAction(location: CGPoint)
} }
enum InputOperateState { enum InputOperateState {
@ -223,7 +225,7 @@ class SessionInputOperateView: UIView {
// //
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:))) let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
longPress.minimumPressDuration = 0.2 // 0.5 longPress.minimumPressDuration = 0.2 // 0.2
voiceHoldView.addGestureRecognizer(longPress) voiceHoldView.addGestureRecognizer(longPress)
$state.sink { [weak self] state in $state.sink { [weak self] state in
@ -311,7 +313,8 @@ class SessionInputOperateView: UIView {
case .began: case .began:
delegate?.operateVoiceAction(on: true) delegate?.operateVoiceAction(on: true)
case .changed: case .changed:
break // voiceIconDecorationView
handleDragGesture(gesture)
case .ended: case .ended:
delegate?.operateVoiceAction(on: false) delegate?.operateVoiceAction(on: false)
case .cancelled, .failed: case .cancelled, .failed:
@ -320,6 +323,15 @@ class SessionInputOperateView: UIView {
break 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 { extension SessionInputOperateView: UITextFieldDelegate {

View File

@ -255,6 +255,25 @@ extension SessionController: SessionInputOperateViewDelegate{
voiceHoldView.record(on: on) 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() { func operateTapMoreAction() {
view.endEditing(true) view.endEditing(true)