From 6979a693fc78609d478f6cddac882b65c12cfc72 Mon Sep 17 00:00:00 2001 From: liangzhenying <867174124@qq.com> Date: Fri, 3 Apr 2026 17:51:47 +0800 Subject: [PATCH] =?utf8?q?=E5=AD=97=E5=B9=95=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- ShenYin/ShenYin.xcodeproj/project.pbxproj | 4 +- .../DeepChords/Channel/UI/JoySubTitleVC.swift | 382 ++++++++--------- .../PLAYBOY/Channel/UI/PbSubTitleVC.swift | 41 +- .../UI/Home/SubTitle/PipContentVC.swift | 390 +++++++++--------- .../ShenYin/UI/Home/SubTitle/PipManager.swift | 219 ++++++++++ .../UI/Home/SubTitle/SubTitleSetVC.swift | 4 + ShenYin/ShenYin/UI/Home/SubTitle/shu1.mp4 | Bin 0 -> 83211 bytes ShenYin/ShenYin/UI/Home/SubTitle/shu2.mp4 | Bin 0 -> 86934 bytes 8 files changed, 639 insertions(+), 401 deletions(-) create mode 100644 ShenYin/ShenYin/UI/Home/SubTitle/PipManager.swift create mode 100644 ShenYin/ShenYin/UI/Home/SubTitle/shu1.mp4 create mode 100644 ShenYin/ShenYin/UI/Home/SubTitle/shu2.mp4 diff --git a/ShenYin/ShenYin.xcodeproj/project.pbxproj b/ShenYin/ShenYin.xcodeproj/project.pbxproj index d7ed4ff..b5e5a20 100644 --- a/ShenYin/ShenYin.xcodeproj/project.pbxproj +++ b/ShenYin/ShenYin.xcodeproj/project.pbxproj @@ -1103,7 +1103,7 @@ CODE_SIGN_ENTITLEMENTS = ShenYin/Channel/DeepChords/DeepChords.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 202604011815; + CURRENT_PROJECT_VERSION = 202604031815; DEVELOPMENT_TEAM = MAC46RQ4KH; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -1169,7 +1169,7 @@ CODE_SIGN_ENTITLEMENTS = ShenYin/Channel/DeepChords/DeepChords.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 202604011815; + CURRENT_PROJECT_VERSION = 202604031815; DEVELOPMENT_TEAM = MAC46RQ4KH; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; diff --git a/ShenYin/ShenYin/Channel/DeepChords/Channel/UI/JoySubTitleVC.swift b/ShenYin/ShenYin/Channel/DeepChords/Channel/UI/JoySubTitleVC.swift index 4e33021..f5a698a 100644 --- a/ShenYin/ShenYin/Channel/DeepChords/Channel/UI/JoySubTitleVC.swift +++ b/ShenYin/ShenYin/Channel/DeepChords/Channel/UI/JoySubTitleVC.swift @@ -1,194 +1,196 @@ +////// +////// Untitled.swift +////// ShenYin +////// +////// Created by liangshi on 2026/3/5. +////// //// -//// Untitled.swift -//// ShenYin -//// -//// Created by liangshi on 2026/3/5. //// +//import UIKit +//import AVKit +//import JoyGemAI +//@available(iOS 15.0, *) +//class JoySubTitleVC: BaseVC { +// let languageItem = LanguageSelectItem() +// let toolBar = RecordBar() +// let pipManager = PipManager() +// var messageList : [(String,UInt)] = [] +// var translateList : [(String,UInt)] = [] +// +// override func viewDidLoad() { +// super.viewDidLoad() +// } +// override func viewDidAppear(_ animated: Bool) { +// super.viewDidAppear(animated) +// +// } +// override func viewWillAppear(_ animated: Bool) { +// super.viewWillAppear(animated) +// let cache = SubTitleCache.shared.get() +// languageItem.leftLabel.language = cache.lan_form +// languageItem.rightLabel.language = cache.lan_to +// } +// override func setupViews() { +// view.backgroundColor = UIColor.white +// navBar.do { v in +// v.backgroundColor = .white +// v.addSubview(languageItem) +// v.rightView.isHidden = false +// v.rightView.setImage(UIImage(named: "set_right"), for: .normal) +// } +// +// view.addSubviews([toolBar]) +// +// toolBar.do { v in +// v.delegate = self +// v.leftItem.isHidden = true +// v.rightItem.isHidden = true +// } +// languageItem.do { v in +// v.languageChange = { (from,to) in +// let cache = SubTitleCache.shared.get() +// cache.lan_form = from +// cache.lan_to = to +// SubTitleCache.shared.save(cache) +// } +// } +// pipManager.setup(superView: self.navigationController!.view) +// } +// override func setupLayout() { +// languageItem.snp.makeConstraints { make in +// make.left.right.equalToSuperview().inset(80) +// make.centerX.equalToSuperview() +// make.bottom.equalToSuperview().offset(5) +// } +// toolBar.snp.makeConstraints { make in +// make.width.centerX.equalToSuperview() +// make.height.equalTo(120) +// make.bottom.equalToSuperview() +// } +// } +// +// override func setupData() { +// do { +// try AVAudioSession.sharedInstance().setCategory(.playback) +// try AVAudioSession.sharedInstance().setActive(true) +// } catch { +// print(error) +// } +// JoyGemSubtitleManager.shared().delegate = self +// +// NotificationCenter.default.addObserver( +// self, +// selector: #selector(appDidEnterBackground(_:)), +// name: UIApplication.willResignActiveNotification, +// object: nil +// ) +// +// NotificationCenter.default.addObserver( +// self, +// selector: #selector(appWillEnterForeground(_:)), +// name: UIApplication.didBecomeActiveNotification, +// object: nil +// ) +// +// } +// override func navRightViewTapped() { +// let vc = SubTitleSetVC() +// App.shared.goToPage(vc) +// } +// +// +// @objc private func appDidEnterBackground(_ notification: Notification) { +//// pipManager.startPictureInPicture() +// } +// +// @objc private func appWillEnterForeground(_ notification: Notification) { +//// pipManager.stopPictureInPicture() +// } +//} +// +// // +//@available(iOS 15.0, *) +//extension JoySubTitleVC:RecordBarDelegate{ +// func recordBarLanguageChange(_ bar: RecordBar) { +// +// } +// func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) { +// if state == .start { +// JoyGemSubtitleManager.shared().originalLanguage = languageItem.leftLabel.language.value +// JoyGemSubtitleManager.shared().targetLanguage = languageItem.rightLabel.language.value +// JoyGemSubtitleManager.shared().start() +// pipManager.start() +// } +// else if state == .stop{ +// JoyGemSubtitleManager.shared().stop() +// pipManager.stop() +// } +// } +//} // -import UIKit -import AVKit -import JoyGemAI -@available(iOS 15.0, *) -class JoySubTitleVC: BaseVC { - let languageItem = LanguageSelectItem() - let toolBar = RecordBar() - let pipManager = PipManager() - var messageList : [(String,UInt)] = [] - var translateList : [(String,UInt)] = [] - - override func viewDidLoad() { - super.viewDidLoad() - } - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - } - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - let cache = SubTitleCache.shared.get() - languageItem.leftLabel.language = cache.lan_form - languageItem.rightLabel.language = cache.lan_to - } - override func setupViews() { - view.backgroundColor = UIColor.white - navBar.do { v in - v.backgroundColor = .white - v.addSubview(languageItem) - v.rightView.isHidden = false - v.rightView.setImage(UIImage(named: "set_right"), for: .normal) - } - - view.addSubviews([toolBar]) - - toolBar.do { v in - v.delegate = self - v.leftItem.isHidden = true - v.rightItem.isHidden = true - } - languageItem.do { v in - v.languageChange = { (from,to) in - let cache = SubTitleCache.shared.get() - cache.lan_form = from - cache.lan_to = to - SubTitleCache.shared.save(cache) - } - } - pipManager.setupPictureInPicture(soureceView: self.navigationController!.view) - } - override func setupLayout() { - languageItem.snp.makeConstraints { make in - make.left.right.equalToSuperview().inset(80) - make.centerX.equalToSuperview() - make.bottom.equalToSuperview().offset(5) - } - toolBar.snp.makeConstraints { make in - make.width.centerX.equalToSuperview() - make.height.equalTo(120) - make.bottom.equalToSuperview() - } - } - - override func setupData() { - do { - try AVAudioSession.sharedInstance().setCategory(.playback) - try AVAudioSession.sharedInstance().setActive(true) - } catch { - print(error) - } - JoyGemSubtitleManager.shared().delegate = self - - NotificationCenter.default.addObserver( - self, - selector: #selector(appDidEnterBackground(_:)), - name: UIApplication.willResignActiveNotification, - object: nil - ) - - NotificationCenter.default.addObserver( - self, - selector: #selector(appWillEnterForeground(_:)), - name: UIApplication.didBecomeActiveNotification, - object: nil - ) - - } - override func navRightViewTapped() { - let vc = SubTitleSetVC() - App.shared.goToPage(vc) - } - - - @objc private func appDidEnterBackground(_ notification: Notification) { - pipManager.startPictureInPicture() - } - - @objc private func appWillEnterForeground(_ notification: Notification) { - pipManager.stopPictureInPicture() - } -} - - - -@available(iOS 15.0, *) -extension JoySubTitleVC:RecordBarDelegate{ - func recordBarLanguageChange(_ bar: RecordBar) { - - } - func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) { - if state == .start { - JoyGemSubtitleManager.shared().originalLanguage = languageItem.leftLabel.language.value - JoyGemSubtitleManager.shared().targetLanguage = languageItem.rightLabel.language.value - JoyGemSubtitleManager.shared().start() - } - else if state == .stop{ - JoyGemSubtitleManager.shared().stop() - } - } -} - -@available(iOS 15.0, *) -extension JoySubTitleVC : JoyGemSubtitleManagerDelegate { - - func subtitleManager(_ manager: JoyGemSubtitleManager, didUpdateRaw model: JoyGemAsrModel) { - debugPrint("text: \(model.text),index: \(model.index)") - updateRaw(model.text,model.index) - } - - func subtitleManager(_ manager: JoyGemSubtitleManager, didUpdateTranslate model: JoyGemAsrModel) { - debugPrint("translate text: \(model.text),index: \(model.index)") - 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) { - - } - - 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") - pipManager.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") - pipManager.contentVC.updateTrans(res) - } -} +//@available(iOS 15.0, *) +//extension JoySubTitleVC : JoyGemSubtitleManagerDelegate { +// +// func subtitleManager(_ manager: JoyGemSubtitleManager, didUpdateRaw model: JoyGemAsrModel) { +// debugPrint("text: \(model.text),index: \(model.index)") +// updateRaw(model.text,model.index) +// } +// +// func subtitleManager(_ manager: JoyGemSubtitleManager, didUpdateTranslate model: JoyGemAsrModel) { +// debugPrint("translate text: \(model.text),index: \(model.index)") +// 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) { +// +// } +// +// 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") +//// pipManager.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") +//// pipManager.contentVC.updateTrans(res) +// } +//} diff --git a/ShenYin/ShenYin/Channel/PLAYBOY/Channel/UI/PbSubTitleVC.swift b/ShenYin/ShenYin/Channel/PLAYBOY/Channel/UI/PbSubTitleVC.swift index 92640b8..ea4c318 100644 --- a/ShenYin/ShenYin/Channel/PLAYBOY/Channel/UI/PbSubTitleVC.swift +++ b/ShenYin/ShenYin/Channel/PLAYBOY/Channel/UI/PbSubTitleVC.swift @@ -42,6 +42,7 @@ class PbSubTitleVC: BaseVC { if toolBar.recordBtn.isSelected { DcStereo.shared.recordStop() } + pipManager.dissmiss() super.navleftViewTapped() } @@ -78,14 +79,10 @@ class PbSubTitleVC: BaseVC { v.separatorStyle = .none v.register(SubTitleCell.self, forCellReuseIdentifier: SubTitleCell.reuseID) } -// let soureView = UIView() -// soureView.backgroundColor = .red -// view.addSubviews([soureView]) -// soureView.snp.makeConstraints { make in -// make.size.equalTo(10) -// make.center.equalToSuperview() -// } - pipManager.setupPictureInPicture(soureceView: self.navigationController!.view) + pipManager.do { v in + v.delegate = self + v.reset(supperView: self.navigationController!.view) + } } override func setupLayout() { languageItem.snp.makeConstraints { make in @@ -128,8 +125,8 @@ class PbSubTitleVC: BaseVC { } else{ - pipManager.contentVC.updateRaw("蓝牙已断开".l10n) - pipManager.contentVC.updateTrans("蓝牙已断开".l10n) + pipManager.updateRaw("蓝牙已断开".l10n) + pipManager.updateTrans("蓝牙已断开".l10n) if toolBar.recordBtn.isSelected { toolBar.recordBtnAction() } @@ -148,24 +145,32 @@ class PbSubTitleVC: BaseVC { guard let self = self else{return} restart() } + vc.showTypeChanged = {[weak self] in + guard let self = self else{return} + pipManager.reset(supperView: self.navigationController!.view) + } + vc.fontChanged = {[weak self] in + guard let self = self else{return} + pipManager.reset(supperView: self.navigationController!.view) + } App.shared.goToPage(vc) } @objc private func appDidEnterBackground(_ notification: Notification) { if sdk.recording { - pipManager.startPictureInPicture() +// pipManager.startPictureInPicture() } } @objc private func appWillEnterForeground(_ notification: Notification) { - pipManager.stopPictureInPicture() +// pipManager.stopPictureInPicture() } func setupSdk(){ sdk.recordNeedReloadData = {[weak self] in guard let self = self else{return} if let model = sdk.dataList.last{ - pipManager.contentVC.updateRaw(model.send) - pipManager.contentVC.updateTrans(model.response) + pipManager.updateRaw(model.send) + pipManager.updateTrans(model.response) tableView.reloadData() if sdk.dataList.count > 0 { tableView.scrollToRow(at: IndexPath(row: sdk.dataList.count - 1, section: 0), at: .bottom, animated: true) @@ -183,6 +188,12 @@ class PbSubTitleVC: BaseVC { } } +@available(iOS 15.0, *) +extension PbSubTitleVC:PipManagerDelegate{ + func pipStopAction(_ pip: PipManager) { + toolBar.recordBtnAction() + } +} @available(iOS 15.0, *) extension PbSubTitleVC:RecordBarDelegate{ @@ -193,9 +204,11 @@ extension PbSubTitleVC:RecordBarDelegate{ func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) { if state == .start { sdk.startEarRecord(from: languageItem.leftLabel.language, to: languageItem.rightLabel.language) + pipManager.start() } else if state == .stop{ sdk.stopEarRecord() + pipManager.stop() } } } diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/PipContentVC.swift b/ShenYin/ShenYin/UI/Home/SubTitle/PipContentVC.swift index 410fedc..f5313ed 100644 --- a/ShenYin/ShenYin/UI/Home/SubTitle/PipContentVC.swift +++ b/ShenYin/ShenYin/UI/Home/SubTitle/PipContentVC.swift @@ -1,198 +1,198 @@ +//// +//// Untitled.swift +//// ShenYin +//// +//// Created by liangshi on 2026/3/5. +//// // -// Untitled.swift -// ShenYin +//import AVKit +//import UIKit // -// Created by liangshi on 2026/3/5. +//@available(iOS 15.0, *) +//class PipManager:NSObject,AVPictureInPictureControllerDelegate{ +// var pipController: AVPictureInPictureController? +// var contentVC: PipContentVC! +// +// +// func setupPictureInPicture(soureceView:UIView) { +// guard AVPictureInPictureController.isPictureInPictureSupported() else { +// print("❌ 设备不支持画中画") +// return +// } +// +// contentVC = PipContentVC() +// let cache = SubTitleCache.shared.get() +// if cache.showType == .all { +// contentVC.preferredContentSize = CGSizeMake(kScreenWid, 100) +// } +// else{ +// contentVC.preferredContentSize = CGSizeMake(kScreenWid, 50) +// } +// let contentSource = AVPictureInPictureController.ContentSource( +// activeVideoCallSourceView:soureceView , +// contentViewController: contentVC +// ) +// +// pipController = AVPictureInPictureController(contentSource: contentSource) +// pipController?.delegate = self +//// pipController?.canStartPictureInPictureAutomaticallyFromInline = false +// } +// +// @objc func startPictureInPicture(){ +// guard let pipController = pipController else { +// Logger.debug("❌ pipController 未初始化,无法启动画中画") +// return +// } +// +// if pipController.isPictureInPicturePossible { +// Logger.debug("✅ 画中画可以启动,准备开始...") +// pipController.startPictureInPicture() +// contentVC?.reset() +// } else { +// Logger.debug("❌ 画中画当前无法启动") +// } +// } +// @objc func stopPictureInPicture() { +// guard let pipController = pipController else { +// Logger.debug("❌ pipController 未初始化,无法停止画中画") +// return +// } +// +// if pipController.isPictureInPictureActive { +// Logger.debug("✅ 画中画正在显示,准备停止...") +// pipController.stopPictureInPicture() +// Logger.debug("✅ 画中画已停止") +// } else { +// Logger.debug("⚠️ 画中画当前未显示") +// } +// } // - -import AVKit -import UIKit - -@available(iOS 15.0, *) -class PipManager:NSObject,AVPictureInPictureControllerDelegate{ - var pipController: AVPictureInPictureController? - var contentVC: PipContentVC! - - - func setupPictureInPicture(soureceView:UIView) { - guard AVPictureInPictureController.isPictureInPictureSupported() else { - print("❌ 设备不支持画中画") - return - } - - contentVC = PipContentVC() - let cache = SubTitleCache.shared.get() - if cache.showType == .all { - contentVC.preferredContentSize = CGSizeMake(kScreenWid, 100) - } - else{ - contentVC.preferredContentSize = CGSizeMake(kScreenWid, 50) - } - let contentSource = AVPictureInPictureController.ContentSource( - activeVideoCallSourceView:soureceView , - contentViewController: contentVC - ) - - pipController = AVPictureInPictureController(contentSource: contentSource) - pipController?.delegate = self -// pipController?.canStartPictureInPictureAutomaticallyFromInline = false - } - - @objc func startPictureInPicture(){ - guard let pipController = pipController else { - Logger.debug("❌ pipController 未初始化,无法启动画中画") - return - } - - if pipController.isPictureInPicturePossible { - Logger.debug("✅ 画中画可以启动,准备开始...") - pipController.startPictureInPicture() - contentVC?.reset() - } else { - Logger.debug("❌ 画中画当前无法启动") - } - } - @objc func stopPictureInPicture() { - guard let pipController = pipController else { - Logger.debug("❌ pipController 未初始化,无法停止画中画") - return - } - - if pipController.isPictureInPictureActive { - Logger.debug("✅ 画中画正在显示,准备停止...") - pipController.stopPictureInPicture() - Logger.debug("✅ 画中画已停止") - } else { - Logger.debug("⚠️ 画中画当前未显示") - } - } - - func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - Logger.debug("画中画即将开始") - } - - func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - Logger.debug("✅ 画中画已开始") - } - - func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) { - Logger.debug("❌ 画中画启动失败: \(error)") - } - - func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - Logger.debug("⏹️ 画中画即将结束") - } - - func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - Logger.debug("🛑 画中画已结束") - } -} - - - -// 画中画内容视图控制器 -@available(iOS 15.0, *) -class PipContentVC : AVPictureInPictureVideoCallViewController { - var textView = UILabel() - var translateTextView = UILabel() - var line = UIView() - - override func viewDidLoad() { - super.viewDidLoad() - view.addSubviews([textView,translateTextView,line]) - view.backgroundColor = .black - reset() - } - - 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)) - } - func reset(){ - -// setClearView() - - if (textView.superview == nil) { - return - } - let cache = SubTitleCache.shared.get() - if cache.showType == .all { - preferredContentSize = CGSizeMake(kScreenWid, 100) - } - else{ - preferredContentSize = CGSizeMake(kScreenWid, 50) - } - - textView.do { v in - v.textColor = .color(.level3) - v.font = cache.fontType.font - v.backgroundColor = .clear - v.adjustsFontSizeToFitWidth = true - v.numberOfLines = 0 - } - - translateTextView.do { v in - v.textColor = .white - v.font = cache.fontType.font - v.backgroundColor = .clear - v.adjustsFontSizeToFitWidth = true - v.numberOfLines = 0 - } - line.backgroundColor = .color(.separateLine).withAlphaComponent(0.5) - - - if cache.showType == .all { - textView.snp.remakeConstraints { make in - make.top.equalToSuperview().inset(10) - make.leading.trailing.equalToSuperview().inset(16) - make.bottom.equalTo(view.snp.centerY) - } - translateTextView.snp.remakeConstraints { make in - make.leading.trailing.equalToSuperview().inset(16) - make.top.equalTo(textView.snp.bottom).offset(2) - make.bottom.equalToSuperview().inset(10) - } - line.snp.remakeConstraints { make in - make.bottom.equalTo(textView.snp.bottom) - make.leading.trailing.equalToSuperview().inset(16) - make.height.equalTo(0.5) - } - textView.isHidden = false - translateTextView.isHidden = false - line.isHidden = false - } - else if cache.showType == .from { - textView.snp.remakeConstraints { make in - make.top.bottom.equalToSuperview().inset(10) - make.leading.trailing.equalToSuperview().inset(16) - } - textView.isHidden = false - translateTextView.isHidden = true - line.isHidden = true - } else { - translateTextView.snp.remakeConstraints { make in - make.top.bottom.equalToSuperview().inset(10) - make.leading.trailing.equalToSuperview().inset(16) - } - textView.isHidden = true - translateTextView.isHidden = false - line.isHidden = true - } - } - func setClearView(){ - for window in UIApplication.shared.windows { - finedView(window) - } - } - func finedView(_ sView:UIView){ - sView.backgroundColor = .clear - for v in sView.subviews { - finedView(v) - } - } -} +// func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { +// Logger.debug("画中画即将开始") +// } +// +// func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { +// Logger.debug("✅ 画中画已开始") +// } +// +// func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) { +// Logger.debug("❌ 画中画启动失败: \(error)") +// } +// +// func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { +// Logger.debug("⏹️ 画中画即将结束") +// } +// +// func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { +// Logger.debug("🛑 画中画已结束") +// } +//} +// +// +// +//// 画中画内容视图控制器 +//@available(iOS 15.0, *) +//class PipContentVC : AVPictureInPictureVideoCallViewController { +// var textView = UILabel() +// var translateTextView = UILabel() +// var line = UIView() +// +// override func viewDidLoad() { +// super.viewDidLoad() +// view.addSubviews([textView,translateTextView,line]) +// view.backgroundColor = .black +// reset() +// } +// +// 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)) +// } +// func reset(){ +// +//// setClearView() +// +// if (textView.superview == nil) { +// return +// } +// let cache = SubTitleCache.shared.get() +// if cache.showType == .all { +// preferredContentSize = CGSizeMake(kScreenWid, 100) +// } +// else{ +// preferredContentSize = CGSizeMake(kScreenWid, 50) +// } +// +// textView.do { v in +// v.textColor = .color(.level3) +// v.font = cache.fontType.font +// v.backgroundColor = .clear +// v.adjustsFontSizeToFitWidth = true +// v.numberOfLines = 0 +// } +// +// translateTextView.do { v in +// v.textColor = .white +// v.font = cache.fontType.font +// v.backgroundColor = .clear +// v.adjustsFontSizeToFitWidth = true +// v.numberOfLines = 0 +// } +// line.backgroundColor = .color(.separateLine).withAlphaComponent(0.5) +// +// +// if cache.showType == .all { +// textView.snp.remakeConstraints { make in +// make.top.equalToSuperview().inset(10) +// make.leading.trailing.equalToSuperview().inset(16) +// make.bottom.equalTo(view.snp.centerY) +// } +// translateTextView.snp.remakeConstraints { make in +// make.leading.trailing.equalToSuperview().inset(16) +// make.top.equalTo(textView.snp.bottom).offset(2) +// make.bottom.equalToSuperview().inset(10) +// } +// line.snp.remakeConstraints { make in +// make.bottom.equalTo(textView.snp.bottom) +// make.leading.trailing.equalToSuperview().inset(16) +// make.height.equalTo(0.5) +// } +// textView.isHidden = false +// translateTextView.isHidden = false +// line.isHidden = false +// } +// else if cache.showType == .from { +// textView.snp.remakeConstraints { make in +// make.top.bottom.equalToSuperview().inset(10) +// make.leading.trailing.equalToSuperview().inset(16) +// } +// textView.isHidden = false +// translateTextView.isHidden = true +// line.isHidden = true +// } else { +// translateTextView.snp.remakeConstraints { make in +// make.top.bottom.equalToSuperview().inset(10) +// make.leading.trailing.equalToSuperview().inset(16) +// } +// textView.isHidden = true +// translateTextView.isHidden = false +// line.isHidden = true +// } +// } +// func setClearView(){ +// for window in UIApplication.shared.windows { +// finedView(window) +// } +// } +// func finedView(_ sView:UIView){ +// sView.backgroundColor = .clear +// for v in sView.subviews { +// finedView(v) +// } +// } +//} diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/PipManager.swift b/ShenYin/ShenYin/UI/Home/SubTitle/PipManager.swift new file mode 100644 index 0000000..dd21d79 --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/SubTitle/PipManager.swift @@ -0,0 +1,219 @@ +// +// PipManager.swift +// PipDemo +// +// Created by liangshi on 2026/4/2. +// + +import UIKit +import AVKit +import AVFoundation + +protocol PipManagerDelegate:NSObject { + func pipStopAction(_ pip:PipManager) +} + +class PipManager: NSObject { + private var pipController: AVPictureInPictureController? + private var contentView = PipContentView() + weak var delegate:PipManagerDelegate? + var playerLayer: AVPlayerLayer? + + func dissmiss(){ + if let pip = pipController{ + pip.stopPictureInPicture() + pipController = nil + playerLayer?.removeFromSuperlayer() + } + } + + func reset(supperView:UIView){ + var isStart = false + if let pip = pipController{ + isStart = pip.isPictureInPictureActive + pip.stopPictureInPicture() + pipController = nil + playerLayer?.removeFromSuperlayer() + } + + do { + try AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback, options: [.allowAirPlay, .allowBluetooth]) + try AVAudioSession.sharedInstance().setActive(true) + } catch { + Logger.debug("Audio session setup failed: \(error)") + } + + + if SubTitleCache.shared.get().showType == .all { + playerLayer = AVPlayerLayer(player: AVPlayer(url: Bundle.main.url(forResource: "shu2", withExtension: "mp4")!)) + playerLayer?.frame = CGRect(x: 10, y: kScreenHei, width: kScreenWid - 20, height: 80) + contentView.frame = CGRectMake(0, 0, kScreenWid - 20, 80) + contentView.reset() + } + else{ + playerLayer = AVPlayerLayer(player: AVPlayer(url: Bundle.main.url(forResource: "shu1", withExtension: "mp4")!)) + playerLayer?.frame = CGRect(x: 10, y: kScreenHei, width: kScreenWid - 20, height: 40) + contentView.frame = CGRectMake(0, 0, kScreenWid - 20, 40) + contentView.reset() + } + + playerLayer?.videoGravity = .resizeAspect + playerLayer?.backgroundColor = UIColor.black.cgColor + playerLayer?.cornerRadius = 10 + playerLayer?.masksToBounds = true + supperView.layer.addSublayer(playerLayer!) + + pipController = AVPictureInPictureController(playerLayer: playerLayer!) + pipController?.delegate = self + pipController?.setValue(1, forKey: "controlsStyle") + + if isStart { + DispatchQueue.main.asyncAfter(deadline:.now() + 0.2) { + Logger.debug("延迟0.2秒后执行") + self.start() + } + } + } + + func start() { + guard let controller = pipController else { return } + if controller.isPictureInPicturePossible == true { + do { + try controller.startPictureInPicture() + Logger.debug("画中画启动成功") + } catch { + Logger.debug("画中画启动失败: \(error)") + } + } + } + + func stop() { + pipController?.stopPictureInPicture() + } + func updateRaw(_ text: String) { + contentView.textView.text = text + // textView.scrollRangeToVisible(NSRange(location: textView.text.count, length: 0)) + } + + func updateTrans(_ text : String) { + contentView.translateTextView.text = text + // translateTextView.scrollRangeToVisible(NSRange(location: translateTextView.text.count, length: 0)) + } +} + +extension PipManager: AVPictureInPictureControllerDelegate { + func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + Logger.debug("画中画即将开始") + UIApplication.shared.windows.first!.addSubview(self.contentView) +// playerLayer?.isHidden = true + } + + func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + Logger.debug("画中画已开始") + } + + func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + Logger.debug("画中画即将停止") +// self.contentView.removeFromSuperview() + } + + func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + Logger.debug("画中画已停止") + delegate?.pipStopAction(self) + } + + func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) { + Logger.debug("画中画启动失败:\(error.localizedDescription)") + } + + func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) { + Logger.debug("用户点击了还原按钮") + completionHandler(false) + } +} + +class PipContentView : UIView { + var textView = UILabel() + var translateTextView = UILabel() + var line = UIView() + + override init(frame: CGRect) { + super.init(frame: frame) + addSubviews([textView,translateTextView,line]) + backgroundColor = .black + reset() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + fileprivate func reset(){ + if (textView.superview == nil) { + return + } + let cache = SubTitleCache.shared.get() + textView.do { v in + v.textColor = .color(.level3) + v.font = cache.fontType.font + v.backgroundColor = .clear + v.adjustsFontSizeToFitWidth = true + v.numberOfLines = 0 + } + + translateTextView.do { v in + v.textColor = .white + v.font = cache.fontType.font + v.backgroundColor = .clear + v.adjustsFontSizeToFitWidth = true + v.numberOfLines = 0 + } + line.backgroundColor = .color(.separateLine).withAlphaComponent(0.5) + + + if cache.showType == .all { + textView.snp.remakeConstraints { make in + make.top.equalToSuperview().inset(10) + make.leading.trailing.equalToSuperview().inset(16) + make.bottom.equalTo(self.snp.centerY) + } + translateTextView.snp.remakeConstraints { make in + make.leading.trailing.equalToSuperview().inset(16) + make.top.equalTo(textView.snp.bottom).offset(2) + make.bottom.equalToSuperview().inset(10) + } + line.snp.remakeConstraints { make in + make.bottom.equalTo(textView.snp.bottom) + make.leading.trailing.equalToSuperview().inset(16) + make.height.equalTo(0.5) + } + textView.textColor = .color(.level3) + translateTextView.textColor = .white + textView.isHidden = false + translateTextView.isHidden = false + line.isHidden = false + } + else if cache.showType == .from { + textView.snp.remakeConstraints { make in + make.top.bottom.equalToSuperview().inset(10) + make.leading.trailing.equalToSuperview().inset(16) + } + textView.textColor = .white + translateTextView.textColor = .white + textView.isHidden = false + translateTextView.isHidden = true + line.isHidden = true + } else { + translateTextView.snp.remakeConstraints { make in + make.top.bottom.equalToSuperview().inset(10) + make.leading.trailing.equalToSuperview().inset(16) + } + textView.textColor = .white + translateTextView.textColor = .white + textView.isHidden = true + translateTextView.isHidden = false + line.isHidden = true + } + } + +} diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleSetVC.swift b/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleSetVC.swift index 7c5c581..07dc0e5 100644 --- a/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleSetVC.swift +++ b/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleSetVC.swift @@ -12,6 +12,8 @@ class SubTitleSetVC: BaseVC{ let imgPicker = ImagePickerManager() var languageChanged:(()->Void)? var soureChanged:(()->Void)? + var showTypeChanged:(()->Void)? + var fontChanged:(()->Void)? override func viewDidLoad() { super.viewDidLoad() @@ -121,6 +123,7 @@ extension SubTitleSetVC{ cache.showType = list.first(where: {$0.title == value})! SubTitleCache.shared.save(cache) tableView.reloadData() + showTypeChanged?() } } @objc func fontAction(){ @@ -134,6 +137,7 @@ extension SubTitleSetVC{ cache.fontType = list.first(where: {$0.title == value})! SubTitleCache.shared.save(cache) tableView.reloadData() + fontChanged?() } } } diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/shu1.mp4 b/ShenYin/ShenYin/UI/Home/SubTitle/shu1.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..452e9e897619c1253be5e56f3294caaa99ad6d17 GIT binary patch literal 83211 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 literal 0 HcmV?d00001 -- 2.17.1