eSur}0hS 2J1S!5G_}bDN2?(*%RI;n`9fQ--sM@I8Cf~mTSPz zvFQmQxiDWxuOg#2RI?UKS$O~8!GkX*@Vi_C4{x`77ma93N=hc!^+#)yO|p%I-l7Ey z7I^vyDr^Lhr!f5f`|qETm6g>;hD0+mT01;`K5dWYK6fP-(?Wr5CHKXXmzS4H?m82% z89?6oFD%Q8#G^iw3Qs8YyKvz`BXBJp>_H8FT(J>B#(~?ZQ>Q$6o~0a}`p4!YSZ>#l zc>gm%%z5|ScW0{V{n=-qExYyBTNmR6N_aW$JC@97Pd?=sI|25!;Q&p3f~ppRucm0i zqqV>N_S;)=!G>u4ZrgP#u`dlG0XK +GvC+~%FD}}Hf-3i z4G%P4q8^MkVv$W`0KS!tMvM@HMGN%=uV(-vC)|Kb#Y|qo5K#hwK+5BfKVDA$SY8y- z7ybiR5AfS_Knia}VL}nv7T_7t9{vNHDrB2%3~zVy4i*7cMrhKt#KaRM;ykPuFO|&V zT5>QQNiz%&^4X-muCDIq9VFw|ty{O6^uu~3%e=@B=OGbRyKURH|4Am^@fDL>E?v@T z(Q#^3k@8q){Wg8JV-Z?W7C Dmf{OG2e zZd%K88&T~hzSsZbyYIf+;6)Ehrg`hut?T#>zYLjQO6JWs-@JzB3h9zg=_bFuo>OZ% z2gotmW?E&Nu(0?#0HUFdBe#~Pr?O(jilbLwef8Rf3m5)($&w|Nt5>f+_RTlnoU)Q* zdahcvs{Y}JAO5njvGJ&>5+&nUGFv2uPDTO7u!&LAWUwrYCDUTt2kdsT`L>wj&03k5QrdP)ikyI~Q!V+Z3G@#E_A d=opB`V~fVfX?PGBwkswoKrpo6b_dC-8AsUg z#w ndLY1aPHT*?Mr_Rd?kG+GrQk&;i3^gt{yQu= z4=*gMy&9vFYDa)z)IR_?PHa?(D|Q4=w0G&Ev1m*TB6K0;8MVX$D8xu$r`V`kf t4{hAc!d_hUc&MQZ{3Tnlc@L%<|HBDmfKU=-CQjg{KNG~&pgfc} zq5Xm@*d|Y@maVcG18rj++M^aP09nFX2(S*2rx8%0{H@h-0X&0DUL&?u0F16pQRAFg zw#EaHWCw{Qjsa8!vsFdVrSDJ1vm`-@ssPg@Td--DY>QWJyZ~AHn{eYAQmp0N()Y6= zDhF-#{VGrCBoTK2V9I3+N)uv3q5#646nRd7gi|duw_SmlJc=BP3{{_K5?PhM(RJvQ zbV(;UZ{4YM0Zy2{L;@5(_)tiINA1MjcT=m_59)A#PoKFh!N_S7=z&1s2*B$fjqXOr z4%4GMB+unv$pXky-^%Bc2Dv;LluEfjA1QY*zzV>sVUYC
o_0I{ O0000 UICollectionReusableView{ diff --git a/ShenYin/ShenYin/ShenYin copy-Info.plist b/ShenYin/ShenYin/ShenYin copy-Info.plist index 8ca32a3..936acd4 100644 --- a/ShenYin/ShenYin/ShenYin copy-Info.plist +++ b/ShenYin/ShenYin/ShenYin copy-Info.plist @@ -51,9 +51,9 @@ bluetooth-central audio -UIViewControllerBasedStatusBarAppearance -UIDesignRequiresCompatibility + UIViewControllerBasedStatusBarAppearance +diff --git a/ShenYin/ShenYin/UI/Base/BaseTC.swift b/ShenYin/ShenYin/UI/Base/BaseTC.swift index ebd434e..a6716ae 100644 --- a/ShenYin/ShenYin/UI/Base/BaseTC.swift +++ b/ShenYin/ShenYin/UI/Base/BaseTC.swift @@ -19,7 +19,7 @@ class BaseTC:UITabBarController{ func setupUI(){ if CCDevice.isJoy{ let homeVC = HomeDeviceVC() -// let homeVC = CustomerVC() + let modeVC = ModeVC() let sleepVC = SleepVC() diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/LanguagePickerView1.swift b/ShenYin/ShenYin/UI/Home/SubTitle/LanguagePickerView1.swift new file mode 100644 index 0000000..636af0b --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/SubTitle/LanguagePickerView1.swift @@ -0,0 +1,306 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/3/5. +// + +import UIKit +import JoyGemAI +@available(iOS 15.0, *) +// MARK: - æ°æ®æ¨¡å +struct LanguageModel { + + let code: String + let name: String + let nativeName: String + + static let chinese = LanguageModel(code: kJoyGemAILocalizationZH, name: "Chinese", nativeName: "䏿") + static let english = LanguageModel(code: kJoyGemAILocalizationEN, name: "English", nativeName: "è±è¯") + static let japanese = LanguageModel(code: kJoyGemAILocalizationJA, name: "Japanese", nativeName: "æ¥æ¬èª") + static let korean = LanguageModel(code: kJoyGemAILocalizationKO, name: "Korean", nativeName: "é©è¯") + static let french = LanguageModel(code: kJoyGemAILocalizationFR, name: "French", nativeName: "æ³è¯") + static let spanish = LanguageModel(code: kJoyGemAILocalizationES, name: "Spanish", nativeName: "西ççè¯") + static let portuguese = LanguageModel(code: kJoyGemAILocalizationPT, name: "Portuguese", nativeName: "è¡èçè¯") + static let indonesia = LanguageModel(code: kJoyGemAILocalizationID, name: "Indonesia", nativeName: "å°å°¼è¯") + static let russian_ori = LanguageModel(code: kJoyGemAILocalizationRU_Original, name: "Russian", nativeName: "ä¿è¯") + static let russian_tar = LanguageModel(code: kJoyGemAILocalizationRU_Target, name: "Russian", nativeName: "ä¿è¯") + static let malay = LanguageModel(code: kJoyGemAILocalizationMS, name: "Malay", nativeName: "马æ¥è¯") + static let german = LanguageModel(code: kJoyGemAILocalizationDE, name: "German", nativeName: "å¾·è¯") + static let filipino = LanguageModel(code: kJoyGemAILocalizationFIL, name: "Filipino", nativeName: "è²å¾å®¾è¯") + static let thai = LanguageModel(code: kJoyGemAILocalizationTH, name: "Thai", nativeName: "æ³°è¯") + static let arabic = LanguageModel(code: kJoyGemAILocalizationAR, name: "Arabic", nativeName: "é¿æä¼¯è¯") + static let vietnamese = LanguageModel(code: kJoyGemAILocalizationVI, name: "Vietnamese", nativeName: "è¶åè¯") +} + +// è¯è¨éæ©å¨çå§æåè®® +@available(iOS 15.0, *) +protocol LanguagePickerView1Delegate: AnyObject { + + func languagePickerView(_ pickerView: LanguagePickerView1, + didSelectSourceLanguage source: LanguageModel, + targetLanguage target: LanguageModel) + + func languagePickerViewDidTapSwap(_ pickerView: LanguagePickerView1) +} +@available(iOS 15.0, *) +class LanguagePickerView1: UIView { + + // MARK: - UI ç»ä»¶ + private let sourcePickerView = UIPickerView() + private let targetPickerView = UIPickerView() + private let swapButton = UIButton(type: .system) + private let sourceLabel = UILabel() + private let targetLabel = UILabel() + + // MARK: - 屿§ + weak var delegate: LanguagePickerView1Delegate? + + var selectedSourceLanguage: LanguageModel? { + didSet { updateSelection() } + } + + var selectedTargetLanguage: LanguageModel? { + didSet { updateSelection() } + } + + var languages: [LanguageModel] = [ + .chinese, .english, .japanese,.korean,.french,.spanish,.portuguese,.indonesia,.russian_ori,.malay,.german,.filipino,.thai,.arabic,.vietnamese + ] + + // MARK: - åå§å + override init(frame: CGRect) { + super.init(frame: frame) + setupUI() + setupDefaults() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupUI() + setupDefaults() + } + + // MARK: - UI 设置 + private func setupUI() { + backgroundColor = .systemBackground + + setupLabels() + setupPickerViews() + setupSwapButton() + setupConstraints() + } + + private func setupLabels() { + sourceLabel.text = "æºè¯è¨" + sourceLabel.font = UIFont.boldSystemFont(ofSize: 16) + sourceLabel.textAlignment = .center + addSubview(sourceLabel) + + targetLabel.text = "ç®æ è¯è¨" + targetLabel.font = UIFont.boldSystemFont(ofSize: 16) + targetLabel.textAlignment = .center + addSubview(targetLabel) + } + + private func setupPickerViews() { + // æºè¯è¨éæ©å¨ + sourcePickerView.translatesAutoresizingMaskIntoConstraints = false + sourcePickerView.dataSource = self + sourcePickerView.delegate = self + sourcePickerView.tag = 0 + addSubview(sourcePickerView) + + // ç®æ è¯è¨éæ©å¨ + targetPickerView.translatesAutoresizingMaskIntoConstraints = false + targetPickerView.dataSource = self + targetPickerView.delegate = self + targetPickerView.tag = 1 + addSubview(targetPickerView) + } + + private func setupSwapButton() { + swapButton.translatesAutoresizingMaskIntoConstraints = false + swapButton.setImage(UIImage(systemName: "arrow.left.arrow.right"), for: .normal) + swapButton.tintColor = .systemBlue + swapButton.backgroundColor = .systemGray6 + swapButton.layer.cornerRadius = 20 + swapButton.addTarget(self, action: #selector(swapButtonTapped), for: .touchUpInside) + addSubview(swapButton) + } + + private func setupConstraints() { + sourceLabel.snp.makeConstraints { make in + make.top.equalToSuperview().offset(8) + make.leading.equalToSuperview().offset(20) + make.trailing.equalTo(swapButton.snp.leading).offset(-20) + make.height.equalTo(20) + } + + targetLabel.snp.makeConstraints { make in + make.top.equalToSuperview().offset(8) + make.trailing.equalToSuperview().offset(-20) + make.leading.equalTo(swapButton.snp.trailing).offset(20) + make.height.equalTo(20) + } + + sourcePickerView.snp.makeConstraints { make in + make.top.equalTo(sourceLabel.snp.bottom).offset(8) + make.leading.equalToSuperview().offset(20) + make.trailing.equalTo(swapButton.snp.leading).offset(-20) + make.bottom.equalToSuperview().offset(-8) + } + + targetPickerView.snp.makeConstraints { make in + make.top.equalTo(targetLabel.snp.bottom).offset(8) + make.trailing.equalToSuperview().offset(-20) + make.leading.equalTo(swapButton.snp.trailing).offset(20) + make.bottom.equalToSuperview().offset(-8) + } + + swapButton.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.centerY.equalTo(sourcePickerView.snp.centerY) + make.height.equalTo(40) + make.width.equalTo(40) + } + } + + private func setupDefaults() { + // 设置é»è®¤éæ© + if let chineseIndex = languages.firstIndex(where: { $0.code == "zh" }) { + sourcePickerView.selectRow(chineseIndex, inComponent: 0, animated: false) + selectedSourceLanguage = languages[chineseIndex] + } + + if let englishIndex = languages.firstIndex(where: { $0.code == "en" }) { + targetPickerView.selectRow(englishIndex, inComponent: 0, animated: false) + selectedTargetLanguage = languages[englishIndex] + } + } + + // MARK: - å ¬å ±æ¹æ³ + func setLanguages(source: LanguageModel, target: LanguageModel) { + if let sourceIndex = languages.firstIndex(where: { $0.code == source.code }) { + sourcePickerView.selectRow(sourceIndex, inComponent: 0, animated: true) + selectedSourceLanguage = source + } + + if let targetIndex = languages.firstIndex(where: { $0.code == target.code }) { + targetPickerView.selectRow(targetIndex, inComponent: 0, animated: true) + selectedTargetLanguage = target + } + } + + func getSelectedLanguages() -> (source: LanguageModel?, target: LanguageModel?) { + return (selectedSourceLanguage, selectedTargetLanguage) + } + + // MARK: - ç§ææ¹æ³ + private func updateSelection() { + // å¯ä»¥å¨è¿éæ·»å éæ©æ´æ°åçé»è¾ + print("æºè¯è¨: \(selectedSourceLanguage?.nativeName ?? "æ ")") + print("ç®æ è¯è¨: \(selectedTargetLanguage?.nativeName ?? "æ ")") + + // éç¥å§æ + if let source = selectedSourceLanguage, let target = selectedTargetLanguage { + delegate?.languagePickerView(self, didSelectSourceLanguage: source, targetLanguage: target) + } + } + + @objc private func swapButtonTapped() { + guard let source = selectedSourceLanguage, + let target = selectedTargetLanguage else { return } + + // 交æ¢è¯è¨ + setLanguages(source: target, target: source) + + // éç¥å§æ + delegate?.languagePickerViewDidTapSwap(self) + } +} + +// MARK: - UIPickerView DataSource & Delegate +@available(iOS 15.0, *) +extension LanguagePickerView1: UIPickerViewDataSource, UIPickerViewDelegate { + + func numberOfComponents(in pickerView: UIPickerView) -> Int { + return 1 + } + + func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + return languages.count + } + + func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + let language = languages[row] + return "\(language.nativeName) (\(language.name))" + } + + func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + let selectedLanguage = languages[row] + + if pickerView.tag == 0 { + selectedSourceLanguage = selectedLanguage + } else { + selectedTargetLanguage = selectedLanguage + } + + // 鲿¢éæ©ç¸åçè¯è¨ + validateSelection() + } + + func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { + return 44.0 + } + + func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { + let language = languages[row] + + let containerView = UIView() + containerView.backgroundColor = .clear + + let stackView = UIStackView() + stackView.axis = .vertical + stackView.alignment = .center + stackView.spacing = 2 + stackView.translatesAutoresizingMaskIntoConstraints = false + + let nativeLabel = UILabel() + nativeLabel.text = language.nativeName + nativeLabel.font = UIFont.systemFont(ofSize: 16, weight: .medium) + nativeLabel.textAlignment = .center + nativeLabel.textColor = .label + + let nameLabel = UILabel() + nameLabel.text = language.name + nameLabel.font = UIFont.systemFont(ofSize: 12, weight: .regular) + nameLabel.textColor = .secondaryLabel + nameLabel.textAlignment = .center + + stackView.addArrangedSubview(nativeLabel) + stackView.addArrangedSubview(nameLabel) + containerView.addSubview(stackView) + + NSLayoutConstraint.activate([ + stackView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor), + stackView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor), + stackView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 8), + stackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8) + ]) + + return containerView + } + + private func validateSelection() { + guard let source = selectedSourceLanguage, + let target = selectedTargetLanguage, + source.code == target.code else { return } + + // 妿鿩äºç¸åçè¯è¨ï¼èªå¨è°æ´ç®æ è¯è¨ + if let alternativeIndex = languages.firstIndex(where: { $0.code != source.code }) { + targetPickerView.selectRow(alternativeIndex, inComponent: 0, animated: true) + selectedTargetLanguage = languages[alternativeIndex] + } + } +} diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/PiPManager.swift b/ShenYin/ShenYin/UI/Home/SubTitle/PiPManager.swift new file mode 100644 index 0000000..99aabad --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/SubTitle/PiPManager.swift @@ -0,0 +1,175 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/3/5. +// + +import AVKit +import UIKit + +@available(iOS 15.0, *) +class PiPManager: NSObject, ObservableObject { + + var pipController: AVPictureInPictureController? + + var contentVC: DesktopLyricsContentVC! + + var messageList : [(String,UInt)] = [] + + var translateList : [(String,UInt)] = [] + + func setupPictureInPicture(sourceView : UIView) { + + contentVC = DesktopLyricsContentVC() + contentVC.preferredContentSize = CGSize(width: UIScreen.main.bounds.width, height: 240) // ç»ä¸ç»çªå£å¤§å° + + // è®¾ç½®ææ¾å¨ + pipController = AVPictureInPictureController(contentSource: AVPictureInPictureController.ContentSource( + activeVideoCallSourceView: sourceView, + contentViewController: contentVC + )) + pipController?.delegate = self + } + + + func startPictureInPicture() { + // 1. æ£æ¥åºæ¯ç¶æ + if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene { + print("åºæ¯æ¿æ´»ç¶æ: \(scene.activationState.rawValue)") + print("åºæ¯ç¶ææè¿°: \(describeActivationState(scene.activationState))") + } else { + print("â æ æ³è·ååºæ¯") + } + + pipController?.startPictureInPicture() + } + + private func describeActivationState(_ state: UIScene.ActivationState) -> String { + switch state { + case .unattached: return "unattached - åºæ¯æªè¿æ¥å°ä¼è¯" + case .foregroundInactive: return "foregroundInactive - 忝ä½ä¸æ´»è·" + case .foregroundActive: return "foregroundActive - 忝䏿´»è· â " + case .background: return "background - åå°" + @unknown default: return "unknown" + } + } + + func stopPictureInPicture() { + pipController?.stopPictureInPicture() + } + + func updateRaw(_ text: String,_ index: UInt) { + let msg = messageList.firstIndex (where: { (str,idx) in + return idx == index; + }) + if (msg == nil) { + messageList.append((text,index)) + } else { + messageList[msg!] = (text,index) + } + + let res = messageList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + contentVC?.updateRaw(res) + } + + func updateTrans(_ text: String,_ index: UInt) { + let msg = translateList.firstIndex (where: { (str,idx) in + return idx == index; + }) + if (msg == nil) { + translateList.append((text,index)) + } else { + translateList[msg!] = (text,index) + } + + let res = translateList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + contentVC?.updateTrans(res) + } +} + +@available(iOS 15.0, *) +extension PiPManager: AVPictureInPictureControllerDelegate { + func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + print("ç»ä¸ç»å³å°å¼å§") + } + + func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + print("ç»ä¸ç»å·²å¼å§") + // å¼å§ææ¾éæè§é¢ + //player?.play() + } + + func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) { + print("ç»ä¸ç»å¯å¨å¤±è´¥: \(error)") + } + + + func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + print("ç»ä¸ç»å·²ç»æ") +// messageList.removeAll() +// translateList.removeAll() + } +} +// ç»ä¸ç»å 容è§å¾æ§å¶å¨ +@available(iOS 15.0, *) +class DesktopLyricsContentVC : AVPictureInPictureVideoCallViewController { + let setView = PipSetView() + var textView = UITextView() + var translateTextView = UITextView() + + override func viewDidLoad() { + super.viewDidLoad() + setupViews() + setupLayout() + } + func setupViews(){ + view.addSubviews([setView,textView,translateTextView]) + + textView.do { v in + v.textColor = UIColor.white + v.text = "åæ" + v.font = UIFont.boldSystemFont(ofSize: 14) + v.backgroundColor = UIColor.gray + } + + translateTextView.do { v in + v.textColor = UIColor.orange + v.text = "ç¿»è¯" + v.font = UIFont.boldSystemFont(ofSize: 14) + v.backgroundColor = UIColor.gray + } + } + func setupLayout(){ + setView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.height.equalTo(34) + make.top.equalToSuperview() + } + textView.snp.makeConstraints { make in + make.top.equalToSuperview().inset(40) + make.leading.trailing.equalToSuperview() + make.bottom.equalTo(view.snp.centerY) + } + translateTextView.snp.makeConstraints { make in + make.leading.trailing.equalToSuperview() + make.top.equalTo(textView.snp.bottom) + make.height.equalTo(textView.snp.height) + } + + } + + func updateRaw(_ text: String) { + textView.text = text + textView.scrollRangeToVisible(NSRange(location: textView.text.count, length: 0)) + } + + func updateTrans(_ text : String) { + translateTextView.text = text + translateTextView.scrollRangeToVisible(NSRange(location: translateTextView.text.count, length: 0)) + } +} diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/PipPicker.swift b/ShenYin/ShenYin/UI/Home/SubTitle/PipPicker.swift new file mode 100644 index 0000000..872364a --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/SubTitle/PipPicker.swift @@ -0,0 +1,164 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/3/6. +// + +import UIKit +class PipPicker: BaseView { + private let btn = UIButton(type: .custom) + private let tableView = UITableView(frame: .zero, style: .plain) + var list:[String] = [] + + var selectedIndex = 0{ + didSet{ + tableView.reloadData() + } + } + var didSelected: ((Int,String)->Void)? + private var title:String = "" + + init(list:[String]) { + super.init(frame: .zero) + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func setupViews() { + addSubviews([btn,tableView]) + + btn.do { v in + v.setImage(UIImage(named: "close_icon"), for: .normal) + v.addTarget(self, action: #selector(closeAction), for: .touchUpInside) + } + tableView.do { v in + v.backgroundColor = .color(.cellBg) + v.delegate = self + v.dataSource = self + v.rowHeight = 52 + v.separatorStyle = .none + v.register(CustomButton.SelectCell.self, forCellReuseIdentifier: CustomButton.SelectCell.reuseID) + } + } + override func setupLayout() { + super.setupLayout() + btn.snp.makeConstraints { (m) in + m.bottom.equalToSuperview() + m.right.equalToSuperview() + m.size.equalTo(60) + } + tableView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().inset(10) + } + } + @objc func closeAction(){ + + } +} +extension PipPicker:UITableViewDelegate,UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return list.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = CustomButton.SelectCell.cell(withTableView: tableView, indexPath: indexPath) + cell.titleLabel.text = list[indexPath.row] + cell.selectedIcon.isHidden = selectedIndex != indexPath.row + return cell + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + selectedIndex = indexPath.row + tableView.reloadData() + didSelected?(selectedIndex,list[selectedIndex]) + } +} + +fileprivate extension CustomButton { + class SelectCell: BaseTableViewCell { + let bgView = UIView() + let titleLabel = UILabel() + let selectedIcon = UIImageView() + let line = UIView() + + var fontType:FontType = .min{ + didSet{ + titleLabel.text = fontType.title + } + } + + var shareType:ShareType = .document{ + didSet{ + titleLabel.text = shareType.title + } + } + + var enable:Bool = true{ + didSet{ + if enable{ + titleLabel.textColor = .color(.first) + } + else{ + titleLabel.textColor = .color(.secondary) + } + } + } + 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.addSubview(bgView) + bgView.addSubviews([titleLabel,selectedIcon,line]) + + backgroundColor = .color(.bgColor) + contentView.backgroundColor = .clear + selectionStyle = .none + bgView.do { v in + v.backgroundColor = .color(.cellBg) + } + titleLabel.do { (v) in + v.textColor = .color(.first) + v.font = .bold(14) + v.textAlignment = .left + } + + selectedIcon.image = UIImage(named: "cell_selected") + line.do { v in + v.backgroundColor = .color(.separateLine) + } + } + + override func setupLayout() { + bgView.snp.makeConstraints { (m) in + m.left.right.equalToSuperview() + m.top.equalToSuperview() + m.bottom.equalToSuperview() + } + selectedIcon.snp.makeConstraints { (m) in + m.width.equalTo(21) + m.height.equalTo(16) + m.right.equalToSuperview().inset(21) + m.centerY.equalToSuperview() + } + titleLabel.snp.makeConstraints { (m) in + m.centerY.equalToSuperview() + m.left.equalToSuperview().offset(24) + m.height.equalToSuperview() + } + line.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.bottom.equalToSuperview() + make.height.equalTo(0.4) + } + } + } + +} + diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/PipSetView.swift b/ShenYin/ShenYin/UI/Home/SubTitle/PipSetView.swift new file mode 100644 index 0000000..8d66cd3 --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/SubTitle/PipSetView.swift @@ -0,0 +1,77 @@ +// +// PipSetView.swift +// ShenYin +// +// Created by liangshi on 2026/3/6. +// + +class PipSetView: BaseView { + let setBtn = UIButton(type: .custom) + let closeBtn = UIButton(type: .custom) + let languageItem = UIButton(type: .custom) + let dispalyItem = UIButton(type: .custom) + + override func setupViews() { + addSubviews([setBtn,languageItem,dispalyItem,closeBtn]) + + setBtn.do { v in + v.setImage(UIImage(named: "pip_set"), for: .normal) + v.addTarget(self, action: #selector(setBtnAction), for: .touchUpInside) + } + closeBtn.do { v in + v.setImage(UIImage(named: "pip_close"), for: .normal) + v.addTarget(self, action: #selector(closeAction), for: .touchUpInside) + } + languageItem.do { v in + v.setTitle("", for: .normal) + v.addTarget(self, action: #selector(languageAction), for: .touchUpInside) + v.yc.setCornerRadius(12) + v.backgroundColor = .red + } + dispalyItem.do { v in + v.setTitle("", for: .normal) + v.addTarget(self, action: #selector(displayAction), for: .touchUpInside) + v.yc.setCornerRadius(12) + v.backgroundColor = .red + } + } + override func setupLayout() { + setBtn.snp.makeConstraints { make in + make.size.equalTo(24) + make.left.equalToSuperview().inset(16) + make.top.equalToSuperview().inset(10) + } + closeBtn.snp.makeConstraints { make in + make.size.equalTo(24) + make.right.equalToSuperview().inset(16) + make.centerY.equalTo(setBtn) + } + languageItem.snp.makeConstraints { make in + make.height.equalTo(24) + make.left.equalTo(setBtn.snp.right).offset(4) + make.centerY.equalTo(setBtn) + make.width.equalTo(57) + } + dispalyItem.snp.makeConstraints { make in + make.height.equalTo(24) + make.left.equalTo(languageItem.snp.right).offset(4) + make.centerY.equalTo(setBtn) + make.width.equalTo(57) + } + } + @objc func setBtnAction(){ + Logger.debug("setBtnAction") + } + @objc func languageAction(){ + Logger.debug("languageAction") + } + @objc func displayAction(){ + Logger.debug("displayAction") + let view = PipPicker(list: ["åªæ¾ç¤ºåæ","åªæ¾è¯æ","åæ & è¯æ"]) + superview?.addSubview(view) + } + @objc func closeAction(){ + Logger.debug("closeAction") + + } +} diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleVC.swift b/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleVC.swift new file mode 100644 index 0000000..915eed2 --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleVC.swift @@ -0,0 +1,186 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/3/5. +// + + +import UIKit +import AVKit +import JoyGemAI +import SVProgressHUD +@available(iOS 15.0, *) +class SubTitleVC: UIViewController { + + lazy var languagePickerView : LanguagePickerView1 = { + let pickerView = LanguagePickerView1() + pickerView.delegate = self + return pickerView + }() + + var startButton : UIButton! + + var stopButton : UIButton! + let pipManager = PiPManager() + + + //ç»ä¸ç»è½½ä½ + internal var customView : UIView = UIView() + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = UIColor.white + title = "åå¹ç¿»è¯" + + self.navigationController?.navigationBar.tintColor = UIColor.black + do { + try AVAudioSession.sharedInstance().setCategory(.playback) + try AVAudioSession.sharedInstance().setActive(true) + } catch { + print(error) + } + + view.addSubview(languagePickerView) + languagePickerView.snp.makeConstraints { make in + make.top.equalTo(view.safeAreaLayoutGuide.snp.top) + make.leading.trailing.equalToSuperview() + make.height.equalTo(100) + } + + // æè 设置ç¹å®è¯è¨ + languagePickerView.setLanguages( + source: LanguageModel.chinese, + target: LanguageModel.english + ) + + view.addSubview(customView) + customView.snp.makeConstraints { make in + make.top.equalTo(languagePickerView.snp.bottom).offset(20) + make.height.equalTo(40) + make.centerX.equalToSuperview() + make.width.equalTo(200) + } + + startButton = UIButton(type: .system) + startButton.setTitle("å¼å§", for: .normal) + startButton.backgroundColor = UIColor.systemGray6 + startButton.setTitleColor(UIColor.darkText, for: .normal) + startButton.addTarget(self, action: #selector(startRecord), for: .touchUpInside) + + stopButton = UIButton(type: .system) + stopButton.setTitle("ç»æ", for: .normal) + stopButton.backgroundColor = UIColor.systemGray6 + stopButton.setTitleColor(UIColor.darkText, for: .normal) + stopButton.addTarget(self, action: #selector(endRecord), for: .touchUpInside) + + let stackView = UIStackView(arrangedSubviews: [startButton, stopButton]) + stackView.axis = .horizontal + stackView.distribution = .fillEqually + stackView.alignment = .center + stackView.spacing = 16 + + view.addSubview(stackView) + stackView.snp.makeConstraints { make in + make.height.equalTo(60) + make.leading.equalToSuperview().offset(20) + make.trailing.equalToSuperview().offset(-20) + make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom).offset(-20) + } + + // Do any additional setup after loading the view. + JoyGemSubtitleManager.shared().delegate = self + + setupPiPManager() + } + + private func setupPiPManager() { + // åå§åç»ä¸ç»ç®¡çå¨ + pipManager.setupPictureInPicture(sourceView: self.customView) + + // çå¬ç»ä¸ç»ç¶æåå + NotificationCenter.default.addObserver( + self, + selector: #selector(pipStateChanged(_:)), + name: NSNotification.Name("PiPStateChanged"), + object: nil + ) + } + + @objc func startRecord() { + JoyGemSubtitleManager.shared().start() + + guard AVPictureInPictureController.isPictureInPictureSupported() else { + print("设å¤ä¸æ¯æç»ä¸ç»") + return + } + + pipManager.startPictureInPicture() + } + + @objc func endRecord() { + JoyGemSubtitleManager.shared().stop() + + pipManager.stopPictureInPicture() + } + + @objc private func pipStateChanged(_ notification: Notification) { + if let userInfo = notification.userInfo, + let isActive = userInfo["isActive"] as? Bool { + print("ç»ä¸ç»ç¶æ: \(isActive ? "æ´»è·" : "éæ´»è·")") + } + } +} + +// å®ç°å§ææ¹æ³ +@available(iOS 15.0, *) +extension SubTitleVC: LanguagePickerView1Delegate { + + func languagePickerView(_ pickerView: LanguagePickerView1, + didSelectSourceLanguage source: LanguageModel, + targetLanguage target: LanguageModel) { + print("è¯è¨éæ©æ´æ°: \(source.nativeName) -> \(target.nativeName)") + // æ´æ°ä½ çç¿»è¯é»è¾ç + JoyGemSubtitleManager.shared().originalLanguage = source.code + JoyGemSubtitleManager.shared().targetLanguage = target.code + } + + func languagePickerViewDidTapSwap(_ pickerView: LanguagePickerView1) { + print("交æ¢äºè¯è¨") + } +} + +@available(iOS 15.0, *) +extension SubTitleVC : JoyGemSubtitleManagerDelegate { + + func subtitleManager(_ manager: JoyGemSubtitleManager, didUpdateRaw model: JoyGemAsrModel) { + debugPrint("text: \(model.text),index: \(model.index)") + pipManager.updateRaw(model.text,model.index) + } + + func subtitleManager(_ manager: JoyGemSubtitleManager, didUpdateTranslate model: JoyGemAsrModel) { + debugPrint("translate text: \(model.text),index: \(model.index)") + pipManager.updateTrans(model.text,model.index) + } + + func subtitleManagerStopped(_ manager: JoyGemSubtitleManager) { + + } + + func subtitleManager(_ manager: JoyGemSubtitleManager, stateChange code: JoyGemStateCode) { + + } + + func subtitleManager(_ manager: JoyGemSubtitleManager, onError code: JoyGemErrorCode) { + + } + + func subTitleManager(_ manager: JoyGemSubtitleManager, stateChange code: JoyGemStateCode) { + debugPrint("code: \(code)") + } + + func subTitleManager(_ manager: JoyGemSubtitleManager, onError code: JoyGemErrorCode) { + + } + +} -- 2.17.1