From: liangzhenying <867174124@qq.com> Date: Fri, 19 Sep 2025 10:14:39 +0000 (+0800) Subject: 完善对话 X-Git-Tag: 1.0.2~98 X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=87ae6762c1ab257e6aee38eff58f749b0c42faa5;p=mirrors%2FShenYin.git 完善对话 --- diff --git a/ShenYin/Podfile.lock b/ShenYin/Podfile.lock index 3e86ec9..7cb6391 100644 --- a/ShenYin/Podfile.lock +++ b/ShenYin/Podfile.lock @@ -71,7 +71,6 @@ PODS: - TZImagePickerController/Location (= 3.8.9) - TZImagePickerController/Basic (3.8.9) - TZImagePickerController/Location (3.8.9) - - TZScrollViewPopGesture (1.0.6) - YYModel (1.0.4) - YYText (1.0.7) @@ -95,7 +94,6 @@ DEPENDENCIES: - swiftScan - TabPageViewController - TZImagePickerController - - TZScrollViewPopGesture - YYModel - YYText @@ -129,7 +127,6 @@ SPEC REPOS: - swiftScan - TabPageViewController - TZImagePickerController - - TZScrollViewPopGesture - YYModel - YYText @@ -162,10 +159,9 @@ SPEC CHECKSUMS: swiftScan: 0849e7d7c1c641384767a573ee8cb90b88d1ddd9 TabPageViewController: 8bd4104909754416b35a98f3b72a5b674ab20dd5 TZImagePickerController: 456f470b5dea97b37226ec7a694994a8663340b2 - TZScrollViewPopGesture: d36355038259e89372a1f4a2db7e9ca675de2278 YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30 YYText: 5c461d709e24d55a182d1441c41dc639a18a4849 -PODFILE CHECKSUM: bc7d3a8004029604d5620b967136223e19d8a67d +PODFILE CHECKSUM: 57796390fdd051e53f61c569309d2caa20f804e9 COCOAPODS: 1.16.2 diff --git a/ShenYin/ShenYin.xcodeproj/project.pbxproj b/ShenYin/ShenYin.xcodeproj/project.pbxproj index 229bec3..aeed467 100644 --- a/ShenYin/ShenYin.xcodeproj/project.pbxproj +++ b/ShenYin/ShenYin.xcodeproj/project.pbxproj @@ -158,8 +158,6 @@ ); mainGroup = 3F887B932E38D8DC00C98658; minimizedProjectReferenceProxies = 1; - packageReferences = ( - ); preferredProjectObjectVersion = 77; productRefGroup = 3F887B9D2E38D8DC00C98658 /* Products */; projectDirPath = ""; diff --git a/ShenYin/ShenYin/Api/HttpClient.swift b/ShenYin/ShenYin/Api/HttpClient.swift index 41cef86..3d88d08 100644 --- a/ShenYin/ShenYin/Api/HttpClient.swift +++ b/ShenYin/ShenYin/Api/HttpClient.swift @@ -12,7 +12,8 @@ import HandyJSON // 相关api enum CommomApi { - case sms(phone:String) + case sms(phone:String,type:SmsType) + case countryList case list(pageNO: Int = 1, pageSize: Int = 10) case login(phone:String,code:String) } @@ -23,14 +24,17 @@ extension CommomApi: TargetType // url var baseURL: URL { - return URL(string: "https://app-api.raisound.com")! +// return URL(string: "https://app-api.raisound.com")! + return URL(string: "https://app-api.deepchords.com")! } /// 请求路径 var path: String { switch self { - case .sms(let phone): - return "/sys/sms/reg-login/\(phone)" + case .sms(let phone,let type): + return "/uc/sms" + case .countryList: + return "/uc/country/list" case .list: return "list" case .login(_,_): @@ -41,7 +45,7 @@ extension CommomApi: TargetType /// 请求方式 var method: Moya.Method { switch self { - case .sms(_): + case .sms(_,_): return .get case .login(_,_): return .post @@ -62,6 +66,9 @@ extension CommomApi: TargetType // 收集参数 switch self { + case let .sms(phone, type): + params["phone"] = phone + params["type"] = type case let .list(pageNO, pageSize): params["pageNO"] = pageNO params["pageSize"] = pageSize diff --git a/ShenYin/ShenYin/Api/ResponseBaseModel.swift b/ShenYin/ShenYin/Api/ResponseBaseModel.swift index a56f0d2..e887c13 100644 --- a/ShenYin/ShenYin/Api/ResponseBaseModel.swift +++ b/ShenYin/ShenYin/Api/ResponseBaseModel.swift @@ -11,7 +11,6 @@ public class ResponseBaseModel: HandyJSON { required public init() { } public var code = -1 - public var status = 0 public var msg = "请求失败" diff --git a/ShenYin/ShenYin/AppLaunch.swift b/ShenYin/ShenYin/AppLaunch.swift index 1fa4370..d021252 100644 --- a/ShenYin/ShenYin/AppLaunch.swift +++ b/ShenYin/ShenYin/AppLaunch.swift @@ -27,10 +27,10 @@ class AppLaunch:NSObject{ static func loadCache(){ let list = DeviceCache.shared.getDeviceList() Logger.debug("缓存设备",list) -// if list.count > 0 { -// CacheManager.shared.currentDevice = list.first -// BleManager.shared.startScanDevice() -// App.shared.enter() -// } + if list.count > 0 { + CacheManager.shared.currentDevice = list.first + BleManager.shared.startScanDevice() + App.shared.enter() + } } } diff --git a/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_ear.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_ear.imageset/Contents.json new file mode 100644 index 0000000..6d169ae --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_ear.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group_2437@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group_2437@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_ear.imageset/Group_2437@2x.png b/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_ear.imageset/Group_2437@2x.png new file mode 100644 index 0000000..275aaab Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_ear.imageset/Group_2437@2x.png differ diff --git a/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_ear.imageset/Group_2437@3x.png b/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_ear.imageset/Group_2437@3x.png new file mode 100644 index 0000000..752bfa3 Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_ear.imageset/Group_2437@3x.png differ diff --git a/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_phone.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_phone.imageset/Contents.json new file mode 100644 index 0000000..b7db985 --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_phone.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group_2435@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group_2435@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_phone.imageset/Group_2435@2x.png b/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_phone.imageset/Group_2435@2x.png new file mode 100644 index 0000000..1fe725e Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_phone.imageset/Group_2435@2x.png differ diff --git a/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_phone.imageset/Group_2435@3x.png b/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_phone.imageset/Group_2435@3x.png new file mode 100644 index 0000000..62a8eab Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/home/dialog_device_phone.imageset/Group_2435@3x.png differ diff --git a/ShenYin/ShenYin/Assets.xcassets/home/split_screen_1.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/home/split_screen_1.imageset/Contents.json new file mode 100644 index 0000000..5446c99 --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/home/split_screen_1.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "分屏_2@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "分屏_2@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/ShenYin/ShenYin/Assets.xcassets/home/split_screen_1.imageset/\345\210\206\345\261\217_2@2x.png" "b/ShenYin/ShenYin/Assets.xcassets/home/split_screen_1.imageset/\345\210\206\345\261\217_2@2x.png" new file mode 100644 index 0000000..e647e2e Binary files /dev/null and "b/ShenYin/ShenYin/Assets.xcassets/home/split_screen_1.imageset/\345\210\206\345\261\217_2@2x.png" differ diff --git "a/ShenYin/ShenYin/Assets.xcassets/home/split_screen_1.imageset/\345\210\206\345\261\217_2@3x.png" "b/ShenYin/ShenYin/Assets.xcassets/home/split_screen_1.imageset/\345\210\206\345\261\217_2@3x.png" new file mode 100644 index 0000000..e947155 Binary files /dev/null and "b/ShenYin/ShenYin/Assets.xcassets/home/split_screen_1.imageset/\345\210\206\345\261\217_2@3x.png" differ diff --git a/ShenYin/ShenYin/Ble/BleManager.swift b/ShenYin/ShenYin/Ble/BleManager.swift index 88d6639..2d28d4c 100644 --- a/ShenYin/ShenYin/Ble/BleManager.swift +++ b/ShenYin/ShenYin/Ble/BleManager.swift @@ -226,6 +226,7 @@ extension BleManager : JoyGemAIManagerDelegate { } extension BleManager : JoyGemAIManagerDataSource { func joyGemSend(_ data: Data, with characteristic: CBCharacteristic) { +// print("发送",data.map { String($0) }.joined(separator: "")) if let connectPeripheral = connectPeripheral{ connectPeripheral.writeValue(data, for: characteristic, type:.withoutResponse) } diff --git a/ShenYin/ShenYin/SceneDelegate.swift b/ShenYin/ShenYin/SceneDelegate.swift index cdba428..d49cccf 100644 --- a/ShenYin/ShenYin/SceneDelegate.swift +++ b/ShenYin/ShenYin/SceneDelegate.swift @@ -20,7 +20,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { App.shared.window = window window?.rootViewController = BaseTC() - window?.rootViewController = UINavigationController(rootViewController: RecordTranslateVC()) +// window?.rootViewController = UINavigationController(rootViewController: DialogTranslateVC()) AppLaunch.start() } diff --git a/ShenYin/ShenYin/UI/Base/BaseNC.swift b/ShenYin/ShenYin/UI/Base/BaseNC.swift index 22ca3c8..8ad6814 100644 --- a/ShenYin/ShenYin/UI/Base/BaseNC.swift +++ b/ShenYin/ShenYin/UI/Base/BaseNC.swift @@ -6,11 +6,16 @@ // import UIKit +import FDFullscreenPopGesture -class BaseNC:UINavigationController{ +class BaseNC: UINavigationController { + override init(rootViewController: UIViewController) { + super.init(rootViewController: rootViewController) + fd_fullscreenPopGestureRecognizer?.isEnabled = true + } - override func viewDidLoad() { - super.viewDidLoad() - + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + fd_fullscreenPopGestureRecognizer?.isEnabled = true } } diff --git a/ShenYin/ShenYin/UI/Base/BaseVC.swift b/ShenYin/ShenYin/UI/Base/BaseVC.swift index da69642..ae612d9 100644 --- a/ShenYin/ShenYin/UI/Base/BaseVC.swift +++ b/ShenYin/ShenYin/UI/Base/BaseVC.swift @@ -8,7 +8,7 @@ import UIKit import Kingfisher -open class BaseVC:UIViewController, UIGestureRecognizerDelegate { +open class BaseVC:UIViewController { let logBtn = UIButton(type: .custom) let navBar = BaseNavigationBar() diff --git a/ShenYin/ShenYin/UI/Home/CallingRecordVC.swift b/ShenYin/ShenYin/UI/Home/CallingRecordVC.swift index 40e4993..d27735f 100644 --- a/ShenYin/ShenYin/UI/Home/CallingRecordVC.swift +++ b/ShenYin/ShenYin/UI/Home/CallingRecordVC.swift @@ -14,7 +14,7 @@ class CallingRecordVC:BaseVC{ let rhythView = RhythView() var messageList : [(String,UInt)] = [] var recordFilePath : String? - + override func viewDidLoad() { super.viewDidLoad() @@ -25,6 +25,7 @@ class CallingRecordVC:BaseVC{ toolBar.hasRightItem = false textView.do { v in + v.backgroundColor = .hexString("#F7F7F7") v.isEditable = false v.font = .regular(14) v.text = "" @@ -53,10 +54,32 @@ class CallingRecordVC:BaseVC{ } override func setupData() { JoyGemCallManager.shared().delegate = self -// JoyGemCallManager.shared().stereoDelegate = self//双声道 } - func scrollToBottom(){ - + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = false + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = true + } + override func navleftViewTapped() { + if toolBar.recordBtn.isSelected { + CustomAlert.showWith(title: "提示", messgage: "当前正在录音,确定放弃当前录音吗?", cancelCallback: nil) { + JoyGemCallManager.shared().stop() + PcmSound.sharedInstance().stopAudioUnit() + PcmSound.sharedInstance().cleanup() + super.navleftViewTapped() + } + } + else{ + super.navleftViewTapped() + } + } + func refreshData(){ + let range = NSMakeRange(textView.text.count - 1, 1) + textView.scrollRangeToVisible(range) } ///播放最后一次录音文件 @objc func playRecord() { @@ -86,9 +109,9 @@ extension CallingRecordVC:FieldRecordBarDelegate{ } else if state == .stop{ JoyGemCallManager.shared().stop() - bar.recordBtn.startAnimating {[weak self] in + bar.startAnimating {[weak self] in guard let self = self else{return} - // toolBar.recordBtn.stopCli() + //toolBar.recordBtn.stopCli() let v = PopTextField() v.show() v.cancelTap = { @@ -105,15 +128,15 @@ extension CallingRecordVC : JoyGemCallManagerDelegate { func callManagerStopped(_ manager: JoyGemCallManager, reason code: JoyGemRecordStopCode) { if (code == .actively) { - YLprogressHUD.showAutoHiden( "用户主动结束") + Logger.debug( "用户主动结束") } else { - YLprogressHUD.showAutoHiden( "自动结束") - ///判断当前蓝牙是否连接从而确定是否因为蓝牙断开造成的录音断开 + Logger.debug( "自动结束") + toolBar.recordBtn.isSelected = false } } func callManagerNotonCall(_ manager: JoyGemCallManager) { - YLprogressHUD.showAutoHiden("未处于通话状态") + Logger.debug("未处于通话状态") } func callManager(_ manager: JoyGemCallManager, saveFile path: String) { @@ -122,7 +145,7 @@ extension CallingRecordVC : JoyGemCallManagerDelegate { } func callManager(_ manager: JoyGemCallManager, stateChange code: JoyGemStateCode) { - debugPrint("code: \(code)") + Logger.debug("code: \(code)") } func callManager(_ manager: JoyGemCallManager, onError code: JoyGemErrorCode) { @@ -130,7 +153,7 @@ extension CallingRecordVC : JoyGemCallManagerDelegate { } func callManager(_ manager: JoyGemCallManager, didUpdateAsr model: JoyGemAsrModel) { - debugPrint("text: \(model.text),index: \(model.index)") + Logger.debug("text: \(model.text),index: \(model.index)") let msg = messageList.firstIndex (where: { (str,idx) in return idx == model.index; }) @@ -143,6 +166,7 @@ extension CallingRecordVC : JoyGemCallManagerDelegate { textView.text = messageList.compactMap({ (str,idx) in return str }).joined(separator: "\n") + refreshData() } } diff --git a/ShenYin/ShenYin/UI/Home/Chat/ChatVC.swift b/ShenYin/ShenYin/UI/Home/Chat/ChatVC.swift index 1534d1e..f271ff9 100644 --- a/ShenYin/ShenYin/UI/Home/Chat/ChatVC.swift +++ b/ShenYin/ShenYin/UI/Home/Chat/ChatVC.swift @@ -20,6 +20,8 @@ class ChatVC:BaseVC{ var isTtsPlaying = false deinit { + PcmSound.sharedInstance().stopAudioUnit() + PcmSound.sharedInstance().cleanup() JoyGemChatManager.shared().removeObserver(self, forKeyPath: "isActive") } @@ -116,7 +118,8 @@ class ChatVC:BaseVC{ } App.shared.goToPage(vc) } - func scrollToBottom(){ + func refreshData(){ + tableView.reloadData() if dataList.count > 0 { tableView.scrollToRow(at: IndexPath(row: dataList.count - 1, section: 0), at: .none, animated: true) } @@ -140,8 +143,7 @@ extension ChatVC:ChatRecordBarDelegate{ model.content = "" model.id = -1 dataList.append(model) - tableView.reloadData() - scrollToBottom() + refreshData() JoyGemChatManager.shared().stopChat() JoyGemChatManager.shared().startSingleChat() @@ -231,8 +233,7 @@ extension ChatVC : JoyGemChatManagerDelegate { model.id = Int(index) dataList.append(model) } - tableView.reloadData() - scrollToBottom() + refreshData() } func chatManager(_ manager: JoyGemChatManager, responseText text: String?, index: UInt, finish isFinish: Bool) { @@ -249,8 +250,7 @@ extension ChatVC : JoyGemChatManagerDelegate { model.id = Int(index) dataList.append(model) } - tableView.reloadData() - scrollToBottom() + refreshData() } } } diff --git a/ShenYin/ShenYin/UI/Home/Chinese2English/Chinese2EnglishVC.swift b/ShenYin/ShenYin/UI/Home/Chinese2English/Chinese2EnglishVC.swift index 14fdfae..138ff35 100644 --- a/ShenYin/ShenYin/UI/Home/Chinese2English/Chinese2EnglishVC.swift +++ b/ShenYin/ShenYin/UI/Home/Chinese2English/Chinese2EnglishVC.swift @@ -13,9 +13,7 @@ class Chinese2EnglishVC:BaseVC{ let tableView = UITableView(frame: CGRectZero, style: .plain) var dataList:[(Chinese2EnglishCellModel,UInt)] = [] var pcmData = Data() - override func viewDidLoad() { - super.viewDidLoad() - } + override func setupViews() { navBar.title = "中英互译" navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal) @@ -54,6 +52,27 @@ class Chinese2EnglishVC:BaseVC{ // print(error) // } } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = false + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = true + } + override func navleftViewTapped() { + if toolBar.recordBtn.isSelected { + CustomAlert.showWith(title: "提示", messgage: "当前正在录音,确定放弃当前录音吗?", cancelCallback: nil) { + JoyGemSIDeviceManager.shared().stop() + PcmSound.sharedInstance().stopAudioUnit() + PcmSound.sharedInstance().cleanup() + super.navleftViewTapped() + } + } + else{ + super.navleftViewTapped() + } + } override func navRightViewTapped() { let tip = RecordTipsView(type: .chinese2english) tip.show() @@ -85,7 +104,7 @@ extension Chinese2EnglishVC:RecordBarDelegate{ } else if state == .stop{ JoyGemSIDeviceManager.shared().stop() - bar.recordBtn.startAnimating {[weak self] in + bar.startAnimating {[weak self] in guard let self = self else{return} // toolBar.recordBtn.stopCli() let v = PopTextField() diff --git a/ShenYin/ShenYin/UI/Home/Dialog/DialogRecordBar.swift b/ShenYin/ShenYin/UI/Home/Dialog/DialogRecordBar.swift new file mode 100644 index 0000000..fd644a5 --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/Dialog/DialogRecordBar.swift @@ -0,0 +1,280 @@ +// +// DialogRecordBar.swift +// ShenYin +// +// Created by liangshi on 2025/9/16. +// + +enum DialogRecordDeviceType{ + case earphone + case phone +} +enum DialogRecordDeviceState{ + case start + case stop +} +enum DialogRecordLayoutType{ + case normal + case spolit +} +protocol DialogRecordBarDelegate { + func recordBar(_ bar:DialogRecordBar,layoutChange :DialogRecordLayoutType) + func recordBar(_ bar:DialogRecordBar, device:DialogRecordDeviceType,stateChange state:RecordBarSate) +} + +class DialogRecordBar: BaseView { + let switchBtn = UIButton() + let leftItem = RecordBarItem() + let rightItem = RecordBarItem() + var delegate:DialogRecordBarDelegate? + + override func setupViews() { + backgroundColor = .white + addSubviews([leftItem,rightItem,switchBtn]) + + switchBtn.do { v in + v.setImage(UIImage(named: "split_screen_1"), for: .normal) + v.setImage(UIImage(named: "split_screen_1"), for: .selected) + v.addTarget(self, action: #selector(switchBtnAction), for: .touchUpInside) + } + leftItem.do { (v) in + v.recordBtn.setImage(UIImage(named: "dialog_device_ear"), for: .normal) + v.recordBtn.setImage(UIImage(named: "dialog_device_ear"), for: .selected) + v.languageItem.language = AiSupportLanguage.chinse + v.languageItem.yc.addTap(target: self, action: #selector(leftLanguageAction)) + v.recordBtn.addTarget(self, action: #selector(leftRecordBtnAction), for: .touchUpInside) + v.language = .chinse + + } + rightItem.do { (v) in + v.recordBtn.setImage(UIImage(named: "dialog_device_phone"), for: .normal) + v.recordBtn.setImage(UIImage(named: "dialog_device_phone"), for: .selected) + v.languageItem.language = AiSupportLanguage.english + v.languageItem.yc.addTap(target: self, action: #selector(rightLanguageAction)) + v.recordBtn.addTarget(self, action: #selector(rightRecordBtnAction), for: .touchUpInside) + v.language = .english + } + } + + override func setupLayout() { + switchBtn.snp.makeConstraints { make in + make.size.equalTo(56) + make.center.equalToSuperview() + } + leftItem.snp.makeConstraints { make in + make.height.equalTo(100) + make.centerY.equalTo(switchBtn) + make.right.equalToSuperview().offset(-(kScreenWid/2 + 60)) + } + rightItem.snp.makeConstraints { make in + make.height.equalTo(100) + make.centerY.equalTo(switchBtn) + make.left.equalToSuperview().offset(kScreenWid/2 + 60) + } + } + + func stopRecord(){ + if leftItem.recordBtn.isSelected { + leftItem.recordBtn.isSelected = false + leftItem.stopAnimating() + } + if rightItem.recordBtn.isSelected { + rightItem.recordBtn.isSelected = false + rightItem.stopAnimating() + } + leftItem.alpha = 1 + rightItem.alpha = 1 + } + + @objc func switchBtnAction(){ + delegate?.recordBar(self, layoutChange: .normal) + } + + @objc func leftRecordBtnAction(){ + if rightItem.recordBtn.activeView.animating { + return + } + if rightItem.recordBtn.isSelected { + return + } + leftItem.recordBtn.isSelected = !leftItem.recordBtn.isSelected + if leftItem.recordBtn.isSelected { + rightItem.recordBtn.alpha = 1 + leftItem.languageItem.alpha = 0.3 + rightItem.recordBtn.alpha = 0.3 + rightItem.languageItem.alpha = 0.3 + + leftItem.startAnimating() + delegate?.recordBar(self, device: .earphone,stateChange: .start) + } + else{ + leftItem.recordBtn.alpha = 1 + leftItem.languageItem.alpha = 1 + rightItem.recordBtn.alpha = 1 + rightItem.languageItem.alpha = 1 + + leftItem.stopAnimating() + delegate?.recordBar(self, device: .earphone,stateChange: .stop) + } + + } + @objc func rightRecordBtnAction(){ + if leftItem.recordBtn.activeView.animating { + return + } + if leftItem.recordBtn.isSelected { + return + } + rightItem.recordBtn.isSelected = !rightItem.recordBtn.isSelected + + if rightItem.recordBtn.isSelected { + leftItem.recordBtn.alpha = 0.3 + leftItem.languageItem.alpha = 0.3 + rightItem.recordBtn.alpha = 1 + rightItem.languageItem.alpha = 0.3 + + rightItem.startAnimating() + delegate?.recordBar(self, device: .phone,stateChange: .start) + } + else{ + leftItem.recordBtn.alpha = 1 + leftItem.languageItem.alpha = 1 + rightItem.recordBtn.alpha = 1 + rightItem.languageItem.alpha = 1 + + rightItem.stopAnimating() + delegate?.recordBar(self, device: .phone,stateChange: .stop) + } + } + + @objc func leftLanguageAction(){ + if leftItem.recordBtn.isSelected || rightItem.recordBtn.isSelected { + return + } + let v = LanguagePickerView(languages: AiSupportLanguage.translate,isLeft: true,left: leftItem.language,right: rightItem.language) + v.selectedIndex = v.list.firstIndex(where: {$0.title == leftItem.language.title}) ?? 0 + v.show() + v.didSelected = {[weak self] (index,value) in + guard let self = self else { return } + if index == 0 { + leftItem.language = value + } + else{ + rightItem.language = value + } + } + } + @objc func rightLanguageAction(){ + if leftItem.recordBtn.isSelected || rightItem.recordBtn.isSelected{ + return + } + let v = LanguagePickerView(languages: AiSupportLanguage.translate,isLeft: false,left: leftItem.language,right: rightItem.language) + v.selectedIndex = v.list.firstIndex(where: {$0.title == rightItem.language.title}) ?? 0 + v.show() + v.didSelected = {[weak self] (index,value) in + guard let self = self else { return } + if index == 0 { + leftItem.language = value + } + else{ + rightItem.language = value + } + } + } + class RecordBarItem: UIView { + let recordBtn = RecordingButton() + let languageItem = RecordLanguageItem() + + var language:AiSupportLanguage = AiSupportLanguage.chinse{ + didSet{ + languageItem.language = language + } + } + + func startAnimating(){ + recordBtn.startAnimating() + } + func stopAnimating(){ + recordBtn.stopAnimating() + } + + override init(frame: CGRect) { + super.init(frame: frame) + setupViews() + setupLayout() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setupViews() { +// backgroundColor = .red + addSubviews([recordBtn,languageItem]) + recordBtn.do { v in +// v.yc.setBorder(width: 1, color: .color(.theme), radius: 17) + } + } + + func setupLayout() { + recordBtn.snp.makeConstraints { make in + make.width.equalTo(66) + make.height.equalTo(34) + make.top.equalToSuperview().inset(0) + make.centerX.equalToSuperview() + } + languageItem.snp.makeConstraints { make in + make.right.equalToSuperview() + make.top.equalTo(recordBtn.snp.bottom).offset(0) + make.centerX.equalToSuperview() + } + + } + } + class RecordLanguageItem: BaseView { + let icon = UIImageView() + let titleLabel = UILabel() + let valueLabel = UILabel() + + var language:AiSupportLanguage = AiSupportLanguage.chinse{ + didSet{ + titleLabel.text = language.title + } + } + + override func setupViews() { +// backgroundColor = .green + addSubviews([titleLabel,valueLabel,icon]) + icon.do { v in + v.image = UIImage(named: "polygon") + } + titleLabel.do { v in + v.textColor = .rgb(0x121212) + v.font = .bold(16) + } + valueLabel.do { v in + v.textColor = .rgb(0x666666) + v.font = .regular(10) + v.text = "耳机" + } + } + + override func setupLayout() { + icon.snp.makeConstraints { make in + make.size.equalTo(12) + make.top.equalToSuperview().inset(10) + make.right.equalToSuperview() + } + titleLabel.snp.makeConstraints { make in + make.right.equalTo(icon.snp.left).offset(-5) + make.centerY.equalTo(icon) + make.left.equalToSuperview() + } + valueLabel.snp.makeConstraints { make in + make.right.equalTo(titleLabel) + make.top.equalTo(titleLabel.snp.bottom).offset(5) + make.bottom.equalToSuperview().inset(10) + } + } + } +} diff --git a/ShenYin/ShenYin/UI/Home/Dialog/DialogTranlateVC.swift b/ShenYin/ShenYin/UI/Home/Dialog/DialogTranlateVC.swift new file mode 100644 index 0000000..7f8263d --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/Dialog/DialogTranlateVC.swift @@ -0,0 +1,341 @@ +// +// DialogTranslateVC.swift +// ShenYin +// +// Created by liangshi on 2025/8/6. +// + +import UIKit +import JoyGemAI +import AVFoundation +class DialogTranslateVC:BaseVC{ + let toolBar = DialogRecordBar() + var recordFilePath : String? + + let tableView = UITableView(frame: CGRectZero, style: .plain) + var dataList:[DialogTranslateCellModel] = [] + + var tmpList : [(String,UInt)] = [] + var tmpOtherList : [(String,UInt)] = [] + var otherPcm = Data() + + override func viewDidLoad() { + super.viewDidLoad() + } + override func setupViews() { + view.addSubviews([toolBar,tableView]) + + navBar.title = "对话翻译" + navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal) + + toolBar.do { v in + v.backgroundColor = .clear + v.delegate = self + } + view.backgroundColor = .hexString("#F7F7F7") + + tableView.do { v in + v.backgroundColor = .hexString("#F7F7F7") + v.delegate = self + v.dataSource = self + v.estimatedRowHeight = 60 + v.separatorStyle = .none + v.register(DialogTranslateCell.self, forCellReuseIdentifier: DialogTranslateCell.reuseID) + } + } + override func setupLayout() { + toolBar.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.height.equalTo(120) + make.bottom.equalToSuperview().inset(20) + } + tableView.snp.makeConstraints { make in + make.top.equalToSuperview().inset(kNavBtm) + make.left.equalToSuperview() + make.width.equalTo(kScreenWid) + make.bottom.equalTo(toolBar.snp.top) + } + } + override func setupData() { + + JoyGemSIManager.shared().delegate = self + JoyGemSIDeviceManager.shared().delegate = self + + PcmSound.sharedInstance().delegate = self; + PcmSound.sharedInstance().setup(16000, withchannels: 1) + do { + try AVAudioSession.sharedInstance().setActive(true) + } catch { + print(error) + } + PcmSound.sharedInstance().setup(16000, withchannels: 1) + } + override func navRightViewTapped() { + let tip = RecordTipsView(type: .tongchuan) + tip.show() + } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = false + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = true + } + override func navleftViewTapped() { + if toolBar.leftItem.recordBtn.isSelected || toolBar.rightItem.recordBtn.isSelected { + CustomAlert.showWith(title: "提示", messgage: "当前正在录音,确定放弃当前录音吗?", cancelCallback: nil) { + JoyGemSIManager.shared().stop() + JoyGemSIDeviceManager.shared().stop() + PcmSound.sharedInstance().stopAudioUnit() + PcmSound.sharedInstance().cleanup() + super.navleftViewTapped() + } + } + else{ + super.navleftViewTapped() + } + } + + func refreshData(){ + tableView.reloadData() + if dataList.count > 0 { + tableView.scrollToRow(at: IndexPath(row: dataList.count - 1, section: 0), at: .none, animated: true) + } + } +} + +extension DialogTranslateVC:UITableViewDelegate,UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return dataList.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = DialogTranslateCell.cell(withTableView: tableView, indexPath: indexPath) + cell.data = dataList[indexPath.row] + return cell + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + + } +} + +extension DialogTranslateVC:DialogRecordBarDelegate{ + func recordBar(_ bar: DialogRecordBar, layoutChange: DialogRecordLayoutType) { + + } + + func recordBar(_ bar: DialogRecordBar, device: DialogRecordDeviceType, stateChange state: RecordBarSate) { + if device == .earphone { + if state == .start { + startEarRecord() + } + else{ + stopEarRecord() + } + } + else if device == .phone{ + if state == .start { + startPhoneRecord() + } + + else{ + stopPhoneRecord() + } + } + } + @objc func startPhoneRecord() { + tmpList.removeAll() + tmpOtherList.removeAll() + let model = DialogTranslateCellModel() + model.isPhone = true + dataList.append(model) + refreshData() + + JoyGemSIManager.shared().originalLanguage = toolBar.rightItem.language.value + JoyGemSIManager.shared().targetLanguage = toolBar.leftItem.language.value + JoyGemSIManager.shared().start() + } + + @objc func stopPhoneRecord() { + JoyGemSIManager.shared().stop() + } + + @objc func startEarRecord() { + tmpList.removeAll() + tmpOtherList.removeAll() + let model = DialogTranslateCellModel() + model.isPhone = false + dataList.append(model) + refreshData() + + JoyGemSIDeviceManager.shared().originalLanguage = toolBar.leftItem.language.value + JoyGemSIDeviceManager.shared().targetLanguage = toolBar.rightItem.language.value + JoyGemSIDeviceManager.shared().start() + otherPcm = Data() + do { + try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: [.defaultToSpeaker]) + try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker) + } catch { + print(error) + } + } + + @objc func stopEarRecord() { + JoyGemSIDeviceManager.shared().stop() + } +} + +extension DialogTranslateVC : JoyGemSIManagerDelegate { + func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) { + + } + + func siManager(_ manager: JoyGemSIManager, didUpdateRaw model: JoyGemAsrModel) { + let msg = tmpList.firstIndex (where: { (str,idx) in + return idx == model.index; + }) + if (msg == nil) { + tmpList.append((model.text,model.index)) + } else { + tmpList[msg!] = (model.text,model.index) + } + + ///监听到一句话结束,自动结束当前录音 + if (model.definite == true) { + stopPhoneRecord() + } + + guard let model = dataList.last else { + return + } + + model.send = tmpList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + refreshData() + } + + func siManager(_ manager: JoyGemSIManager, didUpdateTranslate model: JoyGemAsrModel) { + let msg = tmpOtherList.firstIndex (where: { (str,idx) in + return idx == model.index; + }) + if (msg == nil) { + tmpOtherList.append((model.text,model.index)) + } else { + tmpOtherList[msg!] = (model.text,model.index) + } + + guard let model = dataList.last else { + return + } + + model.response = tmpOtherList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + refreshData() + } + + func siManager(_ manager: JoyGemSIManager, saveFile path: String) { + + } + + + func siManager(_ manager: JoyGemSIManager, stateChange code: JoyGemStateCode) { + + } + + func siManagerStopped(_ manager: JoyGemSIManager) { + + } + + func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data) { + PcmSound.sharedInstance().feed(data) + } + + func siManager(_ manager: JoyGemSIManager, onError code: JoyGemErrorCode) { + + } +} + +extension DialogTranslateVC : JoyGemSIDeviceManagerDelegate { + + func siDeviceManagerStopped(_ manager: JoyGemSIDeviceManager, reason code: JoyGemRecordStopCode) { + + } + + func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateRaw model: JoyGemAsrModel) { + debugPrint("text: \(model.text),index: \(model.index)") + let msg = tmpList.firstIndex (where: { (str,idx) in + return idx == model.index; + }) + if (msg == nil) { + tmpList.append((model.text,model.index)) + } else { + tmpList[msg!] = (model.text,model.index) + } + + ///监听到一句话结束,自动结束当前录音 + if (model.definite == true) { + stopEarRecord() + } + + guard let model = dataList.last else { + return + } + + model.send = tmpList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + refreshData() + } + + func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateTranslate model: JoyGemAsrModel) { + let msg = tmpOtherList.firstIndex (where: { (str,idx) in + return idx == model.index; + }) + if (msg == nil) { + tmpOtherList.append((model.text,model.index)) + } else { + tmpOtherList[msg!] = (model.text,model.index) + } + + guard let model = dataList.last else { + return + } + + model.response = tmpOtherList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + refreshData() + } + + func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data) { + //otherPcm.append(data) + PcmSound.sharedInstance().feed(data) + } + + func siDeviceManager(_ manager: JoyGemSIDeviceManager, update mode: JoyGemRecordMode) { + + } + + func siDeviceManager(_ manager: JoyGemSIDeviceManager, saveFile path: String) { + debugPrint("path: \(path)") + } + + func siDeviceManager(_ manager: JoyGemSIDeviceManager, stateChange code: JoyGemStateCode) { + debugPrint("code: \(code)") + } + + func siDeviceManager(_ manager: JoyGemSIDeviceManager, onError code: JoyGemErrorCode) { + + } +} + + +extension DialogTranslateVC : PcmSoundDelegate { + + func remaining(_ frames: UInt) { + debugPrint("[frame:\(frames)]"); + } +} diff --git a/ShenYin/ShenYin/UI/Home/Dialog/DialogTranslateCell.swift b/ShenYin/ShenYin/UI/Home/Dialog/DialogTranslateCell.swift new file mode 100644 index 0000000..952387e --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/Dialog/DialogTranslateCell.swift @@ -0,0 +1,142 @@ +// +// DialogTranslateCell.swift +// ShenYin +// +// Created by liangshi on 2025/9/17. +// + +class DialogTranslateCellModel{ + var send = "" + var response = "" + var isPhone : Bool = false //是否手机收音 + + init(send: String = "", response: String = "") { + self.send = send + self.response = response + } +} + + + + +class DialogTranslateCell:BaseTableViewCell{ + let bgView = UIView() + let line = BrokenLineView() + let originalLabel = UILabel() + let translationLabel = UILabel() + let copyIcon = UIImageView() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func setupViews() { + contentView.backgroundColor = .hexString("#F7F7F7") + + bgView.do { v in + v.yc.setCornerRadius(12) + v.backgroundColor = .white + } + originalLabel.do { v in + v.font = .regular(14) + v.textColor = .hexString("#606D80") + v.numberOfLines = 0 + v.textAlignment = .left + } + translationLabel.do { v in + v.font = .regular(14) + v.textColor = .hexString("#121212") + v.numberOfLines = 0 + v.textAlignment = .left + } + line.do { v in + // v.backgroundColor = .hexString("#93C6EC") + } + copyIcon.do { v in + v.image = UIImage(named: "play_animate_3") + v.animationImages = [UIImage(named: "play_animate_1")!,UIImage(named: "play_animate_2")!,UIImage(named: "play_animate_3")!] + v.animationDuration = 0.6 + v.animationRepeatCount = 0 + v.yc.addTap(target: self, action: #selector(copyAction)) + } + contentView.addSubview(bgView) + bgView.addSubviews([originalLabel,translationLabel,line,copyIcon]) + } + + override func setupLayout() { + bgView.snp.makeConstraints { make in + make.top.bottom.equalToSuperview().inset(10) + make.width.lessThanOrEqualTo(kScreenWid - 58) + make.left.equalToSuperview().inset(18) + } + + originalLabel.snp.makeConstraints { make in + make.top.equalToSuperview().inset(14) + make.left.equalToSuperview().inset(12) + make.height.greaterThanOrEqualTo(15) + make.width.lessThanOrEqualTo(kScreenWid - 110) + } + line.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(12) + make.height.equalTo(1) + make.top.equalTo(originalLabel.snp.bottom).offset(15) + } + + translationLabel.snp.makeConstraints { make in + make.top.equalTo(line.snp.bottom).offset(15) + make.left.equalToSuperview().inset(12) + make.width.lessThanOrEqualTo(kScreenWid - 110) + make.height.greaterThanOrEqualTo(15) + make.bottom.equalToSuperview().inset(14) + } + copyIcon.snp.makeConstraints { make in + make.right.equalToSuperview().inset(12) + make.size.equalTo(24) + make.top.equalToSuperview().inset(12) + } + } + + var data:DialogTranslateCellModel!{ + didSet{ + originalLabel.text = data.send + translationLabel.text = data.response + + if data.isPhone { +// bgView.snp.remakeConstraints { make in +// make.top.bottom.equalToSuperview().inset(10) +// make.width.lessThanOrEqualTo(kScreenWid - 58) +// make.left.equalToSuperview().inset(18) +// } + bgView.backgroundColor = .color(.theme) + originalLabel.textColor = .white + translationLabel.textColor = .white + } + else{ + bgView.backgroundColor = .white + originalLabel.textColor = .hexString("606D80") + translationLabel.textColor = .hexString("#121212") +// bgView.snp.remakeConstraints { make in +// make.top.bottom.equalToSuperview().inset(10) +// make.width.lessThanOrEqualTo(kScreenWid - 58) +// make.right.equalToSuperview().inset(18) +// } + + } + } + } + @objc func copyAction(){ + // UIPasteboard.general.string = data.send + "\n" + data.response + // YLprogressHUD.showAutoHiden("已拷贝") + if copyIcon.isAnimating { + copyIcon.stopAnimating() + } + else{ + copyIcon.startAnimating() + } + } +} diff --git a/ShenYin/ShenYin/UI/Home/FieldRecording/FieldRecordBar.swift b/ShenYin/ShenYin/UI/Home/FieldRecording/FieldRecordBar.swift index bc6c279..fc0689c 100644 --- a/ShenYin/ShenYin/UI/Home/FieldRecording/FieldRecordBar.swift +++ b/ShenYin/ShenYin/UI/Home/FieldRecording/FieldRecordBar.swift @@ -19,7 +19,7 @@ protocol FieldRecordBarDelegate { class FieldRecordBar: BaseView { - let recordBtn = LoadingButton() + let recordBtn = LoadingButton() let leftItem = RecordBarItem() let rightItem = RecordBarButton() var state:RecordSate = .stop @@ -27,7 +27,7 @@ class FieldRecordBar: BaseView { var hasRightItem:Bool = true override func setupViews() { - backgroundColor = .white + backgroundColor = .hexString("#F7F7F7") addSubviews([leftItem,rightItem,recordBtn]) recordBtn.do { v in @@ -61,8 +61,18 @@ class FieldRecordBar: BaseView { make.left.equalToSuperview().offset(kScreenWid/2 + 60) } } + + func startAnimating(finished:(()->Void)? = nil){ + leftItem.alpha = 0.3 + recordBtn.startAnimating {[weak self] in + guard let self = self else{return} + leftItem.alpha = 1 + finished?() + + } + } @objc func leftAction(){ - if recordBtn.isSelected { + if recordBtn.isSelected || recordBtn.activeView.animating { return } let list = AiSupportLanguage.record.map{$0.title} @@ -78,6 +88,9 @@ class FieldRecordBar: BaseView { return } @objc func recordBtnAction(){ + if recordBtn.activeView.animating { + return + } recordBtn.isSelected = !recordBtn.isSelected rightItem.state = .start rightItem.isHidden = !recordBtn.isSelected diff --git a/ShenYin/ShenYin/UI/Home/FieldRecording/FieldRecordingVC.swift b/ShenYin/ShenYin/UI/Home/FieldRecording/FieldRecordingVC.swift index dc2c896..d8ba505 100644 --- a/ShenYin/ShenYin/UI/Home/FieldRecording/FieldRecordingVC.swift +++ b/ShenYin/ShenYin/UI/Home/FieldRecording/FieldRecordingVC.swift @@ -15,10 +15,7 @@ class FieldRecordingVC:BaseVC{ var messageList : [(String,UInt)] = [] var recordFilePath : String? - - override func viewDidLoad() { - super.viewDidLoad() - } + override func setupViews() { navBar.title = "现场录音" toolBar.delegate = self @@ -27,6 +24,7 @@ class FieldRecordingVC:BaseVC{ v.isEditable = false v.font = .regular(14) v.text = "" + v.backgroundColor = .hexString("#F7F7F7") v.textColor = .hexString("#121212") v.contentInset = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18) } @@ -62,7 +60,30 @@ class FieldRecordingVC:BaseVC{ Logger.debug(error) } } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = false + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = true + } + override func navleftViewTapped() { + if toolBar.recordBtn.isSelected { + CustomAlert.showWith(title: "提示", messgage: "当前正在录音,确定放弃当前录音吗?", cancelCallback: nil) { + JoyGemRecordManager.shared().stop() + PcmSound.sharedInstance().stopAudioUnit() + PcmSound.sharedInstance().cleanup() + super.navleftViewTapped() + } + } + else{ + super.navleftViewTapped() + } + } func scrollToBottom(){ + textView.scrollRangeToVisible(NSMakeRange(textView.text.count - 1, 1)) } } @@ -86,7 +107,7 @@ extension FieldRecordingVC:FieldRecordBarDelegate{ } else if state == .stop{ JoyGemRecordManager.shared().stop() - bar.recordBtn.startAnimating {[weak self] in + bar.startAnimating {[weak self] in guard let self = self else{return} // toolBar.recordBtn.stopCli() let v = PopTextField() diff --git a/ShenYin/ShenYin/UI/Home/FieldRecording/RecordBar.swift b/ShenYin/ShenYin/UI/Home/FieldRecording/RecordBar.swift index 1ffbbde..b748df4 100644 --- a/ShenYin/ShenYin/UI/Home/FieldRecording/RecordBar.swift +++ b/ShenYin/ShenYin/UI/Home/FieldRecording/RecordBar.swift @@ -22,7 +22,7 @@ class RecordBar: BaseView { var delegate:RecordBarDelegate? override func setupViews() { - backgroundColor = .white + backgroundColor = .hexString("#F7F7F7") addSubviews([leftItem,rightItem,recordBtn]) recordBtn.do { v in @@ -57,27 +57,43 @@ class RecordBar: BaseView { make.left.equalToSuperview().offset(kScreenWid/2 + 60) } } - + func startAnimating(finished:(()->Void)? = nil){ + leftItem.alpha = 0.3 + rightItem.alpha = 0.3 + recordBtn.startAnimating {[weak self] in + guard let self = self else{return} + leftItem.alpha = 1 + rightItem.alpha = 1 + finished?() + + } + } @objc func recordBtnAction(){ + if recordBtn.activeView.animating { + return + } recordBtn.isSelected = !recordBtn.isSelected if recordBtn.isSelected { - delegate?.recordBar(self, stateChange: .start) + leftItem.alpha = 0.3 + rightItem.alpha = 0.3 } else{ - delegate?.recordBar(self, stateChange: .stop) + leftItem.alpha = 1 + rightItem.alpha = 1 } if recordBtn.isSelected { - leftItem.alpha = 0.3 - rightItem.alpha = 0.3 + delegate?.recordBar(self, stateChange: .start) } else{ - leftItem.alpha = 1 - rightItem.alpha = 1 + delegate?.recordBar(self, stateChange: .stop) } } @objc func leftAction(){ + if recordBtn.activeView.animating { + return + } if recordBtn.isSelected { return } @@ -95,6 +111,9 @@ class RecordBar: BaseView { } } @objc func rightAction(){ + if recordBtn.activeView.animating { + return + } if recordBtn.isSelected { return } diff --git a/ShenYin/ShenYin/UI/Home/RecordDetail/RecordDetailVC.swift b/ShenYin/ShenYin/UI/Home/RecordDetail/RecordDetailVC.swift index 479fdd5..8624305 100644 --- a/ShenYin/ShenYin/UI/Home/RecordDetail/RecordDetailVC.swift +++ b/ShenYin/ShenYin/UI/Home/RecordDetail/RecordDetailVC.swift @@ -78,7 +78,8 @@ class RecordDetailVC:BaseVC{ } func scrollToBottom(){ - + let range = NSMakeRange(textView.text.count - 1, 1) + textView.scrollRangeToVisible(range) } } extension RecordDetailVC:RecordDetailTopBarDelegate{ diff --git a/ShenYin/ShenYin/UI/Home/RecordTranslateVC.swift b/ShenYin/ShenYin/UI/Home/RecordTranslateVC.swift index 86e9c8d..fc60166 100644 --- a/ShenYin/ShenYin/UI/Home/RecordTranslateVC.swift +++ b/ShenYin/ShenYin/UI/Home/RecordTranslateVC.swift @@ -5,7 +5,6 @@ // Created by liangshi on 2025/8/6. // - import UIKit import JoyGemAI import AVFoundation @@ -17,41 +16,30 @@ class RecordTranslateVC:BaseVC{ var translateList : [(String,UInt)] = [] var recordFilePath : String? - override func viewDidLoad() { - super.viewDidLoad() - } + override func setupViews() { - navBar.title = "同传听译" - navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal) view.addSubviews([toolBar,textView,translateTextView]) - toolBar.delegate = self + navBar.title = "同传听译" + navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal) - textView.do { v in - v.isEditable = false - v.font = .regular(14) - v.text = "" - v.textColor = .hexString("#121212") - v.contentInset = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18) + toolBar.do { v in + v.backgroundColor = .clear + v.delegate = self + v.recordBtn.activeView.backgroundColor = .hexString("#F7F7F7") } + view.backgroundColor = .hexString("#F7F7F7") + textView.do { v in - v.isEditable = false - v.font = .regular(14) - v.text = "" - v.textColor = .hexString("#121212") - v.contentInset = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18) - v.yc.setBorder(width: 1, color: .black) + v.placeHolde = "此处将显示原文" + v.textView.textColor = .hexString("#666666") } translateTextView.do { v in - v.isEditable = false - v.font = .regular(14) - v.text = "" - v.textColor = .hexString("#121212") -// v.yc.addgradientLayer(colors: [.hexString("#0092E1").withAlphaComponent(0.1),.white], direction: .top2down,rect: CGRect(x: 0, y: 0, width: kScreenWid, height: (kScreenHei - kNavBtm - 120 - 10)/2)) -// v.contentInset = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18) - v.yc.setBorder(width: 1, color: .black) -// v.yc.setCorners(withRoundedRect: CGRect(x: 0, y: 0, width: kScreenWid, height: (kScreenHei - kNavBtm - 120 - 10)/2), roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 20, height: 20)) - + v.textView.textColor = .hexString("#121212") + v.placeHolde = "此处将显示原文译文" + v.bgView.yc.addgradientLayer(colors: [.hexString("#0092E1"),.hexString("#F7F7F7")], direction: .top2down,rect: CGRect(x: 0, y: 0, width: kScreenWid, height: (kScreenHei - kNavBtm - 120 - 10)/2)) + v.bgView.alpha = 0.1 + v.yc.setCorners(withRoundedRect: CGRect(x: 0, y: 0, width: kScreenWid, height: (kScreenHei - kNavBtm - 120 - 10)/2), roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 20, height: 20)) } } override func setupLayout() { @@ -87,68 +75,84 @@ class RecordTranslateVC:BaseVC{ let tip = RecordTipsView(type: .tongchuan) tip.show() } - func scrollToBottom(){ - let range = NSMakeRange(textView.text.count - 1, 1) - textView.scrollRangeToVisible(range) + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = false + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + navigationController?.fd_fullscreenPopGestureRecognizer?.isEnabled = true + } + override func navleftViewTapped() { + if toolBar.recordBtn.isSelected { + CustomAlert.showWith(title: "提示", messgage: "当前正在录音,确定放弃当前录音吗?", cancelCallback: nil) { + JoyGemSIDeviceManager.shared().stop() + PcmSound.sharedInstance().stopAudioUnit() + PcmSound.sharedInstance().cleanup() + super.navleftViewTapped() + } + } + else{ + super.navleftViewTapped() + } } } extension RecordTranslateVC:RecordBarDelegate{ - + func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) { - textView.text = "123" - -// if state == .start { -// messageList.removeAll() -// translateList.removeAll() -// textView.text = nil -// translateTextView.text = nil -// -// if Thread.isMainThread { -// do { -// try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: []) -// try AVAudioSession.sharedInstance().setActive(true) -// } catch let error as NSError { -// Logger.debug("An error occurred setting the audio session category: \(error.localizedDescription)") -// } -// } else { -// DispatchQueue.main.sync { -// do { -// try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: []) -// try AVAudioSession.sharedInstance().setActive(true) -// } catch let error as NSError { -// Logger.debug("An error occurred setting the audio session category: \(error.localizedDescription)") -// } -// } -// } -// JoyGemSIManager.shared().originalLanguage = toolBar.leftItem.language.value -// JoyGemSIManager.shared().targetLanguage = toolBar.rightItem.language.value -// JoyGemSIManager.shared().start() -// } -// else if state == .stop{ -// JoyGemSIManager.shared().stop() -// bar.recordBtn.startAnimating {[weak self] in -// guard let self = self else{return} -// // toolBar.recordBtn.stopCli() -// let v = PopTextField() -// v.show() -// v.cancelTap = { -// -// } -// v.okTap = { -// -// } -// } -// } + if state == .start { + messageList.removeAll() + translateList.removeAll() + textView.text = nil + translateTextView.text = nil + + if Thread.isMainThread { + do { + try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: []) + try AVAudioSession.sharedInstance().setActive(true) + } catch let error as NSError { + Logger.debug("An error occurred setting the audio session category: \(error.localizedDescription)") + } + } else { + DispatchQueue.main.sync { + do { + try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: []) + try AVAudioSession.sharedInstance().setActive(true) + } catch let error as NSError { + Logger.debug("An error occurred setting the audio session category: \(error.localizedDescription)") + } + } + } + JoyGemSIManager.shared().originalLanguage = toolBar.leftItem.language.value + JoyGemSIManager.shared().targetLanguage = toolBar.rightItem.language.value + JoyGemSIManager.shared().start() + } + else if state == .stop{ + JoyGemSIManager.shared().stop() + bar.startAnimating {[weak self] in + guard let self = self else{return} + // toolBar.recordBtn.stopCli() + let v = PopTextField() + v.show() + v.cancelTap = { + + } + v.okTap = { + + } + } + } } } extension RecordTranslateVC : JoyGemSIManagerDelegate { func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) { if (code == .actively) { - YLprogressHUD.showAutoHiden("用户主动结束") +// YLprogressHUD.showAutoHiden("用户主动结束") } else { - YLprogressHUD.showAutoHiden("用户被动结束") + toolBar.recordBtn.isSelected = false } } diff --git a/ShenYin/ShenYin/UI/Home/Root/HomeFunctionView.swift b/ShenYin/ShenYin/UI/Home/Root/HomeFunctionView.swift index 69513b0..fd4d4f6 100644 --- a/ShenYin/ShenYin/UI/Home/Root/HomeFunctionView.swift +++ b/ShenYin/ShenYin/UI/Home/Root/HomeFunctionView.swift @@ -165,7 +165,7 @@ class HomeCell: BaseView { case .recordTranslate: App.shared.goToPage(RecordTranslateVC()) case .dialogTranslate: - App.shared.goToPage(DialogTranlateVC()) + App.shared.goToPage(DialogTranslateVC()) case .textTranslate: App.shared.goToPage(TextTranlateVC()) } diff --git a/ShenYin/ShenYin/UI/Home/TextTranslate/TextTranslateVC.swift b/ShenYin/ShenYin/UI/Home/TextTranslate/TextTranslateVC.swift index 8a9417f..bacf365 100644 --- a/ShenYin/ShenYin/UI/Home/TextTranslate/TextTranslateVC.swift +++ b/ShenYin/ShenYin/UI/Home/TextTranslate/TextTranslateVC.swift @@ -61,7 +61,8 @@ class TextTranlateVC:BaseVC{ make.bottom.equalToSuperview() } } - func scrollToBottom(){ + func refreshData(){ + tableView.reloadData() if dataList.count > 0 { tableView.scrollToRow(at: IndexPath(row: dataList.count - 1, section: 0), at: .none, animated: true) } @@ -85,8 +86,7 @@ extension TextTranlateVC:SendToolBarDelegate{ func sendToolBar(_ toolBar: SendToolBar, _ text: String) { // toolBar.textField.resignFirstResponder() dataList.append(TranslateCellModel(send: text,response: "")) - tableView.reloadData() - scrollToBottom() + refreshData() let from = languageItem.leftLabel.language.value let to = languageItem.rightLabel.language.value @@ -95,8 +95,7 @@ extension TextTranlateVC:SendToolBarDelegate{ guard let self = self else{return} if !finish{ dataList.last!.response = dataList.last!.response + result - tableView.reloadData() - scrollToBottom() + refreshData() } } error: { error in diff --git a/ShenYin/ShenYin/UI/Mine/Login/ViewModel/LoginVM.swift b/ShenYin/ShenYin/UI/Mine/Login/ViewModel/LoginVM.swift index b90049f..551f367 100644 --- a/ShenYin/ShenYin/UI/Mine/Login/ViewModel/LoginVM.swift +++ b/ShenYin/ShenYin/UI/Mine/Login/ViewModel/LoginVM.swift @@ -5,6 +5,27 @@ // Created by liangshi on 2025/7/30. // +enum SmsType +{ + case reg + case forget + case update + case login + + var value:String{ + switch self { + case .reg: + return "reg" + case .forget: + return "forget" + case .update: + return "update" + case .login: + return "login" + } + } +} + class LoginModel:ResponseBaseModel { var data:String = "" } @@ -12,7 +33,7 @@ class LoginModel:ResponseBaseModel { class LoginVM:BaseVM{ static public func sendCode(phone:String, completion:@escaping (_ result:ResponseBaseModel) ->Void) { - HttpClient.instance.request(CommomApi.sms(phone: phone)) { (model:ResponseBaseModel, res) in + HttpClient.instance.request(CommomApi.sms(phone: phone,type: .login)) { (model:ResponseBaseModel, res) in completion(model) } } diff --git a/ShenYin/ShenYin/UI/common/CustomAlert.swift b/ShenYin/ShenYin/UI/common/CustomAlert.swift index 6054d7e..8e46a35 100644 --- a/ShenYin/ShenYin/UI/common/CustomAlert.swift +++ b/ShenYin/ShenYin/UI/common/CustomAlert.swift @@ -8,17 +8,29 @@ class CustomAlert { +// static func showWith(title:String,messgage:String,cancel:String = "取消",ok:String = "确定",cancelCallback: (()->Void)?,okCallback: (()->Void)?){ +// let vc = UIAlertController(title: "音量警告", message: "为保护听力,请勿在佩戴耳机时使用此功能。", preferredStyle: .alert) +// let action0 = UIAlertAction(title: "取消", style: .cancel) { action in +// cancelCallback?() +// } +// let action1 = UIAlertAction(title: "播放", style: .default) { action in +// okCallback?() +// } +// vc.addAction(action0) +// vc.addAction(action1) +// App.shared.topVC()?.present(vc, animated: true) +// } + static func showWith(title:String,messgage:String,cancel:String = "取消",ok:String = "确定",cancelCallback: (()->Void)?,okCallback: (()->Void)?){ - let vc = UIAlertController(title: "音量警告", message: "为保护听力,请勿在佩戴耳机时使用此功能。", preferredStyle: .alert) - let action0 = UIAlertAction(title: "取消", style: .cancel) { action in + let vc = UIAlertController(title: title, message: messgage, preferredStyle: .alert) + let action0 = UIAlertAction(title: cancel, style: .cancel) { action in cancelCallback?() } - let action1 = UIAlertAction(title: "播放", style: .default) { action in + let action1 = UIAlertAction(title: ok, style: .default) { action in okCallback?() } vc.addAction(action0) vc.addAction(action1) App.shared.topVC()?.present(vc, animated: true) } - } diff --git a/ShenYin/ShenYin/UI/common/LoadingButton.swift b/ShenYin/ShenYin/UI/common/LoadingButton.swift index 464b106..8bec230 100644 --- a/ShenYin/ShenYin/UI/common/LoadingButton.swift +++ b/ShenYin/ShenYin/UI/common/LoadingButton.swift @@ -6,14 +6,15 @@ // import DGActivityIndicatorView import UIKit + class LoadingButton:UIButton{ - let loadingView = DGActivityIndicatorView(type: .ballClipRotate,tintColor: UIColor.hexString("#0092E1"))! + let activeView = DGActivityIndicatorView(type: .ballClipRotate,tintColor: UIColor.hexString("#0092E1"))! + var loading = false override init(frame: CGRect) { super.init(frame: frame) - loadingView.backgroundColor = .white - addSubview(loadingView) - loadingView.snp.remakeConstraints { make in + addSubview(activeView) + activeView.snp.remakeConstraints { make in make.edges.equalToSuperview() } } @@ -22,14 +23,17 @@ class LoadingButton:UIButton{ fatalError("init(coder:) has not been implemented") } func startAnimating(finished:(()->Void)? = nil){ - loadingView.startAnimating() + loading = true + activeView.backgroundColor = .hexString("#F7F7F7") + activeView.startAnimating() DispatchQueue.main.asyncAfter(deadline:.now() + 3) { self.stopAnimating() finished?() } } func stopAnimating(){ - loadingView.stopAnimating() + loading = false + activeView.stopAnimating() } } @@ -58,3 +62,55 @@ class LoadingView: UIView { activeView.stopAnimating() } } +class RecordingView: UIView { + let activeView = DGActivityIndicatorView(type: .lineScalePulseOut,tintColor: UIColor.hexString("#0092E1"))! + + override init(frame: CGRect) { + super.init(frame: frame) + activeView.backgroundColor = .hexString("#F7F7F7") + addSubview(activeView) + activeView.snp.makeConstraints { make in + make.center.equalToSuperview() + make.width.equalTo(60) + make.height.equalTo(40) + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func startAnimating(finished:(()->Void)? = nil){ + bringSubviewToFront(activeView) + activeView.startAnimating() + } + func stopAnimating(){ + activeView.stopAnimating() + } +} +class RecordingButton:UIButton{ + let activeView = DGActivityIndicatorView(type: .lineScalePulseOut,tintColor: UIColor.hexString("#0092E1"))! + + override init(frame: CGRect) { + super.init(frame: frame) + activeView.backgroundColor = .hexString("#F7F7F7") + addSubview(activeView) + activeView.snp.remakeConstraints { make in + make.edges.equalToSuperview() + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + func startAnimating(finished:(()->Void)? = nil){ + activeView.startAnimating() +// DispatchQueue.main.asyncAfter(deadline:.now() + 3) { +// self.stopAnimating() +// finished?() +// } + } + func stopAnimating(){ + activeView.stopAnimating() + } +} diff --git a/ShenYin/ShenYin/UI/common/PlaceHolderTextView.swift b/ShenYin/ShenYin/UI/common/PlaceHolderTextView.swift index 1662bc5..8e08442 100644 --- a/ShenYin/ShenYin/UI/common/PlaceHolderTextView.swift +++ b/ShenYin/ShenYin/UI/common/PlaceHolderTextView.swift @@ -13,54 +13,62 @@ extension UITextView { } } -class PlaceHolderTextView: UITextView { +class PlaceHolderTextView: BaseView { + let bgView = UIView() + let textView = UITextView() let label = UILabel() - var placeHolde:String = ""{ + var placeHolde:String?{ didSet{ label.text = placeHolde } } + var text:String?{ + didSet{ + textView.text = text + if let text = text,text.isNotEmpty { + label.isHidden = true + return + } + label.isHidden = false + } + } - - override init(frame: CGRect, textContainer: NSTextContainer?) { - super.init(frame: frame, textContainer: textContainer) - - self.addObserver(self, forKeyPath: "observedText", options: [.new,.old], context: nil) - - + override func setupViews() { +// yc.setBorder(width: 1, color: .black) label.do { v in - v.text = "此处将显示原文" + v.backgroundColor = .clear v.textAlignment = .center v.textColor = .hexString("#666666") v.font = .regular(16) } - addSubview(label) + textView.do { v in + v.isEditable = false + v.font = .regular(14) + v.textColor = .hexString("#121212") + v.backgroundColor = .clear + v.contentInset = UIEdgeInsets(top: 18, left: 18, bottom: 18, right: 18) + } + addSubviews([bgView]) + addSubviews([textView,label]) + + } + override func setupLayout() { + bgView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } label.snp.makeConstraints { make in make.center.equalToSuperview() } + textView.snp.makeConstraints { make in +// make.left.right.equalToSuperview().inset(18) +// make.top.bottom.equalToSuperview().inset(18) + make.edges.equalToSuperview() + } } - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") + func scrollToBottom(){ + let range = NSMakeRange(textView.text.count - 1, 1) + textView.scrollRangeToVisible(range) } - - override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { - if keyPath == "observedText", let textView = object as? UITextView { - if let newText = change?[.newKey] as? String { - Logger.debug("Text changed to: \(newText)") - if newText.isNotEmpty { - label.isHidden = true - } - else{ - label.isHidden = false - } - } - else{ - label.isHidden = false - } - } - } - - } diff --git a/ShenYin/ShenYin/UI/common/ScrollView+Bottom.swift b/ShenYin/ShenYin/UI/common/ScrollView+Bottom.swift new file mode 100644 index 0000000..99dd61c --- /dev/null +++ b/ShenYin/ShenYin/UI/common/ScrollView+Bottom.swift @@ -0,0 +1,11 @@ +// +// ScrollView+Bottom.swift +// ShenYin +// +// Created by liangshi on 2025/9/19. +// + +extension UITableView { + + +} diff --git a/ShenYin/podfile b/ShenYin/podfile index fee0b8d..426028d 100644 --- a/ShenYin/podfile +++ b/ShenYin/podfile @@ -10,7 +10,7 @@ target 'ShenYin' do pod 'SnapKit'#布局 pod 'YYText'#富文本 pod 'FDFullscreenPopGesture'#侧滑返回 - pod 'TZScrollViewPopGesture'#侧滑返回 +# pod 'TZScrollViewPopGesture'#侧滑返回 pod 'IQKeyboardManagerSwift'#键盘 pod 'MJRefresh'#刷新 pod 'YYModel'#model解析 diff --git a/document/JOYGEM_AI_IOS_V1.0.19/JoyGemAIDemo-swift/JoyGemAIDemo/DialogueViewController.swift b/document/JOYGEM_AI_IOS_V1.0.19/JoyGemAIDemo-swift/JoyGemAIDemo/DialogueViewController.swift index 8a43f05..f34e346 100644 --- a/document/JOYGEM_AI_IOS_V1.0.19/JoyGemAIDemo-swift/JoyGemAIDemo/DialogueViewController.swift +++ b/document/JOYGEM_AI_IOS_V1.0.19/JoyGemAIDemo-swift/JoyGemAIDemo/DialogueViewController.swift @@ -97,10 +97,10 @@ class DialogueViewController: UIViewController { // earStopButton.addTarget(self, action: #selector(stopEarRecord), for: .touchUpInside) JoyGemSIManager.shared().originalLanguage = kJoyGemAILocalizationZH - JoyGemSIManager.shared().targetLanguage = kJoyGemAILocalizationJA + JoyGemSIManager.shared().targetLanguage = kJoyGemAILocalizationEN JoyGemSIManager.shared().delegate = self - JoyGemSIDeviceManager.shared().originalLanguage = kJoyGemAILocalizationJA + JoyGemSIDeviceManager.shared().originalLanguage = kJoyGemAILocalizationEN JoyGemSIDeviceManager.shared().targetLanguage = kJoyGemAILocalizationZH JoyGemSIDeviceManager.shared().delegate = self