From 2187f6925cf091e27ae45bdeb21d4ac5bc05b901 Mon Sep 17 00:00:00 2001 From: liangzhenying <867174124@qq.com> Date: Fri, 6 Mar 2026 18:03:32 +0800 Subject: [PATCH] =?utf8?q?=E5=8A=A0=E4=BA=86=E7=94=BB=E4=B8=AD=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- ShenYin/DeepChordsSdk/DCSdk.swift | 2 +- ShenYin/ShenYin.xcodeproj/project.pbxproj | 58 +++- .../xcshareddata/xcschemes/playboy.xcscheme | 78 +++++ .../device/pip_close.imageset/Contents.json | 22 ++ .../device/pip_close.imageset/Slice 49@2x.png | Bin 0 -> 1727 bytes .../device/pip_close.imageset/Slice 49@3x.png | Bin 0 -> 2685 bytes .../device/pip_set.imageset/Contents.json | 22 ++ .../device/pip_set.imageset/Slice 47@2x.png | Bin 0 -> 2145 bytes .../device/pip_set.imageset/Slice 47@3x.png | Bin 0 -> 3748 bytes .../ShenYin/Sdk/JoySdk/VC/HomeDeviceVC.swift | 8 +- ShenYin/ShenYin/ShenYin copy-Info.plist | 4 +- ShenYin/ShenYin/UI/Base/BaseTC.swift | 2 +- .../Home/SubTitle/LanguagePickerView1.swift | 306 ++++++++++++++++++ .../ShenYin/UI/Home/SubTitle/PiPManager.swift | 175 ++++++++++ .../ShenYin/UI/Home/SubTitle/PipPicker.swift | 164 ++++++++++ .../ShenYin/UI/Home/SubTitle/PipSetView.swift | 77 +++++ .../ShenYin/UI/Home/SubTitle/SubTitleVC.swift | 186 +++++++++++ 17 files changed, 1083 insertions(+), 21 deletions(-) create mode 100644 ShenYin/ShenYin.xcodeproj/xcshareddata/xcschemes/playboy.xcscheme create mode 100644 ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Contents.json create mode 100644 ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Slice 49@2x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Slice 49@3x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Contents.json create mode 100644 ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Slice 47@2x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Slice 47@3x.png create mode 100644 ShenYin/ShenYin/UI/Home/SubTitle/LanguagePickerView1.swift create mode 100644 ShenYin/ShenYin/UI/Home/SubTitle/PiPManager.swift create mode 100644 ShenYin/ShenYin/UI/Home/SubTitle/PipPicker.swift create mode 100644 ShenYin/ShenYin/UI/Home/SubTitle/PipSetView.swift create mode 100644 ShenYin/ShenYin/UI/Home/SubTitle/SubTitleVC.swift diff --git a/ShenYin/DeepChordsSdk/DCSdk.swift b/ShenYin/DeepChordsSdk/DCSdk.swift index b6a2a7e..8238e97 100644 --- a/ShenYin/DeepChordsSdk/DCSdk.swift +++ b/ShenYin/DeepChordsSdk/DCSdk.swift @@ -25,7 +25,7 @@ public class DCSdk{ // StarburstConfig.shared().sdkFolder = NSHomeDirectory() + "/Documents/DeepChordsSdk" // 日志路径 // StarburstConfig.shared().logPath = logFilePath() - StarburstConfig.shared().logPath = nil +// StarburstConfig.shared().logPath = nil //是否下发文字到耳机仓 StarburstConfig.shared().enableTextSyncToDevice = false //主从耳机切换是否同步sdk信息到耳机 diff --git a/ShenYin/ShenYin.xcodeproj/project.pbxproj b/ShenYin/ShenYin.xcodeproj/project.pbxproj index a8d96c3..d74af2a 100644 --- a/ShenYin/ShenYin.xcodeproj/project.pbxproj +++ b/ShenYin/ShenYin.xcodeproj/project.pbxproj @@ -17,8 +17,8 @@ 644D1BB5EDD3EA1D77C57786 /* Pods_DeepChordsSdk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 248D8B53B5A178ACA5867275 /* Pods_DeepChordsSdk.framework */; }; 7666589AAA94A323B6090425 /* Pods_playboy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4F1BB919D567C3B6B0EBAA7 /* Pods_playboy.framework */; }; 7A6613B4B617D6E71D34944C /* Pods_ShenYin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 90FF0C8B0D4D6AE0933FA672 /* Pods_ShenYin.framework */; }; - 817510A595D00CAD05E3772C /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; - F2E449CAD5874BE652A69B50 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; + 817510A595D00CAD05E3772C /* (null) in Frameworks */ = {isa = PBXBuildFile; }; + F2E449CAD5874BE652A69B50 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -538,6 +538,11 @@ Sdk/JoySdk/VC/JoyTctyVC.swift, Sdk/JoySdk/VC/JoyXclyVC.swift, Sdk/JoySdk/VC/ModeVC.swift, + UI/Home/SubTitle/LanguagePickerView1.swift, + UI/Home/SubTitle/PiPManager.swift, + UI/Home/SubTitle/PipPicker.swift, + UI/Home/SubTitle/PipSetView.swift, + UI/Home/SubTitle/SubTitleVC.swift, ); target = 3FD1DFE32F0E00BF00A62733 /* playboy */; }; @@ -551,22 +556,31 @@ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */ - 3FCC8B152F1E0280004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" build phase */ = { + 3FCC8B152F1E0280004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" phase from "ShenYin" target */ = { isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet; + attributesByRelativePath = { + Sdk/JoySdk/tool/JoyGemAI.framework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); + }; buildPhase = 3FEFE9092F1B252000D82F17 /* Embed Frameworks */; membershipExceptions = ( Sdk/JoySdk/tool/JoyGemAI.framework, ); }; - 3FCC8B1A2F1E030E004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" build phase */ = { + 3FCC8B1A2F1E030E004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" phase from "playboy" target */ = { isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet; + attributesByRelativePath = { + Sdk/DcSdk/tool/JLAudioUnitKit.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); + }; buildPhase = 3FD1DFEF2F0E00BF00A62733 /* Embed Frameworks */; membershipExceptions = ( Sdk/DcSdk/tool/JLAudioUnitKit.xcframework, ); }; - 3FFB77C62F17A11100EF9A60 /* Exceptions for "DeepChordsSdk" folder in "Embed Frameworks" build phase */ = { + 3FFB77C62F17A11100EF9A60 /* Exceptions for "DeepChordsSdk" folder in "Embed Frameworks" phase from "DeepChordsSdk" target */ = { isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet; + attributesByRelativePath = { + StarburstSdk.framework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); + }; buildPhase = 3FFB77C52F17A11100EF9A60 /* Embed Frameworks */; membershipExceptions = ( StarburstSdk.framework, @@ -578,7 +592,7 @@ 0D54E0F02F0A61E600A80EE7 /* DeepChordsSdk */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( - 3FFB77C62F17A11100EF9A60 /* Exceptions for "DeepChordsSdk" folder in "Embed Frameworks" build phase */, + 3FFB77C62F17A11100EF9A60 /* Exceptions for "DeepChordsSdk" folder in "Embed Frameworks" phase from "DeepChordsSdk" target */, 3FFB77C82F17A14C00EF9A60 /* Exceptions for "DeepChordsSdk" folder in "playboy" target */, ); path = DeepChordsSdk; @@ -588,9 +602,9 @@ isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( 3F887BAE2E38D8DD00C98658 /* Exceptions for "ShenYin" folder in "ShenYin" target */, - 3FCC8B152F1E0280004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" build phase */, + 3FCC8B152F1E0280004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" phase from "ShenYin" target */, 3FD1DFF52F0E00BF00A62733 /* Exceptions for "ShenYin" folder in "playboy" target */, - 3FCC8B1A2F1E030E004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" build phase */, + 3FCC8B1A2F1E030E004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" phase from "playboy" target */, 3F03696C2F2B604900F3CB95 /* Exceptions for "ShenYin" folder in "playboy copy" target */, ); path = ShenYin; @@ -619,7 +633,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F2E449CAD5874BE652A69B50 /* BuildFile in Frameworks */, + F2E449CAD5874BE652A69B50 /* (null) in Frameworks */, 7A6613B4B617D6E71D34944C /* Pods_ShenYin.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -629,7 +643,7 @@ buildActionMask = 2147483647; files = ( 3FFB77C12F179F3100EF9A60 /* DeepChordsSdk.framework in Frameworks */, - 817510A595D00CAD05E3772C /* BuildFile in Frameworks */, + 817510A595D00CAD05E3772C /* (null) in Frameworks */, 30B13DEF1AA0E4C54D873276 /* Pods_playboy.framework in Frameworks */, 7666589AAA94A323B6090425 /* Pods_playboy.framework in Frameworks */, ); @@ -880,10 +894,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-ShenYin/Pods-ShenYin-resources-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-ShenYin/Pods-ShenYin-resources-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ShenYin/Pods-ShenYin-resources.sh\"\n"; @@ -919,10 +937,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-playboy/Pods-playboy-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-playboy/Pods-playboy-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-playboy/Pods-playboy-frameworks.sh\"\n"; @@ -958,10 +980,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-ShenYin/Pods-ShenYin-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-ShenYin/Pods-ShenYin-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ShenYin/Pods-ShenYin-frameworks.sh\"\n"; @@ -1498,7 +1524,7 @@ CODE_SIGN_ENTITLEMENTS = ShenYin/PLAYBOY.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 202603031106; + CURRENT_PROJECT_VERSION = 202603051206; DEVELOPMENT_TEAM = JKLJ58J33Q; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -1511,7 +1537,7 @@ ); GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "ShenYin/ShenYin copy-Info.plist"; - INFOPLIST_KEY_CFBundleDisplayName = PLAYBOY; + INFOPLIST_KEY_CFBundleDisplayName = "PLAYBOY眼镜"; INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "扫描附近蓝牙设备需要获得权限"; INFOPLIST_KEY_NSCameraUsageDescription = "修改用户头像、提交意见反馈需要访问相册使用摄像头"; @@ -1530,7 +1556,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 0.01; PRODUCT_BUNDLE_IDENTIFIER = com.ios.playboy; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1554,7 +1580,7 @@ CODE_SIGN_ENTITLEMENTS = ShenYin/PLAYBOY.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 202603031106; + CURRENT_PROJECT_VERSION = 202603051206; DEVELOPMENT_TEAM = JKLJ58J33Q; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -1567,7 +1593,7 @@ ); GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "ShenYin/ShenYin copy-Info.plist"; - INFOPLIST_KEY_CFBundleDisplayName = PLAYBOY; + INFOPLIST_KEY_CFBundleDisplayName = "PLAYBOY眼镜"; INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "扫描附近蓝牙设备需要获得权限"; INFOPLIST_KEY_NSCameraUsageDescription = "修改用户头像、提交意见反馈需要访问相册使用摄像头"; @@ -1586,7 +1612,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 0.01; PRODUCT_BUNDLE_IDENTIFIER = com.ios.playboy; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/ShenYin/ShenYin.xcodeproj/xcshareddata/xcschemes/playboy.xcscheme b/ShenYin/ShenYin.xcodeproj/xcshareddata/xcschemes/playboy.xcscheme new file mode 100644 index 0000000..2cabc21 --- /dev/null +++ b/ShenYin/ShenYin.xcodeproj/xcshareddata/xcschemes/playboy.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Contents.json new file mode 100644 index 0000000..3fd78ab --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Slice 49@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Slice 49@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Slice 49@2x.png b/ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Slice 49@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f21e71bcefd1dee19ed7657aa538c7219d5cfdbe GIT binary patch literal 1727 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 literal 0 HcmV?d00001 diff --git a/ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Contents.json new file mode 100644 index 0000000..dcf07d8 --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Slice 47@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Slice 47@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Slice 47@2x.png b/ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Slice 47@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4b11b186e1f48e21f5527a8e765ed560e7b3abf6 GIT binary patch literal 2145 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 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