From 888128c4d6d43dd383e9e7e6fd20126bdbed4642 Mon Sep 17 00:00:00 2001 From: liangzhenying <867174124@qq.com> Date: Wed, 4 Feb 2026 17:16:47 +0800 Subject: [PATCH] =?utf8?q?deepchords=E6=9B=BF=E6=8D=A2=E6=88=90=E6=B7=B1?= =?utf8?q?=E9=9F=B3=E8=AF=81=E4=B9=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../Example/Example.xcodeproj/project.pbxproj | 4 +- .../Example/tools/CallRecordManager.swift | 9 +- .../Example/Example/tools/WxCallManager.swift | 228 ++++++++++++++++++ ShenYin/ShenYin.xcodeproj/project.pbxproj | 33 +-- .../sport/close_white.imageset/Contents.json | 22 ++ .../close_white.imageset/Slice 195@2x.png | Bin 0 -> 444 bytes .../close_white.imageset/Slice 195@3x.png | Bin 0 -> 926 bytes .../sport/ready_group.imageset/Contents.json | 22 ++ .../ready_group.imageset/Slice 56@2x.png | Bin 0 -> 3476 bytes .../ready_group.imageset/Slice 56@3x.png | Bin 0 -> 6888 bytes .../sport/ready_set.imageset/Contents.json | 22 ++ .../sport/ready_set.imageset/Slice 55@2x.png | Bin 0 -> 1668 bytes .../sport/ready_set.imageset/Slice 55@3x.png | Bin 0 -> 3076 bytes .../sport/sport_ai.imageset/Contents.json | 22 ++ .../sport/sport_ai.imageset/Group_2400@2x.png | Bin 0 -> 5655 bytes .../sport/sport_ai.imageset/Group_2400@3x.png | Bin 0 -> 11027 bytes .../sport/sport_light.imageset/Contents.json | 22 ++ .../sport_light.imageset/Slice 268@2x.png | Bin 0 -> 4495 bytes .../sport_light.imageset/Slice 268@3x.png | Bin 0 -> 8528 bytes .../sport/tips_2.imageset/Contents.json | 22 ++ .../sport/tips_2.imageset/Slice 59@2x.png | Bin 0 -> 1497 bytes .../sport/tips_2.imageset/Slice 59@3x.png | Bin 0 -> 2836 bytes ShenYin/ShenYin/Cache/TipsCache.swift | 23 ++ ShenYin/ShenYin/Launch/SceneDelegate.swift | 3 +- .../ShenYin/Resource/colors/Colors+YL.swift | 6 + ShenYin/ShenYin/Sdk/DcSdk/tool/CCConfig.swift | 2 +- ShenYin/ShenYin/Sdk/JoySdk/VC/JoyChatVC.swift | 2 +- .../ShenYin/Sdk/JoySdk/tool/CCDevice.swift | 2 +- ShenYin/ShenYin/Sdk/LogManager.swift | 2 +- .../Tools/Extensions/UITextView+Scroll.swift | 17 ++ .../UI/Sport/Sport/Light/BrightnessView.swift | 78 ++++++ .../UI/Sport/Sport/Light/LightSetVC.swift | 97 ++++++++ .../UI/Sport/Sport/Light/ModeView.swift | 152 ++++++++++++ .../UI/Sport/Sport/Light/StateView.swift | 52 ++++ .../UI/Sport/Sport/Ready/FooterView.swift | 14 +- .../ShenYin/UI/Sport/Sport/Ready/Navbar.swift | 67 +++++ .../UI/Sport/Sport/Ready/ReadyHeader.swift | 105 ++++++++ .../UI/Sport/Sport/Ready/ReadyVC.swift | 14 +- .../Sport/Sport/{root => Root}/DataView.swift | 0 .../Sport/{root => Root}/GroupView.swift | 0 .../Sport/Sport/{root => Root}/PlanView.swift | 0 .../Sport/{root => Root}/RouteView.swift | 0 .../Sport/{root => Root}/SportRootView.swift | 0 .../Sport/{root => Root}/SportTypeView.swift | 0 .../UI/Sport/Sport/Sporting/DetailBoard.swift | 118 +++++++++ .../UI/Sport/Sport/Sporting/SimpleBoard.swift | 85 +++++++ .../UI/Sport/Sport/Sporting/SportingVC.swift | 86 +++++++ .../UI/Sport/Sport/Target/CalorieView.swift | 45 ++++ .../UI/Sport/Sport/Target/DistanceView.swift | 45 ++++ .../UI/Sport/Sport/Target/DurationView.swift | 44 ++++ .../UI/Sport/Sport/Target/SportTargetVC.swift | 127 ++++++++++ ShenYin/ShenYin/UI/Sport/SportHomeVC.swift | 2 +- ShenYin/ShenYin/UI/common/CustomButton.swift | 9 + .../ShenYin/UI/common/pop/CustomPicker3.swift | 2 +- .../ShenYin/UI/common/pop/SportPopView.swift | 178 ++++++++++++++ 55 files changed, 1753 insertions(+), 30 deletions(-) create mode 100644 DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/WxCallManager.swift create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/close_white.imageset/Contents.json create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/close_white.imageset/Slice 195@2x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/close_white.imageset/Slice 195@3x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/ready_group.imageset/Contents.json create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/ready_group.imageset/Slice 56@2x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/ready_group.imageset/Slice 56@3x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/ready_set.imageset/Contents.json create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/ready_set.imageset/Slice 55@2x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/ready_set.imageset/Slice 55@3x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_ai.imageset/Contents.json create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_ai.imageset/Group_2400@2x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_ai.imageset/Group_2400@3x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_light.imageset/Contents.json create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_light.imageset/Slice 268@2x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_light.imageset/Slice 268@3x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/tips_2.imageset/Contents.json create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/tips_2.imageset/Slice 59@2x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/tips_2.imageset/Slice 59@3x.png create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Light/BrightnessView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Light/LightSetVC.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Light/ModeView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Light/StateView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Ready/Navbar.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Ready/ReadyHeader.swift rename ShenYin/ShenYin/UI/Sport/Sport/{root => Root}/DataView.swift (100%) rename ShenYin/ShenYin/UI/Sport/Sport/{root => Root}/GroupView.swift (100%) rename ShenYin/ShenYin/UI/Sport/Sport/{root => Root}/PlanView.swift (100%) rename ShenYin/ShenYin/UI/Sport/Sport/{root => Root}/RouteView.swift (100%) rename ShenYin/ShenYin/UI/Sport/Sport/{root => Root}/SportRootView.swift (100%) rename ShenYin/ShenYin/UI/Sport/Sport/{root => Root}/SportTypeView.swift (100%) create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Sporting/DetailBoard.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Sporting/SimpleBoard.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Sporting/SportingVC.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Target/CalorieView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Target/DistanceView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Target/DurationView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Target/SportTargetVC.swift create mode 100644 ShenYin/ShenYin/UI/common/pop/SportPopView.swift diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example.xcodeproj/project.pbxproj b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example.xcodeproj/project.pbxproj index 1d5ca53..f73584e 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example.xcodeproj/project.pbxproj +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example.xcodeproj/project.pbxproj @@ -257,7 +257,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = G23X75CJ67; + DEVELOPMENT_TEAM = MAC46RQ4KH; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/DeepChordsSdk", @@ -338,7 +338,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = G23X75CJ67; + DEVELOPMENT_TEAM = MAC46RQ4KH; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/DeepChordsSdk", diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/CallRecordManager.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/CallRecordManager.swift index 44f6b82..a46bc51 100644 --- a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/CallRecordManager.swift +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/CallRecordManager.swift @@ -70,13 +70,16 @@ class CallRecordManager:NSObject{ selfText = "" otherText = "" setupSdk() - if !DcCall.shared.isInCall { + if DcCall.shared.isInCall || WxCallManager.shared.isInWxCall() { + DCLogger.debug("开启通话录音") + DCCallRecord.shared.start() + } + else{ DCLogger.debug("不在通话状态") callRecordNotOnCall?() return } - DCLogger.debug("开启通话录音") - DCCallRecord.shared.start() + } func recordStop(){ isRecording = false diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/WxCallManager.swift b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/WxCallManager.swift new file mode 100644 index 0000000..65386d7 --- /dev/null +++ b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/tools/WxCallManager.swift @@ -0,0 +1,228 @@ +// +// WxCallManager.swift +// Example +// +// Created by liangshi on 2026/2/2. +// + +import Foundation +import AVFoundation + +class WxCallManager: NSObject { + static let shared = WxCallManager() + + private var audioSessionMonitor: AudioSessionMonitor? + private var lastCheckTime: TimeInterval = 0 + private var isLikelyInAppCall = false + private var callStateChangeHandlers: [(Bool) -> Void] = [] + + override init() { + super.init() + audioSessionMonitor = AudioSessionMonitor(delegate: self) + } + + // 添加通话状态变更回调处理 + func addCallStateChangeHandler(_ handler: @escaping (Bool) -> Void) { + callStateChangeHandlers.append(handler) + } + + // 检测是否在应用内通话(如微信语音通话、QQ语音通话等) + func isInWxCall() -> Bool { + // 获取当前时间 + let currentTime = Date.timeIntervalSinceReferenceDate + + // 限制检查频率,避免过度检查 + if currentTime - lastCheckTime < 1.0 { + return isLikelyInAppCall + } + + lastCheckTime = currentTime + + let audioSession = AVAudioSession.sharedInstance() + + // 检查音频会话类别是否为录音或播放录音 + let category = audioSession.category + if category == .playAndRecord || category == .record { + let currentRoute = audioSession.currentRoute + + // 检查是否有输入输出设备激活 + let hasInput = !currentRoute.inputs.isEmpty + let hasOutput = !currentRoute.outputs.isEmpty + + // 检查特定的输出设备类型,这些通常出现在语音通话中 + for outputPort in currentRoute.outputs { + if outputPort.portType == .builtInReceiver || + outputPort.portType == .builtInSpeaker || + outputPort.portType == .bluetoothHFP || + outputPort.portType == .bluetoothLE || + outputPort.portType == .bluetoothA2DP { + // 同时检查是否有输入设备激活 + for inputPort in currentRoute.inputs { + if inputPort.portType == .builtInMic || + inputPort.portType == .bluetoothHFP || + inputPort.portType == .bluetoothLE { + if !isLikelyInAppCall { + isLikelyInAppCall = true + notifyCallStateChange(true) + } + return true + } + } + } + } + + // 检查是否同时存在输入和输出 + if hasInput && hasOutput { + if !isLikelyInAppCall { + isLikelyInAppCall = true + notifyCallStateChange(true) + } + return true + } + } + + // 如果之前的通话状态是正在进行,但现在检测不到通话,则认为通话已结束 + if isLikelyInAppCall { + let isStillInCall = checkAdvancedAudioSessionInfo() + if !isStillInCall { + isLikelyInAppCall = false + notifyCallStateChange(false) + } + return isStillInCall + } + + // 尝试获取当前音频会话的详细信息 + let advancedCheckResult = checkAdvancedAudioSessionInfo() + if !isLikelyInAppCall && advancedCheckResult { + isLikelyInAppCall = true + notifyCallStateChange(true) + } else if isLikelyInAppCall && !advancedCheckResult { + isLikelyInAppCall = false + notifyCallStateChange(false) + } + + return advancedCheckResult + } + + // 通知通话状态变更 + private func notifyCallStateChange(_ isCallActive: Bool) { + DispatchQueue.main.async { + self.callStateChangeHandlers.forEach { handler in + handler(isCallActive) + } + } + } + + // 更高级的音频会话信息检查 + private func checkAdvancedAudioSessionInfo() -> Bool { + let audioSession = AVAudioSession.sharedInstance() + + let currentRoute = audioSession.currentRoute + + // 检查输出端口 + for output in currentRoute.outputs { + if let channels = output.channels, channels.count > 0 { + // 检查输入端口 + for input in currentRoute.inputs { + if let inputChannels = input.channels, inputChannels.count > 0 { + return true + } + } + } + } + + return false + } +} + +// 音频会话监控协议 +protocol AudioSessionMonitorDelegate: AnyObject { + func audioSessionRouteChanged(to isActive: Bool) +} + +// 音频会话监控类 +class AudioSessionMonitor: NSObject { + private let audioSession = AVAudioSession.sharedInstance() + weak var delegate: AudioSessionMonitorDelegate? + + init(delegate: AudioSessionMonitorDelegate?) { + self.delegate = delegate + super.init() + setupNotifications() + } + + private func setupNotifications() { + // 监听音频会话中断通知 + NotificationCenter.default.addObserver( + self, + selector: #selector(handleInterruption), + name: AVAudioSession.interruptionNotification, + object: audioSession + ) + + // 监听路由变化通知 + NotificationCenter.default.addObserver( + self, + selector: #selector(handleRouteChange), + name: AVAudioSession.routeChangeNotification, + object: audioSession + ) + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + @objc private func handleInterruption(notification: Notification) { + guard let userInfo = notification.userInfo, + let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt, + let type = AVAudioSession.InterruptionType(rawValue: typeValue) else { + return + } + + switch type { + case .began: + // 通话被中断,可能是因为有新的来电 + delegate?.audioSessionRouteChanged(to: false) + case .ended: + // 中断结束,重新检查通话状态 + if let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt { + let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue) + if options.contains(.shouldResume) { + // 应该恢复音频,重新检查通话状态 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + // 延迟一点时间再检查,确保系统状态稳定 + self.delegate?.audioSessionRouteChanged(to: WxCallManager.shared.isInWxCall()) + } + } + } + @unknown default: + break + } + } + + @objc private func handleRouteChange(notification: Notification) { + // 处理音频路由变化事件 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + // 延迟一点时间再检查,因为路由变化后可能需要一点时间才能稳定 + let isCallActive = WxCallManager.shared.isInWxCall() + self.delegate?.audioSessionRouteChanged(to: isCallActive) + } + } +} + +// 扩展WxCallManager以符合AudioSessionMonitorDelegate协议 +extension WxCallManager: AudioSessionMonitorDelegate { + func audioSessionRouteChanged(to isActive: Bool) { + if isActive != isLikelyInAppCall { + isLikelyInAppCall = isActive + notifyCallStateChange(isActive) + } + + if !isLikelyInAppCall { + if CallRecordManager.shared.isRecording{ + CallRecordManager.shared.recordStop() + } + } + } +} diff --git a/ShenYin/ShenYin.xcodeproj/project.pbxproj b/ShenYin/ShenYin.xcodeproj/project.pbxproj index 80d4728..a9c06e7 100644 --- a/ShenYin/ShenYin.xcodeproj/project.pbxproj +++ b/ShenYin/ShenYin.xcodeproj/project.pbxproj @@ -317,6 +317,7 @@ UI/common/pop/PopTextField.swift, UI/common/pop/RecordPopView.swift, UI/common/pop/RecordTipsView.swift, + UI/common/pop/SportPopView.swift, "UI/common/ScrollView+Bottom.swift", UI/common/SendToolBar.swift, UI/common/YLprogressHUD.swift, @@ -486,13 +487,15 @@ UI/Sport/Sport/Ready/CountdownView.swift, UI/Sport/Sport/Ready/CustomMapView.swift, UI/Sport/Sport/Ready/FooterView.swift, + UI/Sport/Sport/Ready/Navbar.swift, + UI/Sport/Sport/Ready/ReadyHeader.swift, UI/Sport/Sport/Ready/ReadyVC.swift, - UI/Sport/Sport/root/DataView.swift, - UI/Sport/Sport/root/GroupView.swift, - UI/Sport/Sport/root/PlanView.swift, - UI/Sport/Sport/root/RouteView.swift, - UI/Sport/Sport/root/SportRootView.swift, - UI/Sport/Sport/root/SportTypeView.swift, + UI/Sport/Sport/Root/DataView.swift, + UI/Sport/Sport/Root/GroupView.swift, + UI/Sport/Sport/Root/PlanView.swift, + UI/Sport/Sport/Root/RouteView.swift, + UI/Sport/Sport/Root/SportRootView.swift, + UI/Sport/Sport/Root/SportTypeView.swift, UI/Sport/Sport/Set/SportSetCell.swift, UI/Sport/Sport/Set/SportSetModel.swift, UI/Sport/Sport/Set/SportSetVC.swift, @@ -576,13 +579,15 @@ UI/Sport/Sport/Ready/CountdownView.swift, UI/Sport/Sport/Ready/CustomMapView.swift, UI/Sport/Sport/Ready/FooterView.swift, + UI/Sport/Sport/Ready/Navbar.swift, + UI/Sport/Sport/Ready/ReadyHeader.swift, UI/Sport/Sport/Ready/ReadyVC.swift, - UI/Sport/Sport/root/DataView.swift, - UI/Sport/Sport/root/GroupView.swift, - UI/Sport/Sport/root/PlanView.swift, - UI/Sport/Sport/root/RouteView.swift, - UI/Sport/Sport/root/SportRootView.swift, - UI/Sport/Sport/root/SportTypeView.swift, + UI/Sport/Sport/Root/DataView.swift, + UI/Sport/Sport/Root/GroupView.swift, + UI/Sport/Sport/Root/PlanView.swift, + UI/Sport/Sport/Root/RouteView.swift, + UI/Sport/Sport/Root/SportRootView.swift, + UI/Sport/Sport/Root/SportTypeView.swift, UI/Sport/Sport/Set/SportSetCell.swift, UI/Sport/Sport/Set/SportSetModel.swift, UI/Sport/Sport/Set/SportSetVC.swift, @@ -1387,7 +1392,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 100041; - DEVELOPMENT_TEAM = G23X75CJ67; + DEVELOPMENT_TEAM = MAC46RQ4KH; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -1443,7 +1448,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 100041; - DEVELOPMENT_TEAM = G23X75CJ67; + DEVELOPMENT_TEAM = MAC46RQ4KH; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/close_white.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/sport/close_white.imageset/Contents.json new file mode 100644 index 0000000..5215673 --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/sport/close_white.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Slice 195@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Slice 195@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/close_white.imageset/Slice 195@2x.png b/ShenYin/ShenYin/Assets.xcassets/sport/close_white.imageset/Slice 195@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..648cd347dbddbab310ff44b2376899bb9b902968 GIT binary patch literal 444 zcmV;t0Ym)98426@f+M9J*mOTqyma}w8m%Ulph4+czaRAGf!vG`6Ka$^vGmhb9c<|?Wz-gMc z@_+eW{gyclf1AKp$!;q_1w=1Y+%8E778lwuZxy1OII16yDWzK{FT{ zk}*r#Iq*}dlu(}qA*cgGTawuh$8kJpW+U%{IGE$Ix%XikKotuPq*BV{~9p%EB> zDy(Z7&SH39P{cGV6g} z0b5~4RERSvj{W+dtpXYXoV)A*8IqMr_`8n0;_kURnEbIqHR?D(;9`j> z_9fTH`MMraCf?%D@H@D@*Y}AJZDNQerr3`Ge2pg?5`C0ry6!gd6a1W~yGMTzi@DE9 zE|!>t!A?ROn<3E;jQ_=V=8X@zP!grt5H~RhZtx?LUEpGfMHoyDatRkUL!vJk{D`}d zF4+SjrBNcnCT1>knmbP@wjAV=#)m|IGV=oeis$CjDov9zU?(l5F}cMQTMlyZI(U!h zX0zGiKbSeizu-O{T`(TSSyyEh+;DHjz6pjLjmVac#+I&_wPX zu6+?vZ@_2EfHg&jIqPSwnCQYX@WbFSwjYl+riR^79f=+xVtlkvOm_uaMmcu`zUl(TZ*VV;xl zr7He!kS8!+O4PN%A-Lo)_YL5joWnLArm28)P7aEaXMpE+l2(SL*V7<+<6M7e)fZpiroVCj{`u_k3O zOO)MQD)$w`e$=E4CW%6@NbWwjt1EW3Nf}HM1*%GJ?i)1-TN>46FiMo&Tr2k|Sq7`i zphpyfP2`5qsIjZnWzZu^_NH=^Z`2@MTn0m;Z^-O{?7>W!%!AtlTAvA7_+7lY+zE9MfECJsUQDr{6MxT_ z^@!#Z|66=*i2efr0RR7PGgFTM000I_L_t&o08@vkWT&B3hX4Qo07*qoM6N<$f^6ot AhX4Qo literal 0 HcmV?d00001 diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/ready_group.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/sport/ready_group.imageset/Contents.json new file mode 100644 index 0000000..d3ca90c --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/sport/ready_group.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Slice 56@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Slice 56@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/ready_group.imageset/Slice 56@2x.png b/ShenYin/ShenYin/Assets.xcassets/sport/ready_group.imageset/Slice 56@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..95351b824920c43e40e2cef8d481bc174728bcea GIT binary patch literal 3476 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 iwg3PC21!IgR09C(!#vwY2lwp&0000mZ literal 0 HcmV?d00001 diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/ready_set.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/sport/ready_set.imageset/Contents.json new file mode 100644 index 0000000..bced9e9 --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/sport/ready_set.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Slice 55@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Slice 55@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/ready_set.imageset/Slice 55@2x.png b/ShenYin/ShenYin/Assets.xcassets/sport/ready_set.imageset/Slice 55@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9123d2e123841177e565ea06b734c57c4ece412c GIT binary patch literal 1668 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 x&Dr@?UyS}200960Q=QbL00006Nklm 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 F{tw&BlV<<` literal 0 HcmV?d00001 diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/tips_2.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/sport/tips_2.imageset/Contents.json new file mode 100644 index 0000000..ba40d8a --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/sport/tips_2.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Slice 59@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Slice 59@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/tips_2.imageset/Slice 59@2x.png b/ShenYin/ShenYin/Assets.xcassets/sport/tips_2.imageset/Slice 59@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..680297599f000deeae11c53ca06af542f6e36246 GIT binary patch literal 1497 zcmV;~1t$85P)jCs8m!` zWK?vcdNN2c&`_|49&S(&7*rr+6dZrQVb`I4+ewxz)|ogh;)j~aquBfXW6s^)vE__wg@}`j^J<}>;dCDW`4d7w$Ja`4X4$ND&S{)#1$&yXiahua7 znAn?R31Z-Cj2#49fXQLq0DE z`xrS@g6O9-TmhUWh~(ToSo%s$S0h0h*!7>$U^y_&V*Gw^+J_wKf}Oy*^$+qta0Iw| zwUetSY@7jGDVzl+eSCuch_0251gUrx_;!dSxo1+tg;9+krD``&Unh&m+v~1gNvlIq zJwZ~NK~jBv)|Ge~A&65~nDc|`w68RjhA;Fa^EKs8dPHNg?#ohlM4`0p)Uj?0dPWNK z6>0=g(5Y{(f|L0XGcDLgd93O)OF991k4wx5v1f! z;9Dco4&E@Fq=ripQLL`3`d`|d2K%WnCL)Stz%DYwH*}rXPn6}p@O@>z5W@s_6s0L0 zqoFGqMGmLD%w1);zIw)&k-^w%S^i?FB1qRdnT_@;z2(KN}fv66xSSW+=+yzZytLmPc=3QF11B+ z$QrPU`4nBvW4FYR9tJO#INYt(f>!A>w#4tEChx%mWuc4Y?lRv|oENo?SkX5k1($^$ z(e_#J66l=p zw1?GVYKIEe#ST$<*ht&!n4Z8rmxqqG)ZzxmLF zR)Q+iP?9&1k2&dNK7b=X$hRRU?l|gz2Z6*qD{2FiNt^5N(Mm3HjU`BZ{;T3Po(%(t zWX$py?~h8!z5xM)w4@&`^OpG^@l;C5UvaUG8O9vat&npnQQe*<5{#x8bA|bb${&`o zZ@V=~){k~Wi=++q<}GvcS4T4rr_*Ewwu}^(O+;scTEehDem+1t?AK6c%yiiNrZxUi zR3y)q&RxF_)mcs(|0C{48fy!sF1KC;bAvdV2cBN;LHb+H_(!%#elM>kw+X1@KA$)5 zoS&LtXAkG7coqNv|Nk_-5|sb|00v1!K~w_(UWDdgJ{pyg00000NkvXXu0mjfzTL;$ literal 0 HcmV?d00001 diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/tips_2.imageset/Slice 59@3x.png b/ShenYin/ShenYin/Assets.xcassets/sport/tips_2.imageset/Slice 59@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..3e96cb2bc9577c719436ce87219859d9aa857613 GIT binary patch literal 2836 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 m|3kga!T0000Bool{ + if let value = UserDefaults.standard.value(forKey: type.title){ + return value as? Bool ?? false + } + return true + } +} + class FontCache{ static func saveFont(type:FontType){ UserDefaults.standard.set(type.rawValue, forKey: "FontCache") diff --git a/ShenYin/ShenYin/Launch/SceneDelegate.swift b/ShenYin/ShenYin/Launch/SceneDelegate.swift index 1f9d25a..e4b59b1 100644 --- a/ShenYin/ShenYin/Launch/SceneDelegate.swift +++ b/ShenYin/ShenYin/Launch/SceneDelegate.swift @@ -19,7 +19,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { App.shared.window = window AppLaunch.start() // MapManager.shared.setupSdk() -// window?.rootViewController = UINavigationController(rootViewController: SportSetVC()) +// window?.rootViewController = UINavigationController(rootViewController: SportingVC()) + } func sceneDidDisconnect(_ scene: UIScene) { diff --git a/ShenYin/ShenYin/Resource/colors/Colors+YL.swift b/ShenYin/ShenYin/Resource/colors/Colors+YL.swift index 7e0d091..85cbbd5 100644 --- a/ShenYin/ShenYin/Resource/colors/Colors+YL.swift +++ b/ShenYin/ShenYin/Resource/colors/Colors+YL.swift @@ -26,6 +26,8 @@ public enum YLColor { case separateLine case cellBg + + case sport } extension YLColor: ColorRGBProviderProtocol { @@ -50,6 +52,8 @@ extension YLColor: ColorRGBProviderProtocol { return 0xF2F2F2 case .cellBg: return 0xffffff + case .sport: + return 0x24C789 } } else{ @@ -72,6 +76,8 @@ extension YLColor: ColorRGBProviderProtocol { return 0x666666 case .cellBg: return 0x242424 + case .sport: + return 0x24C789 } } } diff --git a/ShenYin/ShenYin/Sdk/DcSdk/tool/CCConfig.swift b/ShenYin/ShenYin/Sdk/DcSdk/tool/CCConfig.swift index 5c354f1..52dcfb3 100644 --- a/ShenYin/ShenYin/Sdk/DcSdk/tool/CCConfig.swift +++ b/ShenYin/ShenYin/Sdk/DcSdk/tool/CCConfig.swift @@ -15,7 +15,7 @@ class CCConfig{ static let appStoreVersionURL = "https://itunes.apple.com/cn/lookup?id=6752551504" /// 服务器地址 - // static let baseURL = "http://118.145.178.188:32000" + //static let baseURL = "http://118.145.178.188:32000" static let baseURL = "https://app-api.deepchords.com" //海外版 // static let baseURL = "https://app-api.deepchords-ai.com" diff --git a/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyChatVC.swift b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyChatVC.swift index ee53a3f..c61bbfc 100644 --- a/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyChatVC.swift +++ b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoyChatVC.swift @@ -153,7 +153,7 @@ class JoyChatVC:BaseVC{ func refreshData(){ tableView.reloadData() if JoySdkManager.shared.chat.dataList.count > 0 { - tableView.scrollToRow(at: IndexPath(row: JoySdkManager.shared.chat.dataList.count - 1, section: 0), at: .none, animated: true) + tableView.scrollToRow(at: IndexPath(row: JoySdkManager.shared.chat.dataList.count - 1, section: 0), at: .none, animated: false) } } func loadRoleList(){ diff --git a/ShenYin/ShenYin/Sdk/JoySdk/tool/CCDevice.swift b/ShenYin/ShenYin/Sdk/JoySdk/tool/CCDevice.swift index 00ae57b..b126b36 100644 --- a/ShenYin/ShenYin/Sdk/JoySdk/tool/CCDevice.swift +++ b/ShenYin/ShenYin/Sdk/JoySdk/tool/CCDevice.swift @@ -7,7 +7,7 @@ import JoyGemAI class CCDevice{ static var isJoy = true - static var showRaisound = true + static var showRaisound = false static func initSdk(){ if CCDevice.isJoy { JoySdkManager.shared.setupSdk() diff --git a/ShenYin/ShenYin/Sdk/LogManager.swift b/ShenYin/ShenYin/Sdk/LogManager.swift index 8735333..f61e054 100644 --- a/ShenYin/ShenYin/Sdk/LogManager.swift +++ b/ShenYin/ShenYin/Sdk/LogManager.swift @@ -9,7 +9,7 @@ import Foundation /// 日记管理 class Logger { - static var isDebug: Bool = true + static var isDebug: Bool = false fileprivate static let shared = Logger() private var fileHandle:FileHandle? diff --git a/ShenYin/ShenYin/Tools/Extensions/UITextView+Scroll.swift b/ShenYin/ShenYin/Tools/Extensions/UITextView+Scroll.swift index 002321d..b39058c 100644 --- a/ShenYin/ShenYin/Tools/Extensions/UITextView+Scroll.swift +++ b/ShenYin/ShenYin/Tools/Extensions/UITextView+Scroll.swift @@ -11,3 +11,20 @@ public extension UITextView{ self.scrollRangeToVisible(range) } } +//public extension UITextView{ +// func scrollToBottom(){ +// // 优化:避免空文本情况 +// guard !self.text.isEmpty else { return } +// +// // 优化:直接设置 contentOffset 而不是使用 scrollRangeToVisible +// let bottom = max(0, self.contentSize.height - self.bounds.height + self.contentInset.bottom) +// self.setContentOffset(CGPoint(x: 0, y: bottom), animated: false) +// } +// +// // 添加带动画控制的版本 +// func scrollToBottomAnimated(_ animated: Bool = false) { +//// guard !self.text.isEmpty else { return } +// let bottom = max(0, self.contentSize.height - self.bounds.height + self.contentInset.bottom) +// self.setContentOffset(CGPoint(x: 0, y: bottom), animated: animated) +// } +//} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Light/BrightnessView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Light/BrightnessView.swift new file mode 100644 index 0000000..48f4d23 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Light/BrightnessView.swift @@ -0,0 +1,78 @@ +// +// LightView.swift +// ShenYin +// +// Created by liangshi on 2026/2/2. +// + +extension LightSetVC{ + class BrightnessView: BaseView { + let bgView = UIView() + let titleLabel = UILabel() + let btn = UIButton() + let line = UIView() + let slider = UISlider() + + override func setupViews() { + addSubviews([titleLabel,slider,btn,line]) + + titleLabel.do { (v) in + v.textColor = .white + v.font = .bold(14) + v.textAlignment = .left + v.text = "亮度".l10n + } + + btn.do { v in + v.setImage(UIImage(named: "tips_2"), for: .normal) + v.addTarget(self, action: #selector(tipsAction), for: .touchUpInside) + } + + line.do { v in + v.backgroundColor = .color(.separateLine) + } + slider.do { v in + // v.backgroundColor = .red + v.minimumValue = 0 + v.maximumValue = 100 + v.value = 50 + v.minimumTrackTintColor = .white + v.maximumTrackTintColor = .white.withAlphaComponent(0.2) + v.setThumbImage(UIColor.white.imageCircle(with: CGSize(width: 20, height: 20)), for: .normal) + v.addTarget(self, action: #selector(sliderChanged(_:)), for: .valueChanged) // continuous changes + + } + } + + override func setupLayout() { + titleLabel.snp.makeConstraints { (m) in + m.top.equalToSuperview().inset(16) + m.left.equalToSuperview().offset(32) + } + slider.snp.makeConstraints { make in + make.height.equalTo(40) + make.centerX.equalToSuperview() + make.left.right.equalToSuperview().inset(32) + make.top.equalToSuperview().inset(40) + } + btn.snp.makeConstraints { (m) in + m.size.equalTo(30) + m.right.equalToSuperview().inset(32) + m.centerY.equalTo(titleLabel.snp.centerY) + } + + line.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(28) + make.bottom.equalToSuperview() + make.height.equalTo(0.4) + } + } + + @objc func tipsAction(){ + SportPopView().show() + } + @objc func sliderChanged(_ slider:UISlider){ + + } + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Light/LightSetVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/Light/LightSetVC.swift new file mode 100644 index 0000000..dd68c5d --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Light/LightSetVC.swift @@ -0,0 +1,97 @@ +// +// LightSetVC.swift +// ShenYin +// +// Created by liangshi on 2026/2/2. +// + +import UIKit +class LightSetVC: BaseVC{ + let bgView = UIScrollView() + let icon = UIImageView() + let stateView = StateView() + let modelView = ModeView() + let brightnessView = BrightnessView() + let sysView = StateView() + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + + } + override func setupViews() { + navBar.do { v in + v.title = "灯带设置".l10n + v.titleLabel.textColor = .white + v.backgroundColor = .clear + v.leftView.setImage(UIImage(named: "arrow_left_1"), for: .normal) + } + view.backgroundColor = .rgb(0x1F1F1F) + view.addSubviews([bgView]) + bgView.addSubviews([icon,stateView,modelView,brightnessView,sysView]) + + bgView.do { v in + v.backgroundColor = .clear + } + icon.do { v in + v.backgroundColor = .lightGray + } + stateView.do { v in + v.iSwitch.addTarget(self, action: #selector(stateAction), for: .valueChanged) + + } + modelView.do { v in + } + brightnessView.do { v in +// v.backgroundColor = .yellow + } + sysView.do { v in + v.line.isHidden = true + } + } + override func setupLayout() { + + bgView.snp.makeConstraints { make in + make.left.equalToSuperview() + make.width.equalTo(kScreenWid) + make.top.equalToSuperview().inset(kNavBtm) + make.height.equalTo(kScreenHei - kNavBtm) + } + icon.snp.makeConstraints { make in + make.height.equalTo(263.fitR) + make.width.equalTo(kScreenWid) + make.left.top.equalToSuperview() + } + stateView.snp.makeConstraints { make in + make.width.equalTo(kScreenWid) + make.height.equalTo(60) + make.top.equalTo(icon.snp.bottom).offset(0) + make.left.equalToSuperview() + } + modelView.snp.makeConstraints { make in + make.width.equalTo(kScreenWid) + make.height.equalTo(103) + make.top.equalTo(stateView.snp.bottom).offset(0) + make.left.equalToSuperview() + } + brightnessView.snp.makeConstraints { make in + make.width.equalTo(kScreenWid) + make.height.equalTo(90) + make.top.equalTo(modelView.snp.bottom).offset(0) + make.left.equalToSuperview() + } + sysView.snp.makeConstraints { make in + make.width.equalTo(kScreenWid) + make.height.equalTo(60) + make.top.equalTo(brightnessView.snp.bottom).offset(0) + make.left.equalToSuperview() + make.bottom.equalToSuperview() + } + } + + @objc func stateAction(){ + modelView.isHidden = !stateView.iSwitch.isOn + brightnessView.isHidden = !stateView.iSwitch.isOn + sysView.isHidden = !stateView.iSwitch.isOn + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Light/ModeView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Light/ModeView.swift new file mode 100644 index 0000000..81e393d --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Light/ModeView.swift @@ -0,0 +1,152 @@ +// +// ModeView.swift +// ShenYin +// +// Created by liangshi on 2026/2/2. +// +enum LightMode { + case normal + case breath + case flash + + var name:String{ + switch self { + case .normal: + return "正常".l10n + case .breath: + return "呼吸".l10n + case .flash: + return "闪烁".l10n + } + } +} +extension LightSetVC{ + + + class ModeView: BaseView { + let titleLabel = UILabel() + let normalItem = SelectedTypeItem() + let breathItem = SelectedTypeItem() + let flashItem = SelectedTypeItem() + let line = UIView() + + var isOk:Bool{ + return normalItem.isSelected || breathItem.isSelected || flashItem.isSelected + } + + + override func setupViews() { + // backgroundColor = .red + addSubviews([titleLabel,normalItem,breathItem,flashItem,line]) + + titleLabel.do { v in + v.textAlignment = .center + v.font = .bold(14) + v.textColor = .white + v.text = "模式".l10n + } + normalItem.do { v in + v.mode = .normal + v.yc.setCornerRadius(8) + v.yc.addTap(target: self, action: #selector(normalAction)) + } + breathItem.do { v in + v.mode = .breath + v.yc.setCornerRadius(8) + v.yc.addTap(target: self, action: #selector(breathAction)) + } + flashItem.do { v in + v.mode = .flash + v.yc.setCornerRadius(8) + v.yc.addTap(target: self, action: #selector(flashAction)) + } + line.do { v in + v.backgroundColor = .color(.separateLine) + } + } + + override func setupLayout() { + titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview().inset(20) + make.left.equalToSuperview().inset(32) + } + normalItem.snp.makeConstraints { make in + make.left.equalToSuperview().inset(32) + make.top.equalTo(titleLabel.snp.bottom).offset(10) + make.width.equalTo(80.fitR) + make.height.equalTo(32) + } + breathItem.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.width.height.equalTo(normalItem) + } + flashItem.snp.makeConstraints { make in + make.right.equalToSuperview().inset(32) + make.top.width.height.equalTo(normalItem) + } + line.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(28) + make.bottom.equalToSuperview() + make.height.equalTo(0.4) + } + } + + @objc func normalAction(){ + normalItem.isSelected = true + breathItem.isSelected = false + flashItem.isSelected = false + } + @objc func breathAction(){ + normalItem.isSelected = false + breathItem.isSelected = true + flashItem.isSelected = false + } + @objc func flashAction(){ + normalItem.isSelected = false + breathItem.isSelected = false + flashItem.isSelected = true + } + + + class SelectedTypeItem: BaseView { + let titleLabel = UILabel() + var mode:LightMode = .normal{ + didSet{ + titleLabel.text = mode.name + } + } + + var isSelected:Bool = false{ + didSet{ + if isSelected { + titleLabel.textColor = .color(.first) + backgroundColor = .color(.bgColor) + } + else{ + backgroundColor = .clear + titleLabel.textColor = .color(.secondary) + yc.setBorder(width: 1, color: .color(.secondary)) + } + } + } + + override func setupViews() { + backgroundColor = .color(.bgColor) + addSubviews([titleLabel]) + titleLabel.do { v in + v.textAlignment = .center + v.adjustsFontSizeToFitWidth = true + v.font = .regular(14) + } + self.isSelected = false + } + + override func setupLayout() { + titleLabel.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + } + } + +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Light/StateView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Light/StateView.swift new file mode 100644 index 0000000..fead556 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Light/StateView.swift @@ -0,0 +1,52 @@ +// +// StateView.swift +// ShenYin +// +// Created by liangshi on 2026/2/2. +// + +extension LightSetVC{ + class StateView: BaseView { + let titleLabel = UILabel() + let iSwitch = UISwitch() + let line = UIView() + weak var delegate:SportSetCellDelegate? + + override func setupViews() { + addSubviews([titleLabel,iSwitch,line]) + + titleLabel.do { (v) in + v.textColor = .white + v.font = .bold(14) + v.textAlignment = .left + v.text = "灯带".l10n + } + + iSwitch.do { v in + // v.backgroundColor = .red + } + + line.do { v in + v.backgroundColor = .color(.separateLine) + } + } + + override func setupLayout() { + titleLabel.snp.makeConstraints { (m) in + m.centerY.equalToSuperview() + m.left.equalToSuperview().offset(32) + } + + iSwitch.snp.makeConstraints { (m) in + m.right.equalToSuperview().inset(32) + m.centerY.equalToSuperview() + } + + line.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(28) + make.bottom.equalToSuperview() + make.height.equalTo(0.4) + } + } + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Ready/FooterView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Ready/FooterView.swift index e6847f0..31318c4 100644 --- a/ShenYin/ShenYin/UI/Sport/Sport/Ready/FooterView.swift +++ b/ShenYin/ShenYin/UI/Sport/Sport/Ready/FooterView.swift @@ -54,10 +54,20 @@ extension ReadyVC{ } @objc func targetAction(){ - + App.shared.goToPage(SportTargetVC()) } @objc func goAction(){ - CountdownView.show(in: App.shared.window!) + if true { + CustomAlert.showWith(title: "完善数据".l10n, messgage: "完善你的身体档案,让运动数据更准确。".l10n,ok: "前往".l10n) { + + } okCallback: { + App.shared.goToPage(UserInfoVC()) + } + } + else{ + CountdownView.show(in: App.shared.window!) + } + } @objc func routeAction(){ diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Ready/Navbar.swift b/ShenYin/ShenYin/UI/Sport/Sport/Ready/Navbar.swift new file mode 100644 index 0000000..0cd46c1 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Ready/Navbar.swift @@ -0,0 +1,67 @@ +// +// Navbar.swift +// ShenYin +// +// Created by liangshi on 2026/2/3. +// + +import UIKit + +extension ReadyVC{ + class Navbar: BaseView { + + let icon = UIImageView() + let groupBtn = UIButton() + let setBtn = UIButton() + + override func setupViews() { + // backgroundColor = .red + addSubviews([icon,groupBtn,setBtn]) + icon.do { v in + v.image = UIImage(named: "sport_ai") + } + groupBtn.do { v in + v.setBackgroundImage(UIImage(named: "ready_group"), for: .normal) + v.setTitle("组队".l10n, for: .normal) + v.setTitleColor(.color(.first), for: .normal) + v.titleLabel?.font = .medium(14) + v.addTarget(self, action: #selector(groupAction), for: .touchUpInside) + } + setBtn.do { v in + v.setImage(UIImage(named: "ready_set"), for: .normal) + v.addTarget(self, action: #selector(groupAction), for: .touchUpInside) + } + } + + override func setupLayout() { + icon.snp.makeConstraints { make in + make.size.equalTo(32) + make.bottom.equalToSuperview().inset(10) + make.centerX.equalToSuperview() + } + setBtn.snp.makeConstraints { make in + make.width.equalTo(32) + make.height.equalTo(32) + make.centerY.equalTo(icon) + make.right.equalToSuperview().inset(10) + } + groupBtn.snp.makeConstraints { make in + make.width.equalTo(60) + make.height.equalTo(24) + make.centerY.equalTo(icon) + make.right.equalTo(setBtn.snp.left).offset(-10) + } + } + + @objc func groupAction(){ + App.shared.goToPage(SportSetVC()) + } + @objc func setAction(){ + App.shared.goToPage(SportSetVC()) + } + } + + +} + + diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Ready/ReadyHeader.swift b/ShenYin/ShenYin/UI/Sport/Sport/Ready/ReadyHeader.swift new file mode 100644 index 0000000..3ca1809 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Ready/ReadyHeader.swift @@ -0,0 +1,105 @@ +// +// ReadyHeader.swift +// ShenYin +// +// Created by liangshi on 2026/2/3. +// + +extension ReadyVC{ + class ReadyHeader: BaseView { + let typeLabel = UILabel() + let totalLabel = UILabel() + let unitLabel = UILabel() + let aiBtn = ButtonItem() + + + override func setupViews() { + addSubviews([typeLabel,totalLabel,unitLabel,aiBtn]) + typeLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(12) + v.text = "积累跑步距离" + v.textAlignment = .left + } + totalLabel.do { v in + v.textColor = .color(.first) + v.font = .bold(20) + v.text = "120" + v.textAlignment = .left + } + unitLabel.do { v in + v.textColor = .color(.first) + v.font = .medium(14) + v.text = "公里" + v.textAlignment = .left + } + aiBtn.do { v in + v.yc.setBorder(width: 1, color: .color(.secondary), radius: 18) + v.yc.addTap(target: self, action: #selector(aiAction)) + } + } + + override func setupLayout() { + typeLabel.snp.makeConstraints { make in + make.left.equalToSuperview().inset(32) + make.top.equalToSuperview().inset(25) + + } + totalLabel.snp.makeConstraints { make in + make.left.equalTo(typeLabel) + make.bottom.equalToSuperview().inset(27) + + } + unitLabel.snp.makeConstraints { make in + make.left.equalTo(totalLabel.snp.right).offset(2) + make.bottom.equalToSuperview().inset(30) + + } + + aiBtn.snp.makeConstraints { make in + make.size.equalTo(105) + make.height.equalTo(36) + make.centerY.equalToSuperview() + make.right.equalToSuperview().inset(18) + } + } + + @objc func aiAction(){ + App.shared.goToPage(SportTargetVC()) + } + + + class ButtonItem: BaseView { + let aiLabel = UILabel() + let titleLabel = UILabel() + + override func setupViews() { + addSubviews([titleLabel,aiLabel]) + aiLabel.do { v in + v.textColor = .color(.secondary) + v.font = .bold(14) + v.text = "AI" + } + titleLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(14) + v.text = "语音陪练".l10n + } + backgroundColor = .white + } + + override func setupLayout() { + aiLabel.snp.makeConstraints { make in + make.left.equalToSuperview().inset(12) + make.centerY.equalToSuperview() + } + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().inset(12) + } + } + } + + } + +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Ready/ReadyVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/Ready/ReadyVC.swift index 1eab05b..6c0434f 100644 --- a/ShenYin/ShenYin/UI/Sport/Sport/Ready/ReadyVC.swift +++ b/ShenYin/ShenYin/UI/Sport/Sport/Ready/ReadyVC.swift @@ -7,17 +7,24 @@ import MAMapKit class ReadyVC: BaseVC { - let headerView = UIView() + let bar = Navbar() + let headerView = ReadyHeader() var mapView:MAMapView! let footerView = FooterView() override func setupViews() { + + + mapView = MAMapView(frame: .zero) mapView.delegate = self mapView.showsUserLocation = true mapView.zoomLevel = 16 mapView.userTrackingMode = .followWithHeading + navBar.do { v in + v.addSubview(bar) + } view.addSubviews([headerView,mapView,footerView]) headerView.do { v in v.backgroundColor = .white @@ -31,9 +38,12 @@ class ReadyVC: BaseVC { } } override func setupLayout() { + bar.snp.makeConstraints { make in + make.edges.equalToSuperview() + } headerView.snp.makeConstraints { make in make.left.right.equalToSuperview() - make.top.equalToSuperview().inset(kNavBtm + 20) + make.top.equalToSuperview().inset(kNavBtm) make.height.equalTo(100) } footerView.snp.makeConstraints { make in diff --git a/ShenYin/ShenYin/UI/Sport/Sport/root/DataView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Root/DataView.swift similarity index 100% rename from ShenYin/ShenYin/UI/Sport/Sport/root/DataView.swift rename to ShenYin/ShenYin/UI/Sport/Sport/Root/DataView.swift diff --git a/ShenYin/ShenYin/UI/Sport/Sport/root/GroupView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Root/GroupView.swift similarity index 100% rename from ShenYin/ShenYin/UI/Sport/Sport/root/GroupView.swift rename to ShenYin/ShenYin/UI/Sport/Sport/Root/GroupView.swift diff --git a/ShenYin/ShenYin/UI/Sport/Sport/root/PlanView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Root/PlanView.swift similarity index 100% rename from ShenYin/ShenYin/UI/Sport/Sport/root/PlanView.swift rename to ShenYin/ShenYin/UI/Sport/Sport/Root/PlanView.swift diff --git a/ShenYin/ShenYin/UI/Sport/Sport/root/RouteView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Root/RouteView.swift similarity index 100% rename from ShenYin/ShenYin/UI/Sport/Sport/root/RouteView.swift rename to ShenYin/ShenYin/UI/Sport/Sport/Root/RouteView.swift diff --git a/ShenYin/ShenYin/UI/Sport/Sport/root/SportRootView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Root/SportRootView.swift similarity index 100% rename from ShenYin/ShenYin/UI/Sport/Sport/root/SportRootView.swift rename to ShenYin/ShenYin/UI/Sport/Sport/Root/SportRootView.swift diff --git a/ShenYin/ShenYin/UI/Sport/Sport/root/SportTypeView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Root/SportTypeView.swift similarity index 100% rename from ShenYin/ShenYin/UI/Sport/Sport/root/SportTypeView.swift rename to ShenYin/ShenYin/UI/Sport/Sport/Root/SportTypeView.swift diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Sporting/DetailBoard.swift b/ShenYin/ShenYin/UI/Sport/Sport/Sporting/DetailBoard.swift new file mode 100644 index 0000000..87ce152 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Sporting/DetailBoard.swift @@ -0,0 +1,118 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/2/3. +// + +import UIKit + +extension SportingVC{ + class DetailBoard: BaseView { + let topIcon = UIImageView() + let distanceItem = LabelItem() + let durationItem = LabelItem() + let speedItem = LabelItem() + let calorieItem = LabelItem() + let stepItem = LabelItem() + + override func setupViews() { + backgroundColor = .white + addSubviews([topIcon,distanceItem,durationItem,speedItem,calorieItem,stepItem]) + topIcon.do { v in + v.backgroundColor = .color(.secondary) + v.yc.setCornerRadius(2) + } + distanceItem.do { v in + v.titleLabel.text = "0" + v.detailLabel.text = "总距离".l10n + } + durationItem.do { v in + v.titleLabel.text = "00:00:00" + v.detailLabel.text = "总时长".l10n + } + speedItem.do { v in + v.titleLabel.text = "00:00:00" + v.detailLabel.text = "总时长".l10n + } + calorieItem.do { v in + v.titleLabel.text = "00:00:00" + v.detailLabel.text = "总时长".l10n + } + stepItem.do { v in + v.titleLabel.text = "00:00:00" + v.detailLabel.text = "总时长".l10n + } + } + + override func setupLayout() { + topIcon.snp.makeConstraints { make in + make.width.equalTo(50) + make.height.equalTo(4) + make.top.equalToSuperview().inset(6) + make.centerX.equalToSuperview() + } + distanceItem.snp.makeConstraints { make in + make.left.equalToSuperview().inset(18) + make.top.equalToSuperview().inset(27) + make.height.equalTo(50) + } + durationItem.snp.makeConstraints { make in + make.left.equalToSuperview().inset(18) + make.top.equalToSuperview().inset(180) + make.height.equalTo(50) + } + speedItem.snp.makeConstraints { make in + make.left.equalToSuperview().inset(kScreenWid/2 + 18) + make.top.equalTo(durationItem.snp.top) + make.height.equalTo(50) + } + distanceItem.snp.makeConstraints { make in + make.left.equalToSuperview().inset(18) + make.top.equalToSuperview().inset(250) + make.height.equalTo(50) + } + stepItem.snp.makeConstraints { make in + make.left.equalToSuperview().inset(kScreenWid/2 + 18) + make.top.equalTo(distanceItem.snp.top) + make.height.equalTo(50) + } + } + + @objc func groupAction(){ + App.shared.goToPage(SportSetVC()) + } + + } + + class LabelItem: BaseView { + let detailLabel = UILabel() + let titleLabel = UILabel() + + override func setupViews() { + addSubviews([titleLabel,detailLabel]) + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .bold(24) + v.textAlignment = .left + } + detailLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(12) + v.textAlignment = .left + } + } + + override func setupLayout() { + titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview().inset(0) + make.left.right.equalToSuperview() + } + detailLabel.snp.makeConstraints { make in + make.bottom.equalToSuperview().inset(0) + make.left.right.equalToSuperview() + } + } + } + +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Sporting/SimpleBoard.swift b/ShenYin/ShenYin/UI/Sport/Sport/Sporting/SimpleBoard.swift new file mode 100644 index 0000000..ce06371 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Sporting/SimpleBoard.swift @@ -0,0 +1,85 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/2/3. +// + +import UIKit + +extension SportingVC{ + class SimpleBoard: BaseView { + let topIcon = UIImageView() + let distanceItem = LabelItem() + let durationItem = LabelItem() + + override func setupViews() { + backgroundColor = .white + addSubviews([topIcon,distanceItem,durationItem]) + topIcon.do { v in + v.backgroundColor = .color(.secondary) + v.yc.setCornerRadius(2) + } + distanceItem.do { v in + v.titleLabel.text = "0" + v.detailLabel.text = "总距离".l10n + } + durationItem.do { v in + v.titleLabel.text = "00:00:00" + v.detailLabel.text = "总时长".l10n + } + } + + override func setupLayout() { + topIcon.snp.makeConstraints { make in + make.width.equalTo(50) + make.height.equalTo(4) + make.top.equalToSuperview().inset(6) + make.centerX.equalToSuperview() + } + distanceItem.snp.makeConstraints { make in + make.left.equalToSuperview().inset(18) + make.top.equalToSuperview().inset(27) + make.height.equalTo(50) + } + durationItem.snp.makeConstraints { make in + make.left.equalToSuperview().inset(kScreenWid/2 + 18) + make.top.equalToSuperview().inset(27) + make.height.equalTo(distanceItem) + } + } + + @objc func groupAction(){ + App.shared.goToPage(SportSetVC()) + } + class LabelItem: BaseView { + let detailLabel = UILabel() + let titleLabel = UILabel() + + override func setupViews() { + addSubviews([titleLabel,detailLabel]) + titleLabel.do { v in + v.textColor = .color(.secondary) + v.font = .bold(24) + v.textAlignment = .left + } + detailLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(12) + v.textAlignment = .left + } + } + + override func setupLayout() { + titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview().inset(0) + make.left.right.equalToSuperview() + } + detailLabel.snp.makeConstraints { make in + make.bottom.equalToSuperview().inset(0) + make.left.right.equalToSuperview() + } + } + } + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Sporting/SportingVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/Sporting/SportingVC.swift new file mode 100644 index 0000000..ed528b3 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Sporting/SportingVC.swift @@ -0,0 +1,86 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/2/3. +// + +import MAMapKit + +class SportingVC: BaseVC { + var mapView:MAMapView! + let simpleView = SimpleBoard() + let detailView = DetailBoard() + + override func setupViews() { + + + + mapView = MAMapView(frame: .zero) + mapView.delegate = self + mapView.showsUserLocation = true + mapView.zoomLevel = 16 + mapView.userTrackingMode = .followWithHeading + + view.addSubviews([mapView,simpleView,detailView]) + mapView.do { v in + + } + simpleView.do { v in + // v.backgroundColor = .blue + v.yc.addTap(target: self, action: #selector(simpleTapAction)) + } + detailView.do { v in + v.isHidden = true + v.yc.addTap(target: self, action: #selector(detailTapAction)) + } + } + override func setupLayout() { + simpleView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.bottom.equalToSuperview() + make.height.equalTo(100) + } + detailView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.bottom.equalToSuperview() + make.height.equalTo(460) + } + mapView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + make.bottom.equalTo(simpleView.snp.top) + } + } + override func setupData() { + + } + + @objc func simpleTapAction(){ + simpleView.isHidden = true + detailView.isHidden = false + showDetail() + } + @objc func detailTapAction(){ + simpleView.isHidden = false + detailView.isHidden = true + showSimple() + } + func showSimple(){ + mapView.snp.remakeConstraints { make in + make.left.right.top.equalToSuperview() + make.bottom.equalTo(simpleView.snp.top) + } + } + func showDetail(){ + mapView.snp.remakeConstraints { make in + make.left.right.top.equalToSuperview() + make.bottom.equalTo(detailView.snp.top) + } + } +} + +extension SportingVC:MAMapViewDelegate{ + func mapViewRequireLocationAuth(_ locationManager: CLLocationManager!) { + // locationManager.requestAlwaysAuthorization() + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Target/CalorieView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Target/CalorieView.swift new file mode 100644 index 0000000..e3f5607 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Target/CalorieView.swift @@ -0,0 +1,45 @@ +// +// CaloriView.swift +// ShenYin +// +// Created by liangshi on 2026/2/3. +// + +import JXSegmentedView + +extension SportTargetVC{ + class CaloriView: BaseView ,JXSegmentedListContainerViewListDelegate{ + let label = UILabel() + + override func setupViews() { + addSubviews([label]) + label.do { v in + v.font = .bold(28) + v.textAlignment = .center + v.textColor = .color(.first) + v.text = "300 千卡" + v.yc.addTap(target: self, action: #selector(tapAction)) + } + } + override func setupLayout() { + label.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalToSuperview().inset(50) + } + } + + func listView() -> UIView { + return self + } + @objc func tapAction(){ + let list = Array(stride(from: 1, to: 1000, by: 1)).map { String($0) + " 千卡" } + let v = CustomPicker2(title: "卡路里".l10n, list:list ) + v.selectedIndex = 0 + v.show() + v.didSelected = {[weak self] (index,value) in + guard let self = self else { return } + label.text = value + } + } + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Target/DistanceView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Target/DistanceView.swift new file mode 100644 index 0000000..4aeadce --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Target/DistanceView.swift @@ -0,0 +1,45 @@ +// +// DistanceView.swift +// ShenYin +// +// Created by liangshi on 2026/2/3. +// + +import JXSegmentedView + +extension SportTargetVC{ + class DistanceView: BaseView ,JXSegmentedListContainerViewListDelegate{ + let label = UILabel() + + override func setupViews() { + addSubviews([label]) + label.do { v in + v.font = .bold(28) + v.textAlignment = .center + v.textColor = .color(.first) + v.text = "3 km" + v.yc.addTap(target: self, action: #selector(tapAction)) + } + } + override func setupLayout() { + label.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalToSuperview().inset(50) + } + } + + func listView() -> UIView { + return self + } + @objc func tapAction(){ + let list = Array(stride(from: 1, to: 100, by: 1)).map { String($0) + " km" } + let v = CustomPicker2(title: "距离".l10n, list:list ) + v.selectedIndex = 0 + v.show() + v.didSelected = {[weak self] (index,value) in + guard let self = self else { return } + label.text = value + } + } + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Target/DurationView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Target/DurationView.swift new file mode 100644 index 0000000..aa5b92d --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Target/DurationView.swift @@ -0,0 +1,44 @@ +// +// DurationView.swift +// ShenYin +// +// Created by liangshi on 2026/2/3. +// + +import JXSegmentedView + +extension SportTargetVC{ + class DurationView: BaseView ,JXSegmentedListContainerViewListDelegate{ + let label = UILabel() + + override func setupViews() { + addSubviews([label]) + label.do { v in + v.font = .bold(28) + v.textAlignment = .center + v.textColor = .color(.first) + v.text = "00:30" + v.yc.addTap(target: self, action: #selector(tapAction)) + } + } + override func setupLayout() { + label.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalToSuperview().inset(50) + } + } + + func listView() -> UIView { + return self + } + @objc func tapAction(){ + let v = CustomPicker3(title: "日期".l10n) + v.datePicker.datePickerMode = .time + v.show() + v.didSelected = {[weak self] (date) in + guard let self = self else { return } + label.text = date.format(.shortTime) + } + } + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Target/SportTargetVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/Target/SportTargetVC.swift new file mode 100644 index 0000000..f0cd5cc --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Target/SportTargetVC.swift @@ -0,0 +1,127 @@ +// +// SportTargetVC.swift +// ShenYin +// +// Created by liangshi on 2026/2/3. +// + +import JXSegmentedView + + +enum SportTargetType{ + case distance + case duration + case calorie + + var name:String{ + switch self { + case .distance: + return "距离".l10n + case .duration: + return "时长".l10n + case .calorie: + return "卡路里".l10n + } + } + static var all:[SportTargetType] = [.distance,.duration,.calorie] +} + +class SportTargetVC: BaseVC { + let okBtn = CustomButton() + private let segmentedDataSource = JXSegmentedTitleDataSource() + private let segmentedView = JXSegmentedView() + private lazy var listContainerView: JXSegmentedListContainerView! = { + let view = JXSegmentedListContainerView(dataSource: self, type: .scrollView) + return view + }() + + var list:[SportTargetType] = SportTargetType.all + override func viewDidLoad() { + super.viewDidLoad() + + + } + + override func setupLayout(){ + + segmentedView.snp.makeConstraints { (m) in + m.left.right.equalToSuperview() + m.height.equalTo(44.fitR) + m.top.equalToSuperview().inset(kNavBtm) + } + listContainerView.snp.makeConstraints { (m) in + m.width.left.equalToSuperview() + m.top.equalTo(segmentedView.snp.bottom) + m.bottom.equalToSuperview() + } + okBtn.snp.makeConstraints { make in + make.bottom.equalToSuperview().inset(80) + make.left.right.equalToSuperview().inset(51) + make.height.equalTo(46) + } + } + override func setupViews(){ + navBar.title = "设置目标".l10n + view.addSubview(segmentedView) + view.addSubview(listContainerView) + view.addSubview(okBtn) + + view.backgroundColor = .color(.bgColor) + segmentedView.do { (v) in + segmentedDataSource.titleNormalColor = .color(.secondary) + segmentedDataSource.titleSelectedColor = .color(.first) + segmentedDataSource.titleNormalFont = .regular(14) + segmentedDataSource.titleSelectedFont = .bold(16) + segmentedDataSource.isTitleZoomEnabled = true + // segmentedDataSource.titleSelectedZoomScale = 1 + segmentedDataSource.isTitleStrokeWidthEnabled = true + segmentedDataSource.isItemSpacingAverageEnabled = true + segmentedDataSource.isTitleColorGradientEnabled = true + segmentedDataSource.titles = list.map{"\($0.name)"} + segmentedView.backgroundColor = .clear + + //配置指示器 + let indicator = JXSegmentedIndicatorLineView() + indicator.indicatorWidth = 20 + indicator.indicatorHeight = 4 + indicator.verticalOffset = 6 + indicator.indicatorColor = .color(.sport) + segmentedView.indicators = [indicator] + + v.dataSource = segmentedDataSource + v.listContainer = listContainerView + } + okBtn.set(title: "确认".l10n,style: .sport, target: self, action: #selector(okAction)) + } + @objc func okAction(){ + self.navigationController?.popViewController(animated: true) + } +} + +extension SportTargetVC: JXSegmentedListContainerViewListDelegate { + func listView() -> UIView { + return view + } +} + +extension SportTargetVC: JXSegmentedListContainerViewDataSource { + func numberOfLists(in listContainerView: JXSegmentedListContainerView) -> Int { + if let titleDataSource = segmentedView.dataSource as? JXSegmentedBaseDataSource { + return titleDataSource.dataSource.count + } + return 0 + } + + func listContainerView(_ listContainerView: JXSegmentedListContainerView, initListAt index: Int) -> JXSegmentedListContainerViewListDelegate { + let type = list[index] + switch type { + case .distance: + return DistanceView() + case .duration: + return DurationView() + case .calorie: + return CaloriView() + } + + } +} diff --git a/ShenYin/ShenYin/UI/Sport/SportHomeVC.swift b/ShenYin/ShenYin/UI/Sport/SportHomeVC.swift index 4a4f320..be4ce07 100644 --- a/ShenYin/ShenYin/UI/Sport/SportHomeVC.swift +++ b/ShenYin/ShenYin/UI/Sport/SportHomeVC.swift @@ -43,7 +43,7 @@ class SportHomeVC: BaseVC { // AudioPlayView.shared.show() self.view.addSubview(AudioPlayView.shared) -// MapManager.shared.setupSdk() + MapManager.shared.setupSdk() } override func setupLayout(){ diff --git a/ShenYin/ShenYin/UI/common/CustomButton.swift b/ShenYin/ShenYin/UI/common/CustomButton.swift index a56ca70..c81ffad 100644 --- a/ShenYin/ShenYin/UI/common/CustomButton.swift +++ b/ShenYin/ShenYin/UI/common/CustomButton.swift @@ -13,6 +13,7 @@ enum CustomButtonStyle:Int{ case theme3 case theme4 case custom + case sport } class CustomButton: UIButton { @@ -40,6 +41,10 @@ class CustomButton: UIButton { backgroundColor = .color(.cellBg) titleLabel?.font = .regular(14) } + else if style == .sport { + setTitleColor(.white, for: .normal) + backgroundColor = .color(.sport) + } else{ setTitleColor(.color(.theme), for: .normal) backgroundColor = .clear @@ -70,6 +75,10 @@ class CustomButton: UIButton { backgroundColor = .color(.cellBg) titleLabel?.font = .regular(14) } + else if style == .sport { + setTitleColor(.white, for: .normal) + backgroundColor = .color(.sport) + } else{ setTitleColor(.white, for: .normal) backgroundColor = .clear diff --git a/ShenYin/ShenYin/UI/common/pop/CustomPicker3.swift b/ShenYin/ShenYin/UI/common/pop/CustomPicker3.swift index 54c40bf..81933bb 100644 --- a/ShenYin/ShenYin/UI/common/pop/CustomPicker3.swift +++ b/ShenYin/ShenYin/UI/common/pop/CustomPicker3.swift @@ -4,7 +4,7 @@ class CustomPicker3: BasePresentView { private let titleLabel = UILabel() private let btn = UIButton(type: .custom) private let okBtn = CustomButton() - private let datePicker = UIDatePicker() + let datePicker = UIDatePicker() var selectedDate = Date() { didSet { datePicker.date = selectedDate diff --git a/ShenYin/ShenYin/UI/common/pop/SportPopView.swift b/ShenYin/ShenYin/UI/common/pop/SportPopView.swift new file mode 100644 index 0000000..564334b --- /dev/null +++ b/ShenYin/ShenYin/UI/common/pop/SportPopView.swift @@ -0,0 +1,178 @@ +// +// SportPopView.swift +// ShenYin +// +// Created by liangshi on 2026/2/2. +// + +import UIKit + + +class SportPopView: BasePresentView { + let titleLabel = UILabel() + let scorllView = UIScrollView() + let btn = UIButton(type: .custom) + let tipsIcon = UIImageView() + let desLabel1 = UILabel() + let desLabel2 = UILabel() + let actionbtn = UIButton(type: .custom) + let agreeView = AgreeView() + var didSelected: ((Int,String)->Void)? + + init() { + let h = 350.0 + super.init(contentHeight:h) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func setupSubView() { + super.setupSubView() + + contentView.addSubviews([titleLabel,btn,scorllView,actionbtn,agreeView]) + scorllView.addSubviews([tipsIcon,desLabel1,desLabel2]) + + } + override func setupLayout() { + super.setupLayout() + titleLabel.snp.makeConstraints { (m) in + m.top.equalToSuperview() + m.left.right.equalToSuperview().inset(36) + m.height.equalTo(60) + } + btn.snp.makeConstraints { (m) in + m.centerY.equalTo(titleLabel) + m.right.equalToSuperview() + m.size.equalTo(60) + } + actionbtn.snp.makeConstraints { (m) in + m.bottom.equalToSuperview().inset(60) + m.left.right.equalToSuperview().inset(52) + m.height.equalTo(46) + } + agreeView.snp.makeConstraints { (m) in + m.bottom.equalToSuperview().inset(20) + m.centerX.equalToSuperview() + m.height.equalTo(30) + } + + scorllView.snp.makeConstraints { make in + make.width.equalTo(kScreenWid) + make.height.equalTo(450) + make.left.equalToSuperview().inset(0) + make.top.equalToSuperview().inset(64) + } + tipsIcon.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalToSuperview() + make.size.equalTo(62) + } + desLabel1.snp.makeConstraints { (m) in + m.top.equalTo(tipsIcon.snp.bottom).offset(20) + m.left.equalToSuperview().inset(40) + m.width.equalTo(kScreenWid - 60) + } + desLabel2.snp.makeConstraints { (m) in + m.top.equalTo(desLabel1.snp.bottom).offset(20) + m.left.equalToSuperview().inset(30) + m.width.equalTo(kScreenWid - 60) + m.bottom.equalToSuperview().inset(10) + } + + } + + override func setupStyle() { + super.setupStyle() + contentView.backgroundColor = .rgb(0x242424) + scorllView.do { v in + v.backgroundColor = .clear + } + titleLabel.do { (v) in + v.textColor = .white + v.font = .semiBold(16) + v.textAlignment = .center + v.text = "亮度提醒".l10n + } + btn.do { v in + v.setImage(UIImage(named: "close_white"), for: .normal) + v.addTarget(self, action: #selector(closeAction), for: .touchUpInside) + } + tipsIcon.do { v in + v.image = UIImage(named: "sport_light") + } + desLabel1.do { v in + let text = "高亮度提升可见度,低亮度更省电量, 建议根据使用场景合理调节".l10n + let attributedString = NSMutableAttributedString(string: text) + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineSpacing = 10 + attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: text.count)) + v.attributedText = attributedString + + v.textColor = .white + v.font = .semiBold(16) + v.textAlignment = .center + v.numberOfLines = 0 + } + + actionbtn.do { v in + v.backgroundColor = .color(.theme) + v.setTitle("开始体验".l10n, for: .normal) + v.titleLabel?.font = .regular(14) + v.yc.setCornerRadius(12) + v.setTitleColor(.white, for: .normal) + v.addTarget(self, action: #selector(btnAction), for: .touchUpInside) + } + agreeView.do { v in + v.agreeBtn.isSelected = false + v.agreeBtn.addTarget(self, action: #selector(agreeBtnAction), for: .touchUpInside) + } + } + @objc func closeAction(){ + CommonTipsCache.needShow(type: .light, show: !agreeView.agreeBtn.isSelected) + hide() + } + @objc func btnAction(){ + CommonTipsCache.needShow(type: .light, show: !agreeView.agreeBtn.isSelected) + hide() + } + @objc func agreeBtnAction(){ + agreeView.agreeBtn.isSelected = !agreeView.agreeBtn.isSelected + CommonTipsCache.needShow(type: .light, show: !agreeView.agreeBtn.isSelected) + } + class AgreeView: BaseView { + let agreeBtn = UIButton() + let agreeTitle = UILabel() + override func setupViews() { + agreeBtn.do { (v) in + v.isSelected = true + v.contentMode = .center + v.setImage(UIImage(named: "tip_normal"), for: .normal) + v.setImage(UIImage(named: "tip_selected"), for: .selected) + } + agreeTitle.do { (v) in + v.textColor = .color(.secondary) + v.font = .regular(12) + v.textAlignment = .left + v.text = "下次不再提示".l10n + } + // backgroundColor = .red + addSubviews([agreeBtn,agreeTitle]) + } + override func setupLayout() { + agreeBtn.snp.makeConstraints { (m) in + m.left.top.equalToSuperview() + m.height.equalTo(30) + m.width.equalTo(30) + } + agreeTitle.snp.makeConstraints { (m) in + m.left.equalTo(agreeBtn.snp.right) + m.height.equalToSuperview() + m.centerY.equalTo(agreeBtn) + m.right.equalToSuperview() + } + } + } + +} -- 2.17.1