字幕修改
authorliangzhenying <867174124@qq.com>
Fri, 3 Apr 2026 09:51:47 +0000 (17:51 +0800)
committerliangzhenying <867174124@qq.com>
Fri, 3 Apr 2026 09:51:47 +0000 (17:51 +0800)
ShenYin/ShenYin.xcodeproj/project.pbxproj
ShenYin/ShenYin/Channel/DeepChords/Channel/UI/JoySubTitleVC.swift
ShenYin/ShenYin/Channel/PLAYBOY/Channel/UI/PbSubTitleVC.swift
ShenYin/ShenYin/UI/Home/SubTitle/PipContentVC.swift
ShenYin/ShenYin/UI/Home/SubTitle/PipManager.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/SubTitle/SubTitleSetVC.swift
ShenYin/ShenYin/UI/Home/SubTitle/shu1.mp4 [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/SubTitle/shu2.mp4 [new file with mode: 0644]

index d7ed4ff..b5e5a20 100644 (file)
                                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;
                                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;
index 4e33021..f5a698a 100644 (file)
+//////
+//////  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)
+//    }
+//}
index 92640b8..ea4c318 100644 (file)
@@ -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()
         }
     }
 }
index 410fedc..f5313ed 100644 (file)
+////
+////  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 (file)
index 0000000..dd21d79
--- /dev/null
@@ -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
+        }
+    }
+
+}
index 7c5c581..07dc0e5 100644 (file)
@@ -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 (file)
index 0000000..452e9e8
Binary files /dev/null and b/ShenYin/ShenYin/UI/Home/SubTitle/shu1.mp4 differ
diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/shu2.mp4 b/ShenYin/ShenYin/UI/Home/SubTitle/shu2.mp4
new file mode 100644 (file)
index 0000000..bc65daa
Binary files /dev/null and b/ShenYin/ShenYin/UI/Home/SubTitle/shu2.mp4 differ