From 770db3a97899425704ffe927c2dbe2224dafd5c4 Mon Sep 17 00:00:00 2001 From: liangzhenying <867174124@qq.com> Date: Thu, 26 Feb 2026 15:16:57 +0800 Subject: [PATCH] =?utf8?q?=E8=BF=90=E5=8A=A8=E6=92=AD=E6=8A=A5=E4=BF=AE?= =?utf8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- ShenYin/DeepChordsSdk/DCTts.swift | 46 +++ ShenYin/Podfile.lock | 21 +- ShenYin/ShenYin.xcodeproj/project.pbxproj | 26 +- .../sport/sport_call.imageset/Contents.json | 22 + .../sport_call.imageset/Frame_2255@2x.png | Bin 0 -> 2051 bytes .../sport_call.imageset/Frame_2255@3x.png | Bin 0 -> 3755 bytes .../Contents.json | 22 + .../Frame_2255@2x.png | Bin 0 -> 1429 bytes .../Frame_2255@3x.png | Bin 0 -> 2679 bytes .../sport_music_list.imageset/Contents.json | 22 + .../Frame_2255@2x.png | Bin 0 -> 1563 bytes .../Frame_2255@3x.png | Bin 0 -> 2735 bytes .../sport/sport_record.imageset/Contents.json | 22 + .../sport_record.imageset/Frame_2255@2x.png | Bin 0 -> 2161 bytes .../sport_record.imageset/Frame_2255@3x.png | Bin 0 -> 3921 bytes ShenYin/ShenYin/Sdk/LogManager.swift | 2 +- ShenYin/ShenYin/UI/Base/BaseTC.swift | 4 +- .../UI/Sport/Music/MusicRootView.swift | 53 +++ .../UI/Sport/Record/RecordHeadItem.swift | 117 ++++++ .../UI/Sport/Record/RecordRootView.swift | 50 +++ .../Sport/Group/Create/AddGroupHeader.swift | 40 ++ .../Sport/Group/Create/CreateGroupVC.swift | 144 +++++++ .../Sport/Group/Create/SportInputItem.swift | 136 +++++++ .../Sport/Group/Create/SportSelectItem.swift | 48 +++ .../Sport/Group/Create/SportSwitchItem.swift | 51 +++ .../Sport/Group/Detail/GroupDetailVC.swift | 29 ++ .../Sport/Group/Member/GroupMemberCell.swift | 161 ++++++++ .../Sport/Group/Member/GroupMemberModel.swift | 21 + .../Sport/Group/Member/GroupMemberVC.swift | 114 ++++++ .../Sport/Group/MyGroup/MyGroupCell.swift | 90 +++++ .../Sport/Group/MyGroup/MyGroupModel.swift | 31 ++ .../Sport/Sport/Group/MyGroup/MyGroupVC.swift | 118 ++++++ .../Sport/Group/MyGroup/MyGroupView.swift | 84 ++++ .../Sport/Sport/Group/Other/GroupRoleVC.swift | 241 +++++++++++ .../Sport/Sport/Group/Other/GroupTypeVC.swift | 80 ++++ .../UI/Sport/Sport/Group/add/AddGroupVC.swift | 106 +++++ .../Sport/Sport/History/SportHistoryVC.swift | 124 ++++++ .../Sport/History/SprotHistoryView.swift | 25 ++ .../UI/Sport/Sport/Light/BrightnessView.swift | 78 ++++ .../UI/Sport/Sport/Light/LightSetVC.swift | 103 +++++ .../UI/Sport/Sport/Light/ModeView.swift | 165 ++++++++ .../UI/Sport/Sport/Light/StateView.swift | 52 +++ .../UI/Sport/Sport/Ready/CountdownView.swift | 76 ++++ .../UI/Sport/Sport/Ready/CustomMapView.swift | 12 + .../Sport/Sport/Ready/DeviceStateView.swift | 57 +++ .../UI/Sport/Sport/Ready/FooterView.swift | 106 +++++ .../ShenYin/UI/Sport/Sport/Ready/Navbar.swift | 110 +++++ .../UI/Sport/Sport/Ready/ReadyHeader.swift | 107 +++++ .../UI/Sport/Sport/Ready/ReadyVC.swift | 76 ++++ .../UI/Sport/Sport/Root/DataView.swift | 136 +++++++ .../UI/Sport/Sport/Root/GroupView.swift | 102 +++++ .../UI/Sport/Sport/Root/PlanView.swift | 26 ++ .../UI/Sport/Sport/Root/RouteView.swift | 98 +++++ .../UI/Sport/Sport/Root/SportRootView.swift | 66 +++ .../UI/Sport/Sport/Root/SportTypeView.swift | 134 +++++++ .../UI/Sport/Sport/Set/SportSetCell.swift | 321 +++++++++++++++ .../UI/Sport/Sport/Set/SportSetModel.swift | 22 + .../UI/Sport/Sport/Set/SportSetVC.swift | 149 +++++++ .../Sport/Sport/SportDetail/CadenceView.swift | 7 + .../Sport/SportDetail/ElevationView.swift | 7 + .../UI/Sport/Sport/SportDetail/FeelView.swift | 57 +++ .../Sport/Sport/SportDetail/PacePerView.swift | 7 + .../UI/Sport/Sport/SportDetail/PaceView.swift | 7 + .../Sport/SportDetail/SportDetailBoard.swift | 309 ++++++++++++++ .../Sport/SportDetail/SportDetailVC.swift | 99 +++++ .../Sport/SportDetail/StepRateView.swift | 7 + .../Sport/SportDetail/StrideLengthView.swift | 7 + .../UI/Sport/Sport/Sporting/DetailBoard.swift | 377 ++++++++++++++++++ .../UI/Sport/Sport/Sporting/SimpleBoard.swift | 90 +++++ .../UI/Sport/Sport/Sporting/SportingVC.swift | 164 ++++++++ .../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 ++++++ .../UI/Sport/Sport/Tools/IBSpeaker.swift | 87 ++++ .../UI/Sport/Sport/Tools/MapManager.swift | 58 +++ .../Sport/Tools/SportCalculateUtil.swift | 168 ++++++++ .../UI/Sport/Sport/Tools/SportManager.swift | 126 ++++++ ShenYin/ShenYin/UI/Sport/SportHomeVC.swift | 131 ++++++ .../Sport/Translate/TranslateRootView.swift | 49 +++ .../UI/common/NotificationManager.swift | 13 +- ShenYin/podfile | 8 +- 82 files changed, 6069 insertions(+), 16 deletions(-) create mode 100644 ShenYin/DeepChordsSdk/DCTts.swift create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_call.imageset/Contents.json create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_call.imageset/Frame_2255@2x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_call.imageset/Frame_2255@3x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_music_collection.imageset/Contents.json create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_music_collection.imageset/Frame_2255@2x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_music_collection.imageset/Frame_2255@3x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_music_list.imageset/Contents.json create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_music_list.imageset/Frame_2255@2x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_music_list.imageset/Frame_2255@3x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_record.imageset/Contents.json create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_record.imageset/Frame_2255@2x.png create mode 100644 ShenYin/ShenYin/Assets.xcassets/sport/sport_record.imageset/Frame_2255@3x.png create mode 100644 ShenYin/ShenYin/UI/Sport/Music/MusicRootView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Record/RecordHeadItem.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Record/RecordRootView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/Create/AddGroupHeader.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/Create/CreateGroupVC.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/Create/SportInputItem.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/Create/SportSelectItem.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/Create/SportSwitchItem.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/Detail/GroupDetailVC.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/Member/GroupMemberCell.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/Member/GroupMemberModel.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/Member/GroupMemberVC.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupCell.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupModel.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupVC.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/Other/GroupRoleVC.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/Other/GroupTypeVC.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Group/add/AddGroupVC.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/History/SportHistoryVC.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/History/SprotHistoryView.swift 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/CountdownView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Ready/CustomMapView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Ready/DeviceStateView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Ready/FooterView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Ready/Navbar.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Ready/ReadyHeader.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Ready/ReadyVC.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Root/DataView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Root/GroupView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Root/PlanView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Root/RouteView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Root/SportRootView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Root/SportTypeView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetCell.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetModel.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetVC.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/SportDetail/CadenceView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/SportDetail/ElevationView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/SportDetail/FeelView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/SportDetail/PacePerView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/SportDetail/PaceView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/SportDetail/SportDetailBoard.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/SportDetail/SportDetailVC.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/SportDetail/StepRateView.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/SportDetail/StrideLengthView.swift 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/Sport/Sport/Tools/IBSpeaker.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Tools/MapManager.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Tools/SportCalculateUtil.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Sport/Tools/SportManager.swift create mode 100644 ShenYin/ShenYin/UI/Sport/SportHomeVC.swift create mode 100644 ShenYin/ShenYin/UI/Sport/Translate/TranslateRootView.swift diff --git a/ShenYin/DeepChordsSdk/DCTts.swift b/ShenYin/DeepChordsSdk/DCTts.swift new file mode 100644 index 0000000..acf2de2 --- /dev/null +++ b/ShenYin/DeepChordsSdk/DCTts.swift @@ -0,0 +1,46 @@ +// +// DCTts.swift +// ShenYin +// +// Created by liangshi on 2026/2/26. +// +import StarburstSdk +public class DCTts:NSObject{ + public static let shared = DCTts() + + /// + var ttsCallBack:((_ stream:Data)->Void)? + + /// 文本合成语音 + /// - Parameters: + /// - text: 文本 + /// - callback: 音频流回调 + public func tts(text:String,callback: @escaping ((Data))->Void){ + self.ttsCallBack = callback + TextToSpeechManager.sharedInstance().textToSpeech( + companyId: "2101982539", + text: text, + reqId: UUID().uuidString, + speedRatio: 1.0, + voiceType: "zh_female_qingchezizi_moon_bigtts", + encoding: "pcm", + callback: self + ) + } +} +extension DCTts: TextToSpeechCallback { + public func onResult(_ response: String) { + guard let jsonObj = try? JSONSerialization.jsonObject(with: response.data(using: .utf8)!, options: []) as? [String:Any], + let respObj = jsonObj["resp"] as? [String:Any], + let base64Data = respObj["data"] as? String, + let pcmBytes = Data(base64Encoded: base64Data) else { + DCLogger.debug("解析PCM失败\n") + return + } + ttsCallBack?(pcmBytes) + } + + public func onError(_ code: Int, message: String) { + DCLogger.debug("合成失败:\(message)\n") + } +} diff --git a/ShenYin/Podfile.lock b/ShenYin/Podfile.lock index 7d0c168..9df3bbc 100644 --- a/ShenYin/Podfile.lock +++ b/ShenYin/Podfile.lock @@ -1,4 +1,11 @@ PODS: + - AMap3DMap-NO-IDFA (10.0.700): + - AMapFoundation-NO-IDFA (>= 1.8.0) + - AMapFoundation-NO-IDFA (1.8.2) + - AMapLocation-NO-IDFA (2.10.0): + - AMapFoundation-NO-IDFA (>= 1.8.0) + - AMapSearch-NO-IDFA (9.7.0): + - AMapFoundation-NO-IDFA (>= 1.8.0) - CryptoSwift (1.8.4) - DeviceKit (5.4.0) - DGActivityIndicatorView (2.1.1) @@ -70,6 +77,9 @@ PODS: - ZipArchive (1.4.0) DEPENDENCIES: + - AMap3DMap-NO-IDFA (= 10.0.700) + - AMapLocation-NO-IDFA + - AMapSearch-NO-IDFA (= 9.7.0) - CryptoSwift - DeviceKit (~> 5.4.0) - DGActivityIndicatorView @@ -81,7 +91,6 @@ DEPENDENCIES: - JXPagingView/Paging - JXSegmentedView - Kingfisher - - Kingfisher (~> 6.0) - MBProgressHUD - MJRefresh - SnapKit @@ -98,6 +107,10 @@ DEPENDENCIES: SPEC REPOS: https://gitee.com/mirrors/CocoaPods-Specs.git: + - AMap3DMap-NO-IDFA + - AMapFoundation-NO-IDFA + - AMapLocation-NO-IDFA + - AMapSearch-NO-IDFA - CryptoSwift - DeviceKit - DGActivityIndicatorView @@ -131,6 +144,10 @@ SPEC REPOS: - ZipArchive SPEC CHECKSUMS: + AMap3DMap-NO-IDFA: e029d65ea35d3e4c0a948e1dab9e77109bf0e86a + AMapFoundation-NO-IDFA: 6ce0ef596d4eb8d934ff498e56747b6de1247b05 + AMapLocation-NO-IDFA: 735791aeb1ba52dff666e4158b86caf4b68b5be7 + AMapSearch-NO-IDFA: 749f73d5a773758f01a24eb8872f8798c8b47cb4 CryptoSwift: e64e11850ede528a02a0f3e768cec8e9d92ecb90 DeviceKit: bd34bb1766d926c3cc5a5f6e3e9a6dd10148b805 DGActivityIndicatorView: ff2c76073d79692f724863c8dd38685866a03cb6 @@ -163,6 +180,6 @@ SPEC CHECKSUMS: YYText: 5c461d709e24d55a182d1441c41dc639a18a4849 ZipArchive: e25a4373192673e3229ac8d6e9f64a3e5713c966 -PODFILE CHECKSUM: d3adf5685042140a1efe3de6a225914ce9255e5c +PODFILE CHECKSUM: 6c1824ce6ff4ee834e74a20980186ec7c76f1d1c COCOAPODS: 1.16.2 diff --git a/ShenYin/ShenYin.xcodeproj/project.pbxproj b/ShenYin/ShenYin.xcodeproj/project.pbxproj index 4db690f..75fb146 100644 --- a/ShenYin/ShenYin.xcodeproj/project.pbxproj +++ b/ShenYin/ShenYin.xcodeproj/project.pbxproj @@ -782,6 +782,7 @@ 3F887B9A2E38D8DC00C98658 /* Resources */, 5A934A35F54A5620CFD3C08A /* [CP] Embed Pods Frameworks */, 3FEFE9092F1B252000D82F17 /* Embed Frameworks */, + 169D8C02ED63ABC1BD3F8125 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -894,6 +895,27 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 169D8C02ED63ABC1BD3F8125 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + 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"; + showEnvVarsInLog = 0; + }; 3FD1DFE62F0E00BF00A62733 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1279,7 +1301,7 @@ CODE_SIGN_ENTITLEMENTS = ShenYin/ShenYin.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 100041; + CURRENT_PROJECT_VERSION = 202602241648; DEVELOPMENT_TEAM = MAC46RQ4KH; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -1334,7 +1356,7 @@ CODE_SIGN_ENTITLEMENTS = ShenYin/ShenYin.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 100041; + CURRENT_PROJECT_VERSION = 202602241648; DEVELOPMENT_TEAM = MAC46RQ4KH; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/sport_call.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/sport/sport_call.imageset/Contents.json new file mode 100644 index 0000000..2942dce --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/sport/sport_call.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame_2255@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame_2255@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/sport_call.imageset/Frame_2255@2x.png b/ShenYin/ShenYin/Assets.xcassets/sport/sport_call.imageset/Frame_2255@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe1f1348dc1e2357c9d48e90822fd0a3261ee60e GIT binary patch literal 2051 zcmV+e2>kbnP)8tV?h_0aeLJV$dG5U2nR&A_Z)SI&=;fZ7JNMr6=l;Yz{ zxu~rzdzlUDVdBL^l%@M=5;3nZh>t2T9UUE0?pHq?S7SIc^{rc$`4tNtCZ-&PBn2ju zF-`%5=R>+RRZ}PcGiJrWS%kXSQh}i#hK&BKXHbM z=ft;<+a0In7}g7Sn#30|N*sXz%$hZ8x{={Cc=MqyA8I2!0ej5Xp?#X5(FcIe`OG-u z05d<8)IKI!YGT(&DrY45eE?*$+2u?;GcBdev<)CM?Nsmqz=o_yYw%S$Oj%5Yjnt&c z2LS9eL8)eJp=+Ae3jzS(_Ebl&!qf|h-qd2VQ2+oVWTHXys0otSgdhOQ-cG4J3ne}x zTb4D~DwsD-EB`mnDKDpCmk$6Txt@k#uM8&n$6FWo8Mdtl&m?A^lF4uv%89cN022Nq zvD5$o!hAZQI$jl1i^~9ct!_EV-3I{P)Y&8ggL&nKQ*nBN+lJQW@X2ji`7fXG(&!0d4Zxq(Hwx<^n^Ex7)%}kL%BTX!O zNO_1p=&(BGQpDr~;6m?(5_l8OSIDSlaj46-?IYEP2p2Jo|BvAJx6cS%1kAX`XUGFG zC_MQ9K=+Ce9mYLjmVOL=6$ZaONDx-^JY051p^#r5)ShOYSuj_Ew&wHs7rku*{w_KJ}@v)lPw~?og)x{s&bK?)q6u1ZpW&;l0wowt}GCu zEzW?f09d;2g!FE_35)NqB+fF`g(rmrTyNvw4aN`^9KiY{qB`r7XZxPvIuRX1`{*o#*ZJTdvZJp15`VTLw~JI!zQV@ zI=WUsk#b!C2D!H<^8#LnsSx=vqdEcn5xD9$4E}^T5;-;@4zB~x70|A7xqMt@MWIjt zU`$zs>I)d#)7+jYouC;-~C ze5?F9?hWqw^AB8sfx`V0$z3V?eZ!8YPML}*)NVw#_wnB9l|kv~+>cSy!+e!SRQnrs(K&%mb8 zbH)(HkcUVBC_ldDHY8dQAq1zfrTEg@iel5`$N=C#aou5Am)LUV^#I}1#Bx#+5E%f~ zSL!Y8$5iOiaw1~>7#}Aa6Uj)4ITiq_^KSp$4MdVSYJ};~-3%ba2H?`AOLqn;vJ@-v z9R(x_DF~PjJ0PRsg%P}oa%=!p8`gtAnPiQY6BE`1vylHT#unL9D9q}F_pCHK&DA(x zyc&boXKq6Ps1$5T2XQ{=)^bu};>B$fC;WuLMoy@wLF65@z0C$%)%B$U}IW``ovQx4uZ|iQfnTPZ~CHD?FpCZDRI9Q1~l| zJuv`!RPO7${g#jEn_b=>cWsYZs*<|D)#69WL_NI|&Ue{W%` zAgEv_lZYDJA-L5*yb)7kBV^RgM%|W}4Z1y!2naFl@OHY zGuh-xqXvNL9b6ngA>sLW8&|b%RLS9;UVVnQVl)`PR zdOq}A3z9kJ0JuhByWHD%>-Mt%LeCB900FmW>^>U7qB^N*08snXMFGTi7lmIRj7x@L#r#oY0ayWSHcKL_9H$4FE0Msk0VA%F1ySKCiZ+II1 z6Fyn&wYk_^dJ-RBuVDIYI(P2eZI>s?{h&xDfyV literal 0 HcmV?d00001 diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/sport_call.imageset/Frame_2255@3x.png b/ShenYin/ShenYin/Assets.xcassets/sport/sport_call.imageset/Frame_2255@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..842ec4f1bc97d7b497c284b551ea385aaf9dd5d4 GIT binary patch literal 3755 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 literal 0 HcmV?d00001 diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/sport_music_collection.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/sport/sport_music_collection.imageset/Contents.json new file mode 100644 index 0000000..2942dce --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/sport/sport_music_collection.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame_2255@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame_2255@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/sport_music_collection.imageset/Frame_2255@2x.png b/ShenYin/ShenYin/Assets.xcassets/sport/sport_music_collection.imageset/Frame_2255@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..45eb13628c2eacee774366b2a70b4d59e1ecef3e GIT binary patch literal 1429 zcmV;G1#0?izK~nZ7rYJLsDHNHs?yH>Wq8x($OvG^zea#h z0B{@tA`Nl3&J8U90yqH!k`Uol zohw=Z0Qe9LN<#2JlOh20+nU#SxLMr(IbgHj;D1I%z~Fn&9u5I8gxfmzECMKiGdKRN z?{m=20?~WDnuXo~V27oY?^G(4zn7PnuLU**{q-mHv$Ls$4E#p#Vj@K zF$SQ~XwLC~*4J*8FQGw$UgxolWMcq$eg9@eQ34~yV)0UfW{H{@128o;HJxZg1C6Gp z&5dOYfSbYjv9Yne2KC#V*mi~k&)5+&MSdnDJk1(41UNtV`>P4 z0F)%0RRVcW`wWijI?7wlgW*T@h5-O4oXzLGFL=io1Y6FtMz8>Y%_2Nw5JDz9nq{DN z{-m)3fEfToI9xcKQ@h=4>Wq&7Pl2Um4*&~x1GnqzwbOKdWm6sJxQ^BU5Yg{^`^rCt z;v_`ZD;vsIbgRy5tpVV$Ep;v{+!HI|5>v<40IX0(A8Uu02d$&nxt-Qyn^>OI&k@pJ(&uFcukdQnnRQ= zU7w`o)&LOVPS@@XJNx{|NOlzg&cG(5EQ6TmTjT-Q%`$^oM30q@=em%cK!H(s`K)mU=UAEOibLX`f{Xvi~u{q1QW>`fR;GK zH}9dl{~8(1-T-vYA>d@IJmLcpJO=UAoqPBEqxvGi99T?t0az4n%JdL`DU$taXBYwf zI&-FU4npv`Ez{`4#Y_<#Ny0er;&`_J<;zw7M^H`P9U z00b9-f|IV}j>#2ynjd#Umhj_w8Fee!DB+mgX(=A7209P+C5e_7=*Ru(KUc@m={Iy z`*xj7+MYoeUmj5aqTkOm5avl854Y=Jd1!sn10XsV9#|dojDKf3%$40vwxr90Po_&c z`&t0&>l>%}a`Ytt)q&tD?=SM%{6Q(?<2J)wCp4}+ygXW(Svv)^RIAnA(Dl~>D3{CE zh47yhEB;+V$o)e2FL!b_*9~l|d~10KrJ1!$=^t0`7V5^+0?>8XZROqS`5yoP|NoQN jTC)HE00v1!K~w_(?o*JT|H;Wi00000NkvXXu0mjfkDsiL literal 0 HcmV?d00001 diff --git a/ShenYin/ShenYin/Assets.xcassets/sport/sport_music_collection.imageset/Frame_2255@3x.png b/ShenYin/ShenYin/Assets.xcassets/sport/sport_music_collection.imageset/Frame_2255@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..c0c4c7415c277f2e98c848664b4cea930ccc690f GIT binary patch literal 2679 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 UIView { + return self + } +} + diff --git a/ShenYin/ShenYin/UI/Sport/Record/RecordHeadItem.swift b/ShenYin/ShenYin/UI/Sport/Record/RecordHeadItem.swift new file mode 100644 index 0000000..51f4273 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Record/RecordHeadItem.swift @@ -0,0 +1,117 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/2/25. +// +class SprotRocrodView: BaseView { + let header = RecordheadItem() + let line = UIView() + let item1 = SportRecordItem() + let item2 = SportRecordItem() + + override func setupViews() { + yc.setCornerRadius(12) + backgroundColor = .color(.cellBg) + addSubviews([header,line,item1,item2]) + line.backgroundColor = .color(.separateLine) + } + + override func setupLayout() { + header.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + make.height.equalTo(51) + } + item1.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(15) + make.height.equalTo(60) + make.top.equalToSuperview().offset(61) + } + item2.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(15) + make.height.equalTo(60) + make.top.equalTo(item1.snp.bottom).offset(14) + } + line.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(14) + make.height.equalTo(1) + make.top.equalToSuperview().inset(47) + } + } +} +class SportRecordItem: BaseView { + let icon = UIImageView() + let titleLabel = UILabel() + + override func setupViews() { + yc.setCornerRadius(12) + backgroundColor = .color(.bgColor) + addSubviews([icon,titleLabel]) + + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .medium(14) + v.text = "".l10n + } + icon.do { v in + v.image = UIImage(named: "arrow_2") + } + } + + override func setupLayout() { + + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(60) + } + icon.snp.makeConstraints { make in + make.size.equalTo(32) + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(14) + } + } +} + +class RecordheadItem: BaseView { + let icon = UIImageView() + let detailLabel = UILabel() + let titleLabel = UILabel() + + override func setupViews() { + yc.setCornerRadius(12) + addSubviews([icon,titleLabel,detailLabel]) + + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .medium(14) + v.text = "".l10n + } + detailLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(12) + v.text = "历史记录".l10n + } + icon.do { v in + v.image = UIImage(named: "arrow_2") + } + backgroundColor = .white + } + + override func setupLayout() { + + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(14) + } + detailLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().inset(27) + } + icon.snp.makeConstraints { make in + make.width.equalTo(7) + make.height.equalTo(13) + make.centerY.equalToSuperview() + make.right.equalToSuperview().inset(12) + } + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Record/RecordRootView.swift b/ShenYin/ShenYin/UI/Sport/Record/RecordRootView.swift new file mode 100644 index 0000000..e1cbc9a --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Record/RecordRootView.swift @@ -0,0 +1,50 @@ +// +// RecordView.swift +// ShenYin +// +// Created by liangshi on 2026/1/20. +// + +import UIKit +import JXSegmentedView + +class RecordRootView: BaseView{ + let content = SprotRocrodView() + + override func setupViews() { + addSubviews([content]) + content.do { v in + v.header.titleLabel.text = "录音".l10n + v.item1.titleLabel.text = "现场录音".l10n + v.item1.icon.image = UIImage(named: "sport_record") + v.item2.titleLabel.text = "通话录音".l10n + v.item2.icon.image = UIImage(named: "sport_call") + v.header.yc.addTap(target: self, action: #selector(historyAction)) + v.item1.yc.addTap(target: self, action: #selector(recordAction)) + v.item2.yc.addTap(target: self, action: #selector(callAction)) + } + } + + override func setupLayout() { + content.snp.makeConstraints { make in + make.left.right.top.equalToSuperview().inset(18) + make.height.equalTo(209) + } + } + @objc func historyAction(){ + App.shared.goToPage(HistoryRecordVC()) + } + @objc func recordAction(){ + CCPage.gotoFieldRecord() + } + @objc func callAction(){ + CCPage.gotoCallRecord() + } +} + +extension RecordRootView: JXSegmentedListContainerViewListDelegate { + func listView() -> UIView { + return self + } +} + diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/AddGroupHeader.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/AddGroupHeader.swift new file mode 100644 index 0000000..f9912ab --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/AddGroupHeader.swift @@ -0,0 +1,40 @@ +// +// AddGroupHeader.swift +// ShenYin +// +// Created by liangshi on 2026/2/6. +// + +class AddGroupHeader: BaseView { + let icon = UIImageView() + let titleLabel = UILabel() + + override func setupViews() { + addSubviews([icon,titleLabel]) + + titleLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(10) + v.text = "设置团队头像".l10n + } + + icon.do { v in + v.image = UIImage(named: "group_avatar") + yc.setCornerRadius(12) + + } + } + + override func setupLayout() { + icon.snp.makeConstraints { make in + make.size.equalTo(90) + make.centerX.equalToSuperview() + make.top.equalToSuperview() + } + titleLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(icon.snp.bottom).offset(6) + } + + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/CreateGroupVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/CreateGroupVC.swift new file mode 100644 index 0000000..c77bec8 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/CreateGroupVC.swift @@ -0,0 +1,144 @@ +// +// CreateGroupVC.swift +// ShenYin +// +// Created by liangshi on 2026/2/5. +// + +import UIKit + +class CreateGroupVC: BaseVC{ + let headerView = AddGroupHeader() + let nameView = SportInputItem() + let typeView = SportSelectItem() + let desView = SportInputItem() + let lightView = SportSelectItem() + let verifyView = SportSwitchItem() + let okBtn = CustomButton() + var sportType:SportType = .walk + + override func setupViews() { + navBar.do { v in + v.title = "创建团队".l10n + v.rightView.setImage(UIImage(named: "help_icon"), for: .normal) + v.rightView.isHidden = false + } + view.addSubviews([headerView,nameView,typeView,desView,lightView,verifyView,okBtn]) + nameView.do { v in + v.yc.setCornerRadius(12) + v.titleLabel.text = "团队名称".l10n + v.textField.text = "111" + v.textField.attributedPlaceholder = NSAttributedString(string: "给团队起个名字".l10n, attributes: [NSAttributedString.Key.foregroundColor: UIColor.color(.placeholder)]) + + } + typeView.do { v in + v.yc.setCornerRadius(12) + v.titleLabel.text = "团队类型".l10n + v.detailLabel.text = sportType.name + v.yc.addTap(target: self, action: #selector(typeAction)) + } + desView.do { v in + v.yc.setCornerRadius(12) + v.titleLabel.text = "简介".l10n + v.textField.attributedPlaceholder = NSAttributedString(string: "描述一下您的团".l10n, attributes: [NSAttributedString.Key.foregroundColor: UIColor.color(.placeholder)]) + } + lightView.do { v in + v.yc.setCornerRadius(12) + v.titleLabel.text = "灯带设置".l10n + v.yc.addTap(target: self, action: #selector(lightAction)) + + } + verifyView.do { v in + v.yc.setCornerRadius(12) + v.titleLabel.text = "加入验证".l10n + v.detailLabel.text = "开启后,成员需要申请才能加入".l10n + } + okBtn.set(title: "完成创建".l10n,style: .theme1, target: self, action: #selector(okAction)) + } + override func setupLayout() { + headerView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalToSuperview().inset(kNavBtm + 20) + make.height.equalTo(120) + } + nameView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.top.equalTo(headerView.snp.bottom).offset(10) + make.height.equalTo(51) + } + typeView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.top.equalTo(nameView.snp.bottom).offset(10) + make.height.equalTo(51) + } + desView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.top.equalTo(typeView.snp.bottom).offset(10) + make.height.equalTo(65) + } + lightView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.top.equalTo(desView.snp.bottom).offset(10) + make.height.equalTo(51) + } + verifyView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.top.equalTo(lightView.snp.bottom).offset(10) + make.height.equalTo(65) + } + okBtn.snp.makeConstraints { make in + make.bottom.equalToSuperview().inset(80) + make.left.right.equalToSuperview().inset(51) + make.height.equalTo(46) + } + } + override func navRightViewTapped() { + let tip = GroupDesPopView() + tip.show() + } + @objc func typeAction(){ + let vc = GroupTypeVC() + vc.didSelected = {[weak self] value in + self?.sportType = value + self?.typeView.detailLabel.text = value.name + } + App.shared.goToPage(vc) + } + @objc func lightAction(){ + App.shared.goToPage(LightSetVC()) + } + @objc func okAction(){ + if nameView.isEmpty { + YLprogressHUD.showAutoHiden("团队名称不能为空".l10n) + return + } + + YLprogressHUD.showLoading() + var params:[String:Any] = [:] + // 团队名称,必须 + params["name"] = nameView.value + // 头像 +// params["icon"] = "" + // 运动类型:running | cycling | walking,必须 + params["type"] = sportType.rawValue + // 团队简介,非必须 +// params["introduction"] = "" + // 1加入审核,0不需要审核,非必须,默认0 + params["joinReview"] = verifyView.iSwitch.isOn ? "1":"0" + let api = CommomApi.createSportGroup(param: params) + HttpClient.request(api){ (model:CommonDataModel, res) in + + if model.isSuccess{ + YLprogressHUD.showSuccess { + App.shared.popPage(animated: false) + let vc = GroupDetailVC(groupId:model.data ) + App.shared.goToPage(vc) + } + } + else{ + YLprogressHUD.showAutoHiden("请求失败".l10n) + } + } + } +} + diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/SportInputItem.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/SportInputItem.swift new file mode 100644 index 0000000..4c10566 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/SportInputItem.swift @@ -0,0 +1,136 @@ +// +// SportInputItem.swift +// ShenYin +// +// Created by liangshi on 2026/2/6. +// + +class SportInputItem: BaseView,UITextFieldDelegate { + let valueLabel = UILabel() + let textField = UITextField() + let titleLabel = UILabel() + var maxLength = 10 + + var isEmpty:Bool{ + if let text = textField.text,text.isNotEmpty { + return false + } + return true + } + var value:String{ + return textField.text ?? "" + } + + override func setupViews() { + yc.setCornerRadius(12) + addSubviews([valueLabel,titleLabel,textField]) + + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .medium(14) + } + textField.do { v in + v.textColor = .color(.first) + v.keyboardType = .default + v.returnKeyType = .done + v.textAlignment = .left + v.addTarget(self, action: #selector(textFieldValueChange), for: .valueChanged) +// v.backgroundColor = .red + v.delegate = self + } + valueLabel.do { v in + v.textColor = .color(.placeholder) + v.font = .regular(10) + v.textAlignment = .right + v.text = "0/\(maxLength)" + } + backgroundColor = .white + } + + override func setupLayout() { + + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(14) + } + textField.snp.makeConstraints { make in + make.left.equalTo(titleLabel.snp.right).offset(10).priority(.high) + make.centerY.height.equalToSuperview() + make.right.equalToSuperview().inset(50).priority(.low) + } + valueLabel.snp.makeConstraints { make in + make.width.equalTo(38) + make.height.equalToSuperview() + make.centerY.equalToSuperview() + make.right.equalToSuperview().inset(12) + } + } + @objc func textFieldValueChange(){ + valueLabel.text = "\(textField.text?.count ?? 0)/\(maxLength)" + } + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + return false + } +} + +class SportTextViewItem: BaseView,UITextViewDelegate { + let valueLabel = UILabel() + let textView = UITextView() + let titleLabel = UILabel() + var maxLength = 10 + + override func setupViews() { + yc.setCornerRadius(12) + addSubviews([valueLabel,titleLabel,textView]) + + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .medium(14) + v.text = "简介".l10n + } + textView.do { v in + v.textColor = .color(.first) + v.keyboardType = .default + v.returnKeyType = .done + v.textAlignment = .left + v.delegate = self + } + valueLabel.do { v in + v.textColor = .color(.placeholder) + v.font = .regular(10) + v.textAlignment = .right + v.text = "0/\(maxLength)" + } + backgroundColor = .white + } + + override func setupLayout() { + + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(14) + } + textView.snp.makeConstraints { make in + make.left.equalTo(titleLabel.snp.right).offset(10).priority(.high) + make.centerY.height.equalToSuperview() + make.right.equalToSuperview().inset(50).priority(.low) + } + valueLabel.snp.makeConstraints { make in + make.width.equalTo(38) + make.height.equalToSuperview() + make.centerY.equalToSuperview() + make.right.equalToSuperview().inset(12) + } + } + + func textViewDidChange(_ textView: UITextView) { + valueLabel.text = "\(textView.text?.count ?? 0)/\(maxLength)" + } + + func textView(_ textView: UITextView, shouldChangeTextInRanges ranges: [NSValue], replacementText text: String) -> Bool { + textView.resignFirstResponder() + + return false + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/SportSelectItem.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/SportSelectItem.swift new file mode 100644 index 0000000..51d8041 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/SportSelectItem.swift @@ -0,0 +1,48 @@ +// +// SportItem.swift +// ShenYin +// +// Created by liangshi on 2026/2/6. +// + +class SportSelectItem: BaseView { + let icon = UIImageView() + let detailLabel = UILabel() + let titleLabel = UILabel() + + override func setupViews() { + yc.setCornerRadius(12) + addSubviews([icon,titleLabel,detailLabel]) + + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .medium(14) + } + detailLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(12) + } + icon.do { v in + v.image = UIImage(named: "arrow_2") + } + backgroundColor = .white + } + + override func setupLayout() { + + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(14) + } + detailLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().inset(27) + } + icon.snp.makeConstraints { make in + make.width.equalTo(7) + make.height.equalTo(13) + make.centerY.equalToSuperview() + make.right.equalToSuperview().inset(12) + } + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/SportSwitchItem.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/SportSwitchItem.swift new file mode 100644 index 0000000..4f5423a --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/Create/SportSwitchItem.swift @@ -0,0 +1,51 @@ +// +// SportSwitchItem.swift +// ShenYin +// +// Created by liangshi on 2026/2/6. +// + +class SportSwitchItem: BaseTableViewCell { + let titleLabel = UILabel() + let detailLabel = UILabel() + let iSwitch = UISwitch() + + + override func setupViews() { + addSubviews([titleLabel,detailLabel,iSwitch]) + + backgroundColor = .color(.cellBg) + titleLabel.do { (v) in + v.textColor = .color(.first) + v.font = .bold(14) + v.textAlignment = .left + } + detailLabel.do { (v) in + v.textColor = .color(.secondary) + v.font = .regular(10) + v.textAlignment = .left + } + iSwitch.do { v in + v.addTarget(self, action: #selector(swicthAction), for: .valueChanged) + } + } + + override func setupLayout() { + titleLabel.snp.makeConstraints { (m) in + m.top.equalToSuperview().inset(16) + m.left.equalToSuperview().offset(16) + } + detailLabel.snp.makeConstraints { (m) in + m.bottom.equalToSuperview().inset(16) + m.left.equalToSuperview().offset(16) + } + iSwitch.snp.makeConstraints { (m) in + m.right.equalToSuperview().inset(15) + m.centerY.equalToSuperview() + } + } + + + @objc func swicthAction(){ + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/Detail/GroupDetailVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/Detail/GroupDetailVC.swift new file mode 100644 index 0000000..bbfc751 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/Detail/GroupDetailVC.swift @@ -0,0 +1,29 @@ +// +// GroupDetailVC.swift +// ShenYin +// +// Created by liangshi on 2026/2/25. +// + +import UIKit + +class GroupDetailVC: BaseVC{ + var groupId = "" + + + init(groupId:String) { + self.groupId = groupId + super.init() + } + + override func setupViews() { + navBar.do { v in + v.title = "团队详情".l10n + } + + } + override func setupLayout() { + + } + +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/Member/GroupMemberCell.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/Member/GroupMemberCell.swift new file mode 100644 index 0000000..048f84e --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/Member/GroupMemberCell.swift @@ -0,0 +1,161 @@ +// +// GroupMemberCell.swift +// ShenYin +// +// Created by liangshi on 2026/2/12. +// + +class GroupMemberCell: BaseTableViewCell { + let bgView = UIView() + let icon = UIImageView() + let nameLabel = UILabel() + let roleType = UIImageView() + let roleLabel = UILabel() + let moreIcon = UIImageView() + let line = UIView() + var model:GroupMemberModel!{ + didSet{ + icon.kf.setImage(with: model.avatar.yc.asUrl) + nameLabel.text = model.userName + roleLabel.text = model.role.name + roleType.backgroundColor = model.role.color +// moreIcon.isHidden = model.userId != CacheManager.shared.user.info.userId + } + } + + + 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([icon,nameLabel,roleLabel,roleType,moreIcon,line]) + + backgroundColor = .color(.bgColor) + contentView.backgroundColor = .clear + selectionStyle = .none + bgView.do { v in + v.backgroundColor = .color(.cellBg) +// v.yc.setCornerRadius(18) + } + nameLabel.do { (v) in + v.textColor = .color(.first) + v.font = .medium(12) + v.textAlignment = .left + v.lineBreakMode = .byTruncatingTail + } + roleLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(10) + v.textAlignment = .left + v.lineBreakMode = .byTruncatingTail + } + roleType.do { v in + v.yc.setCornerRadius(3) + } + icon.do { v in + v.yc.setCornerRadius(18) + } + moreIcon.do { v in + v.image = UIImage(named: "more") + v.contentMode = .center + v.yc.addTap(target: self, action: #selector(moreAction)) + } + line.do { v in + v.backgroundColor = .color(.separateLine) + } + } + + override func setupLayout() { + bgView.snp.makeConstraints { (m) in + m.left.right.equalToSuperview().inset(18) + m.top.equalToSuperview() + m.bottom.equalToSuperview() + } + + icon.snp.makeConstraints { (m) in + m.size.equalTo(36) + m.left.equalToSuperview().inset(15) + m.top.equalToSuperview().inset(15) + } + nameLabel.snp.makeConstraints { (m) in + m.top.equalToSuperview().inset(16) + m.left.equalToSuperview().offset(60) + m.right.lessThanOrEqualToSuperview().inset(16) + } + roleType.snp.makeConstraints { make in + make.left.equalTo(nameLabel) + make.bottom.equalToSuperview().inset(16) + make.width.equalTo(6) + make.height.equalTo(10) + } + roleLabel.snp.makeConstraints { (m) in + m.bottom.equalToSuperview().inset(16) + m.left.equalToSuperview().offset(77) + m.right.lessThanOrEqualToSuperview().inset(16) + } + moreIcon.snp.makeConstraints { make in + make.right.equalToSuperview() + make.centerY.equalToSuperview() + make.width.equalTo(18+18+5) + make.height.equalToSuperview() + } + line.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.bottom.equalToSuperview() + make.height.equalTo(0.4) + } + } + + @objc func moreAction(){ + let vc = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + let action0 = UIAlertAction(title: "团长转让".l10n, style: .default) {[weak self] action in + self?.transferGroup() + } + let action1 = UIAlertAction(title: "移除团队".l10n, style: .default) {[weak self] action in + self?.removeFromGroup() + } + let action2 = UIAlertAction(title: "取消".l10n, style: .cancel) { action in + + } + vc.addAction(action0) + vc.addAction(action1) + vc.addAction(action2) + App.shared.topVC().present(vc, animated: true) + } + @objc func removeFromGroup(){ + let vc = UIAlertController(title: "提示".l10n, message: "确认要把TA移出团队吗?".l10n, preferredStyle: .alert) + let action0 = UIAlertAction(title: "确定".l10n, style: .destructive) {[weak self] action in + self?.removeFromGroup2() + } + let action1 = UIAlertAction(title: "取消".l10n, style: .cancel) {[weak self] action in + } + + vc.addAction(action0) + vc.addAction(action1) + App.shared.topVC().present(vc, animated: true) + } + @objc func removeFromGroup2(){ + + } + + @objc func transferGroup(){ + let vc = UIAlertController(title: "提示".l10n, message: "确认要把团长转让给TA吗?".l10n, preferredStyle: .alert) + let action0 = UIAlertAction(title: "确定".l10n, style: .destructive) {[weak self] action in + self?.transferGroup2() + } + let action1 = UIAlertAction(title: "取消".l10n, style: .cancel) {[weak self] action in + } + + vc.addAction(action0) + vc.addAction(action1) + App.shared.topVC().present(vc, animated: true) + } + @objc func transferGroup2(){ + + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/Member/GroupMemberModel.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/Member/GroupMemberModel.swift new file mode 100644 index 0000000..6552a48 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/Member/GroupMemberModel.swift @@ -0,0 +1,21 @@ +// +// GroupMemberModel.swift +// ShenYin +// +// Created by liangshi on 2026/2/12. +// + +import HandyJSON + +class GroupMemberModelRoot:ResponseBaseModel { + var data = [GroupMemberModel]() +} + +class GroupMemberModel:HandyJSON { + var userId = ""/// 用户ID + var userName = "" /// 用户名 + var role:GroupRoleType = .bjy /// 角色名 + var avatar = "" /// 头像 + + required public init() { } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/Member/GroupMemberVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/Member/GroupMemberVC.swift new file mode 100644 index 0000000..74b7740 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/Member/GroupMemberVC.swift @@ -0,0 +1,114 @@ +// +// GroupMemberVC.swift +// ShenYin +// +// Created by liangshi on 2026/2/12. +// + +import UIKit + +class GroupMemberVC: BaseVC{ + let tableView = UITableView(frame: .zero, style: .plain) + var list:[GroupMemberModel] = [] + var currentType = GroupRoleType.none + let btn = CustomButton() + var groupId = "" + + + init(groupId:String) { + self.groupId = groupId + super.init() + } + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + override func setupViews() { + navBar.do { v in + v.title = "成员管理".l10n +// v.rightView.setImage(UIImage(named: "help_icon"), for: .normal) +// v.rightView.isHidden = false + } + + tableView.do { v in + v.backgroundColor = .clear + v.delegate = self + v.dataSource = self + v.rowHeight = 68 + v.separatorStyle = .none + v.contentInset = UIEdgeInsets(top: 10, left: 0, bottom: 0, right: 0) + v.register(GroupMemberCell.self, forCellReuseIdentifier: GroupMemberCell.reuseID) + + let header = UIView(frame: CGRect(x: 0, y: 0, width: kScreenWid, height: 40)) + let label = UILabel(frame: CGRect(x: 18, y: 0, width: kScreenWid - 36, height: 40)) + label.do { v in + v.text = "成员列表".l10n + v.font = .regular(14) + v.textColor = .color(.secondary) + } + header.addSubview(label) + v.tableHeaderView = header + } + view.addSubviews([tableView,btn]) + + btn.set(title: "邀请".l10n, target: self, action: #selector(btnAction)) + } + override func setupLayout() { + tableView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalTo(kNavBtm) + } + btn.snp.makeConstraints { make in + make.width.equalTo(272) + make.height.equalTo(46) + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().inset(82) + } + } + override func setupData() { + let api = CommomApi.getGroupMembers(groupId: self.groupId) + HttpClient.request(api) {[weak self] (model:GroupMemberModelRoot,res) in + guard let self = self else {return} + if model.isSuccess{ + list = model.data + } + tableView.reloadData() + tableView.mj_header?.endRefreshing() + } + } + @objc func btnAction(){ + + } + override func navRightViewTapped() { + + } +} + +extension GroupMemberVC:UITableViewDelegate,UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return list.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = GroupMemberCell.cell(withTableView: tableView, indexPath: indexPath) + cell.model = list[indexPath.row] + let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 68) + if indexPath.row == 0 { + cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 8, height: 8)) + } + else if indexPath.row == list.count - 1{ + cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.bottomLeft,.bottomRight], cornerRadii: CGSize(width: 8, height: 8)) + } + else{ + cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 0, height: 0)) + } + if list.count == 1 { + cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 8, height: 8)) + } + return cell + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupCell.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupCell.swift new file mode 100644 index 0000000..3174168 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupCell.swift @@ -0,0 +1,90 @@ +// +// MyGroupCell.swift +// ShenYin +// +// Created by liangshi on 2026/2/6. +// + + class MyGroupCell: BaseTableViewCell { + let bgView = UIView() + let icon = UIImageView() + let nameLabel = UILabel() + let contentLabel = UILabel() + + var model:MyGroupModel!{ + didSet{ + if model.icon.isEmpty{ + icon.image = UIImage(named: "group_avatar") + } + else{ + icon.kf.setImage(with: model.icon.yc.asUrl) + } + nameLabel.text = model.name + contentLabel.text = model.type.name + ".\(model.userCount)" + "人已加入".l10n + } + } + + + 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([icon,nameLabel,contentLabel]) + + backgroundColor = .color(.bgColor) + contentView.backgroundColor = .clear + selectionStyle = .none + bgView.do { v in + v.backgroundColor = .color(.cellBg) + v.yc.setCornerRadius(8) + } + nameLabel.do { (v) in + v.textColor = .color(.first) + v.font = .medium(14) + v.textAlignment = .left + v.lineBreakMode = .byTruncatingTail + } + contentLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(11) + v.textAlignment = .left + v.lineBreakMode = .byTruncatingTail + } + + icon.do { v in + v.yc.setCornerRadius(8) + } + } + + override func setupLayout() { + bgView.snp.makeConstraints { (m) in + m.left.right.equalToSuperview().inset(18) + m.top.equalToSuperview().inset(6) + m.bottom.equalToSuperview() + } + + icon.snp.makeConstraints { (m) in + m.size.equalTo(36) + m.left.equalToSuperview().inset(15) + m.top.equalToSuperview().inset(15) + } + nameLabel.snp.makeConstraints { (m) in + m.top.equalToSuperview().inset(16) + m.left.equalToSuperview().offset(60) + m.right.lessThanOrEqualToSuperview().inset(16) + } + contentLabel.snp.makeConstraints { (m) in + m.bottom.equalToSuperview().inset(16) + m.left.equalToSuperview().offset(60) + m.right.lessThanOrEqualToSuperview().inset(16) + } + + } + + +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupModel.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupModel.swift new file mode 100644 index 0000000..703df34 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupModel.swift @@ -0,0 +1,31 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/2/6. +// + +import HandyJSON + +class MyGroupModelRoot:ResponseBaseModel { + var data = [MyGroupModel]() +} + +class MyGroupModel:HandyJSON { + var id = "" // 团队ID + var userId = ""// 用户ID,创建者的ID + var creatorNickname = "" // 创建者昵称 + var creatorAvatar = "" // https://example.com/avatar.jpg", // 创建者头像 + var icon = "" // https://example.com/icon.jpg", // 图标 + var state = "" // 状态:1正常,0解散 + var name = "" // 团队名称 + var type:SportType = SportType.walk // 运动类型:running | cycling | walking + var introduction = "" // 团队简介 + var joinReview = ""// 1加入审核,0不需要审核 + var userCount = 0 //加入人数 + var totalDistance = "" // 总距离,单位米 + var totalTime = "" // 总运动时长,单位秒 + var createTime = "" // 创建时间 + var updateTime = ""// 更新时间 + required public init() { } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupVC.swift new file mode 100644 index 0000000..4b34e04 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupVC.swift @@ -0,0 +1,118 @@ +// +// MyGroupVC.swift +// ShenYin +// +// Created by liangshi on 2026/2/6. +// + +import JXSegmentedView + + +enum MyGroupType{ + case create + case add + + var name:String{ + switch self { + case .create: + return "我管理的" + case .add: + return "我加入的" + } + } + var value:String{ + switch self { + case .create: + return "1" + case .add: + return "0" + } + } + static var all:[MyGroupType] = [.create,.add] +} + +class MyGroupVC: BaseVC { + private let segmentedDataSource = JXSegmentedTitleDataSource() + private let segmentedView = JXSegmentedView() + private lazy var listContainerView: JXSegmentedListContainerView! = { + let view = JXSegmentedListContainerView(dataSource: self, type: .scrollView) + return view + }() + + var list:[MyGroupType] = MyGroupType.all + override func viewDidLoad() { + super.viewDidLoad() + + // AudioPlayView.shared.show() + self.view.addSubview(AudioPlayView.shared) + MapManager.shared.setupSdk() + } + + 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() + } + } + override func setupViews(){ + navBar.do { v in + v.title = "我的团队" + } + view.addSubview(segmentedView) + view.addSubview(listContainerView) + + 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(.theme) + segmentedView.indicators = [indicator] + + v.dataSource = segmentedDataSource + v.listContainer = listContainerView + } + } +} + +extension MyGroupVC: JXSegmentedListContainerViewListDelegate { + func listView() -> UIView { + return view + } +} + +extension MyGroupVC: 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] + return MyGroupView(selectType: type) + + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupView.swift new file mode 100644 index 0000000..8cb5dea --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/MyGroup/MyGroupView.swift @@ -0,0 +1,84 @@ +// +// GroupListView.swift +// ShenYin +// +// Created by liangshi on 2026/2/6. +// + +import UIKit +import JXSegmentedView +import MJRefresh + + +class MyGroupView: BaseView,UITableViewDelegate,UITableViewDataSource { + let tableView = UITableView(frame: .zero, style: .plain) + var list = [MyGroupModel]() + var type = MyGroupType.add + + init(selectType type:MyGroupType){ + super.init(frame: .zero) + self.type = type + tableView.mj_header?.beginRefreshing() + } + + override func setupViews() { + addSubview(tableView) + tableView.do { v in + v.backgroundColor = .color(.bgColor) + v.delegate = self + v.dataSource = self + v.rowHeight = 74 + v.separatorStyle = .none + v.register(MyGroupCell.self, forCellReuseIdentifier: MyGroupCell.reuseID) + v.mj_header = MJRefreshNormalHeader(refreshingTarget: self, refreshingAction: #selector(refreshAction)) + } + } + + override func setupLayout() { + tableView.snp.makeConstraints { (m) in + m.top.equalToSuperview().inset(10) + m.left.right.bottom.equalToSuperview() + } + } + + @objc func refreshAction(){ + var params:[String:Any] = [:] + params["type"] = type.value + + let api = CommomApi.getSportGroupList(param: params) + HttpClient.request(api) {[weak self] (model:MyGroupModelRoot,res) in + guard let self = self else {return} + if model.isSuccess{ + list = model.data + } + tableView.reloadData() + tableView.mj_header?.endRefreshing() + } + + } + + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return list.count + } + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = MyGroupCell.cell(withTableView: tableView, indexPath: indexPath) + cell.model = list[indexPath.row] + return cell + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let vc = GroupDetailVC(groupId: list[indexPath.row].id) + App.shared.goToPage(vc) + } + +} + +extension MyGroupView: JXSegmentedListContainerViewListDelegate { + func listView() -> UIView { + return self + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/Other/GroupRoleVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/Other/GroupRoleVC.swift new file mode 100644 index 0000000..613860a --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/Other/GroupRoleVC.swift @@ -0,0 +1,241 @@ +import UIKit +import HandyJSON + +enum GroupRoleType:String,HandyJSONEnum{ + case none = "none" + case pfs = "pfs" + case lpz = "lpz" + case gpz = "gpz" + case ccs = "ccs" + case psy = "psy" + case bjy = "bjy" + case qns = "qns" + case lqz = "lqz" + case gqz = "gqz" + case pps = "pps" + + + var name:String{ + switch self { + case .none: + return "无" + case .pfs: + return "破风手" + case .lpz: + return "领跑者" + case .gpz: + return "跟跑者" + case .ccs: + return "冲刺手" + case .psy: + return "配速员" + case .bjy: + return "补给员" + case .qns: + return "全能手" + case .lqz: + return "领骑者" + case .gqz: + return "跟骑者" + case .pps: + return "爬坡手" + } + } + var color:UIColor{ + switch self { + case .none: + return .rgb(0xD9D9D9) + case .pfs: + return .rgb(0xFF9232) + case .lpz: + return .rgb(0x7C2CED) + case .gpz: + return .rgb(0x21B7F8) + case .ccs: + return .rgb(0xFF583B) + case .psy: + return .rgb(0x4252FF) + case .bjy: + return .rgb(0x13D598) + case .qns: + return .rgb(0xF1BF18) + case .lqz: + return .rgb(0x7C2CED) + case .gqz: + return .rgb(0xF1BF18) + case .pps: + return .rgb(0xF1BF18) + } + } + + static var all:[GroupRoleType] = [.none,.pfs,.lpz,.gpz,.ccs,.psy,.bjy,.qns] +} + +class GroupRoleVC: BaseVC{ + let tableView = UITableView(frame: .zero, style: .plain) + var list:[GroupRoleType] = GroupRoleType.all + var currentType = GroupRoleType.none + let btn = CustomButton() + var didSelected:((GroupRoleType)->Void)? + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + override func setupViews() { + navBar.do { v in + v.title = "我的团队角色".l10n + v.rightView.setImage(UIImage(named: "help_icon"), for: .normal) + v.rightView.isHidden = false + } + + tableView.do { v in + v.backgroundColor = .color(.bgColor) + v.delegate = self + v.dataSource = self + v.rowHeight = 52 + v.separatorStyle = .none + v.contentInset = UIEdgeInsets(top: 10, left: 0, bottom: 0, right: 0) + v.register(RoleCell.self, forCellReuseIdentifier: RoleCell.reuseID) + + let header = UIView(frame: CGRect(x: 0, y: 0, width: kScreenWid, height: 40)) + let label = UILabel(frame: CGRect(x: 18, y: 0, width: kScreenWid - 36, height: 40)) + label.do { v in + v.text = "我的团队-团队设置-骑行团队角色".l10n + v.font = .regular(14) + v.textColor = .color(.secondary) + } + header.addSubview(label) + v.tableHeaderView = header + } + view.addSubviews([tableView,btn]) + + btn.set(title: "确定".l10n, target: self, action: #selector(btnAction)) + } + override func setupLayout() { + tableView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalTo(kNavBtm) + } + btn.snp.makeConstraints { make in + make.width.equalTo(272) + make.height.equalTo(46) + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().inset(82) + } + } + @objc func btnAction(){ + didSelected?(currentType) + self.navigationController?.popViewController(animated: true) + } + override func navRightViewTapped() { + let tip = GroupDesPopView() + tip.titleLabel.text = "团队角色说明".l10n + tip.titleLabel1.text = "团队角色是什么?".l10n + tip.desLabel2.text = "团队运动中各角色是分工明确、协同发力的核心单元,通过精准配合最大化团队运动效果;".l10n + tip.titleLabel2.text = "团队角色说明".l10n + tip.desLabel2.text = "破风手:以 “挡风减阻” 为核心,为后方队友节省体能;\n领骑者:把控整体节奏,稳定配速避免失衡;\n跟骑者:借助风阻优势储备体力,为关键阶段蓄力;\n冲刺手:聚焦终点前爆发,锁定最终成绩;\n配速员:作为 “节奏标杆”,助力队友校准目标; \n补给员:保障物资供应,维持运动续航; \n全能手:灵活补位,适配破风、带速等多元需求。".l10n + tip.tipsIcon.image = UIImage(named: "role_des") + tip.show() + } +} + +extension GroupRoleVC:UITableViewDelegate,UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return list.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = RoleCell.cell(withTableView: tableView, indexPath: indexPath) + cell.titleLabel.text = list[indexPath.row].name + cell.type.backgroundColor = list[indexPath.row].color + let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 52) + if indexPath.row == 0 { + cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 8, height: 8)) + } + else if indexPath.row == list.count - 1{ + cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.bottomLeft,.bottomRight], cornerRadii: CGSize(width: 8, height: 8)) + } + else{ + cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 0, height: 0)) + } + cell.selectedIcon.isHidden = list[indexPath.row] != currentType + return cell + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let data = list[indexPath.row] + currentType = data + tableView.reloadData() + } +} +class RoleCell: BaseTableViewCell { + let bgView = UIView() + let type = UIView() + let titleLabel = UILabel() + let selectedIcon = UIImageView() + let line = UIView() + + + 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([type,titleLabel,selectedIcon,line]) + + backgroundColor = .color(.bgColor) + contentView.backgroundColor = .clear + selectionStyle = .none + bgView.do { v in + v.backgroundColor = .color(.cellBg) + } + type.do { v in + v.yc.setCornerRadius(3) + v.backgroundColor = .red + } + 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().inset(18) + m.top.equalToSuperview() + m.bottom.equalToSuperview() + } + type.snp.makeConstraints { make in + make.left.equalToSuperview().inset(16) + make.centerY.equalToSuperview() + make.width.equalTo(6) + make.height.equalTo(16) + } + selectedIcon.snp.makeConstraints { (m) in + m.width.equalTo(21) + m.height.equalTo(16) + m.right.equalToSuperview().inset(22) + m.centerY.equalToSuperview() + } + titleLabel.snp.makeConstraints { (m) in + m.centerY.equalToSuperview() + m.left.equalToSuperview().offset(32) + 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/Sport/Sport/Group/Other/GroupTypeVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/Other/GroupTypeVC.swift new file mode 100644 index 0000000..bb7a0e6 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/Other/GroupTypeVC.swift @@ -0,0 +1,80 @@ +// +// GroupTypeVC.swift +// ShenYin +// +// Created by liangshi on 2026/2/6. +// + +import UIKit + +class GroupTypeVC: BaseVC{ + let tableView = UITableView(frame: .zero, style: .plain) + var list:[SportType] = SportType.all + var currentType = SportType.walk + let btn = CustomButton() + var didSelected:((SportType)->Void)? + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + override func setupViews() { + self.navBar.title = "团队类型".l10n + tableView.do { v in + v.backgroundColor = .color(.bgColor) + v.delegate = self + v.dataSource = self + v.rowHeight = 52 + v.separatorStyle = .none + v.contentInset = UIEdgeInsets(top: 10, left: 0, bottom: 0, right: 0) + v.register(SelectCell.self, forCellReuseIdentifier: SelectCell.reuseID) + } + view.addSubviews([tableView,btn]) + + btn.set(title: "确定".l10n, target: self, action: #selector(btnAction)) + } + override func setupLayout() { + tableView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalTo(kNavBtm) + } + btn.snp.makeConstraints { make in + make.width.equalTo(272) + make.height.equalTo(46) + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().inset(82) + } + } + @objc func btnAction(){ + didSelected?(currentType) + self.navigationController?.popViewController(animated: true) + } +} + +extension GroupTypeVC:UITableViewDelegate,UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return list.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = SelectCell.cell(withTableView: tableView, indexPath: indexPath) + cell.titleLabel.text = list[indexPath.row].name + let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 52) + if indexPath.row == 0 { + cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 8, height: 8)) + } + else if indexPath.row == list.count - 1{ + cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.bottomLeft,.bottomRight], cornerRadii: CGSize(width: 8, height: 8)) + } + else{ + cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 0, height: 0)) + } + cell.selectedIcon.isHidden = list[indexPath.row] != currentType + return cell + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let data = list[indexPath.row] + currentType = data + tableView.reloadData() + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Group/add/AddGroupVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/Group/add/AddGroupVC.swift new file mode 100644 index 0000000..a403111 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Group/add/AddGroupVC.swift @@ -0,0 +1,106 @@ +// +// AddGrpupVC.swift +// ShenYin +// +// Created by liangshi on 2026/2/6. +// + +import UIKit + +class AddGroupVC: BaseVC{ + let nameView = SportInputItem() + let typeView = SportSelectItem() + let desView = SportInputItem() + let okBtn = CustomButton() + var sportType:GroupRoleType = .none + + override func setupViews() { + navBar.do { v in + v.title = "加入团队".l10n + v.rightView.setImage(UIImage(named: "help_icon"), for: .normal) + v.rightView.isHidden = false + } + view.addSubviews([nameView,typeView,desView,okBtn]) + nameView.do { v in + v.yc.setCornerRadius(12) + v.titleLabel.text = "团队号".l10n + v.textField.text = "" + v.textField.attributedPlaceholder = NSAttributedString(string: "请输入团队号".l10n, attributes: [NSAttributedString.Key.foregroundColor: UIColor.color(.placeholder)]) + + } + typeView.do { v in + v.yc.setCornerRadius(12) + v.titleLabel.text = "团队角色".l10n + v.detailLabel.text = sportType.name + v.yc.addTap(target: self, action: #selector(typeAction)) + } + desView.do { v in + v.yc.setCornerRadius(12) + v.titleLabel.text = "打招呼内容".l10n + v.textField.attributedPlaceholder = NSAttributedString(string: "描述一下您的团".l10n, attributes: [NSAttributedString.Key.foregroundColor: UIColor.color(.placeholder)]) + v.maxLength = 50 + } + okBtn.set(title: "加入".l10n,style: .theme1, target: self, action: #selector(okAction)) + } + override func setupLayout() { + nameView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.top.equalToSuperview().inset(kNavBtm + 20) + make.height.equalTo(51) + } + + desView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.top.equalTo(nameView.snp.bottom).offset(10) + make.height.equalTo(65) + } + typeView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.top.equalTo(desView.snp.bottom).offset(10) + make.height.equalTo(51) + } + okBtn.snp.makeConstraints { make in + make.bottom.equalToSuperview().inset(80) + make.left.right.equalToSuperview().inset(51) + make.height.equalTo(46) + } + } + override func navRightViewTapped() { + let tip = GroupDesPopView() + tip.show() + } + @objc func typeAction(){ + let vc = GroupRoleVC() + vc.didSelected = {[weak self] value in + self?.sportType = value + self?.typeView.detailLabel.text = value.name + } + App.shared.goToPage(vc) + } + @objc func lightAction(){ + App.shared.goToPage(LightSetVC()) + } + @objc func okAction(){ + if nameView.isEmpty { + YLprogressHUD.showAutoHiden("团队名称不能为空".l10n) + return + } + + YLprogressHUD.showLoading() + var params:[String:Any] = [:] + params["id"] = nameView.value + params["requestText"] = desView.value + + let api = CommomApi.applyJoinGroup(param: params) + HttpClient.request(api){ (model:ResponseBaseModel, res) in + + if model.isSuccess{ + YLprogressHUD.hidden() + } + else{ + YLprogressHUD.showAutoHiden(model.msg) + } + } + } +} + diff --git a/ShenYin/ShenYin/UI/Sport/Sport/History/SportHistoryVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/History/SportHistoryVC.swift new file mode 100644 index 0000000..d64dece --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/History/SportHistoryVC.swift @@ -0,0 +1,124 @@ +// +// History.swift +// ShenYin +// +// Created by liangshi on 2026/2/25. +// + +import JXSegmentedView + + +enum SportHistoryType{ + case week + case month + case year + case all + + var name:String{ + switch self { + case .week: + return "周" + case .month: + return "月" + case .year: + return "年" + case .all: + return "总" + } + } + static var allData:[SportHistoryType] = [.week,.month,.year,.all] +} + +class SportHistoryVC: BaseVC { + private let segmentedDataSource = JXSegmentedTitleDataSource() + private let segmentedView = JXSegmentedView() + private lazy var listContainerView: JXSegmentedListContainerView! = { + let view = JXSegmentedListContainerView(dataSource: self, type: .scrollView) + return view + }() + + var list:[SportHistoryType] = SportHistoryType.allData + override func viewDidLoad() { + super.viewDidLoad() + + // AudioPlayView.shared.show() + self.view.addSubview(AudioPlayView.shared) + MapManager.shared.setupSdk() + } + + 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() + } + } + override func setupViews(){ + navBar.do { v in + v.title = "运动记录" + } + view.addSubview(segmentedView) + view.addSubview(listContainerView) + + 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(.theme) + segmentedView.indicators = [indicator] + + v.dataSource = segmentedDataSource + v.listContainer = listContainerView + } + } +} + +extension SportHistoryVC: JXSegmentedListContainerViewListDelegate { + func listView() -> UIView { + return view + } +} + +extension SportHistoryVC: 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 .week: + return SprotHistoryView() + case .month: + return SprotHistoryView() + case .year: + return SprotHistoryView() + case .all: + return SprotHistoryView() + } + + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/History/SprotHistoryView.swift b/ShenYin/ShenYin/UI/Sport/Sport/History/SprotHistoryView.swift new file mode 100644 index 0000000..bd99048 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/History/SprotHistoryView.swift @@ -0,0 +1,25 @@ +// +// SprotHistoryView.swift +// ShenYin +// +// Created by liangshi on 2026/2/25. +// + +import UIKit +import JXSegmentedView + +class SprotHistoryView: BaseView{ + + override func setupViews() { + + } + override func setupLayout() { + + } +} + +extension SprotHistoryView: JXSegmentedListContainerViewListDelegate { + func listView() -> UIView { + return self + } +} 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..2fb42fd --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Light/LightSetVC.swift @@ -0,0 +1,103 @@ +// +// 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. + + } + open override var preferredStatusBarStyle: UIStatusBarStyle { + return .lightContent + } + override func setupViews() { + navBar.do { v in + v.title = "灯带设置".l10n + v.titleLabel.textColor = .white + v.backgroundColor = .clear + v.leftView.setImage(UIImage(named: "arrow_back_white"), 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.image = UIImage(named: "guchuandao_icon") + } + stateView.do { v in + v.iSwitch.addTarget(self, action: #selector(stateAction), for: .valueChanged) + v.titleLabel.text = "灯带".l10n + } + modelView.do { v in + } + brightnessView.do { v in +// v.backgroundColor = .yellow + } + sysView.do { v in + v.line.isHidden = true + v.titleLabel.text = "同步至队员".l10n + } + stateAction() + } + 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(130.fitR) + make.width.equalTo(220.fitR) + make.centerX.equalToSuperview() + make.top.equalToSuperview().inset(40) + } + stateView.snp.makeConstraints { make in + make.width.equalTo(kScreenWid) + make.height.equalTo(60) + make.top.equalTo(icon.snp.bottom).offset(35) + 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..f6f5a06 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Light/ModeView.swift @@ -0,0 +1,165 @@ +// +// 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 + } + var mode:LightMode?{ + if normalItem.isSelected { + return .normal + } + if breathItem.isSelected { + return .breath + } + if flashItem.isSelected { + return .flash + } + return nil + } + + + 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)) + v.isSelected = true + } + 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..66a1d90 --- /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 + } + + iSwitch.do { v in + // v.backgroundColor = .red + v.isOn = true + } + + 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/CountdownView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Ready/CountdownView.swift new file mode 100644 index 0000000..5afe77e --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Ready/CountdownView.swift @@ -0,0 +1,76 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/1/21. +// + +extension ReadyVC{ + class CountdownView: BaseView { + let label = UILabel() + let skip = UILabel() + var timer:Timer! + var count = 3 + + override func setupViews() { + addSubviews([label,skip]) + label.do { v in + v.textColor = .color(.first) + v.font = .bold(210) + v.textAlignment = .center + v.text = "\(count)" + } + skip.do { v in + v.textColor = .color(.first) + v.font = .regular(14) + v.textAlignment = .center + v.text = "直接开始".l10n + v.yc.addTap(target: self, action: #selector(skipAction)) + + let line = UIView() + line.backgroundColor = .color(.first) + v.addSubview(line) + line.snp.makeConstraints { make in + make.height.equalTo(1) + make.left.right.equalToSuperview() + make.bottom.equalToSuperview().offset(4) + } + } + backgroundColor = .white + } + + override func setupLayout() { + label.snp.makeConstraints { make in + make.center.equalToSuperview() + } + skip.snp.makeConstraints { make in + make.bottom.equalToSuperview().inset(100) + make.centerX.equalToSuperview() + } + } + override func setupData() { + timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true) + } + @objc func timerAction(){ + count -= 1 + label.text = "\(count)" + print(count) + if count == 0 { + skipAction() + } + } + static func show(in view:UIView){ + let countView = CountdownView() + view.addSubview(countView) + countView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + @objc func skipAction(){ + timer.invalidate() + App.shared.goToPage(SportingVC()) + removeFromSuperview() + } + } + +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Ready/CustomMapView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Ready/CustomMapView.swift new file mode 100644 index 0000000..fbc7567 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Ready/CustomMapView.swift @@ -0,0 +1,12 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/1/21. +// + +class CustomMapView: BaseView { + + + +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Ready/DeviceStateView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Ready/DeviceStateView.swift new file mode 100644 index 0000000..06be847 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Ready/DeviceStateView.swift @@ -0,0 +1,57 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/2/25. +// + +class DeviceStateView: BaseView { + let deviceLabel = UILabel() + let deviceBg = UIView() + let stateLabel = UIView() + + + var value:String = ""{ + didSet{ + deviceLabel.text = value + } + } + + override func setupViews() { + addSubviews([deviceBg]) + deviceBg.addSubviews([stateLabel,deviceLabel]) + deviceBg.do { v in + v.backgroundColor = .rgb(0xF7F7F7 ) + v.yc.setCornerRadius(13) + } + stateLabel.do { v in + v.backgroundColor = .rgb(0x2BD84E ) + v.yc.setCornerRadius(2) + } + deviceLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(10) + v.textAlignment = .center + v.text = "Deep Lite2 Ultra" + } + } + + override func setupLayout() { + deviceBg.snp.makeConstraints { make in + make.height.equalTo(26) + make.top.equalToSuperview() + make.left.right.equalToSuperview() + } + stateLabel.snp.makeConstraints { make in + make.size.equalTo(4) + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(9) + } + deviceLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(18) + make.right.equalToSuperview().inset(8) + make.height.equalToSuperview() + } + } + } diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Ready/FooterView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Ready/FooterView.swift new file mode 100644 index 0000000..78b2034 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Ready/FooterView.swift @@ -0,0 +1,106 @@ +// +// FooterView.swift +// ShenYin +// +// Created by liangshi on 2026/1/21. +// + +extension ReadyVC{ + class FooterView: BaseView { + let targetBtn = ButtonItem() + let routeBtn = ButtonItem() + let goBtn = UIImageView() + + override func setupViews() { + addSubviews([targetBtn,goBtn,routeBtn]) + targetBtn.do { v in + v.titleLabel.text = "目标".l10n + v.icon.image = UIImage(named: "ready_target") + v.yc.setCornerRadius(18) + v.yc.addTap(target: self, action: #selector(targetAction)) + } + goBtn.do { v in + v.image = UIImage(named: "ready_go") + v.yc.addTap(target: self, action: #selector(goAction)) + } + routeBtn.do { v in + v.yc.setCornerRadius(18) + v.titleLabel.text = "路线".l10n + v.icon.image = UIImage(named: "ready_route") + v.yc.addTap(target: self, action: #selector(routeAction)) + } + } + + override func setupLayout() { + goBtn.snp.makeConstraints { make in + make.size.equalTo(90) + make.centerY.equalToSuperview() + make.centerX.equalTo(kScreenWid/2) + } + + targetBtn.snp.makeConstraints { make in + make.width.equalTo(90) + make.height.equalTo(36) + make.centerY.equalToSuperview() + make.right.equalTo(goBtn.snp.left).offset(-27) + } + routeBtn.snp.makeConstraints { make in + make.width.equalTo(90) + make.height.equalTo(36) + make.centerY.equalToSuperview() + make.left.equalTo(goBtn.snp.right).offset(27) + } + + } + + @objc func targetAction(){ + App.shared.goToPage(SportTargetVC()) + } + @objc func goAction(){ + if false { + CustomAlert.showWith(title: "完善数据".l10n, messgage: "完善你的身体档案,让运动数据更准确。".l10n,ok: "前往".l10n) { + + } okCallback: { + App.shared.goToPage(UserInfoVC()) + } + } + else{ + CountdownView.show(in: App.shared.window!) + } + + } + @objc func routeAction(){ + + } + + class ButtonItem: BaseView { + let icon = UIImageView() + let titleLabel = UILabel() + + override func setupViews() { + addSubviews([titleLabel,icon]) + icon.do { v in + } + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .regular(14) + } + backgroundColor = .white + } + + override func setupLayout() { + icon.snp.makeConstraints { make in + make.size.equalTo(24) + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(16) + } + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalTo(43) + } + } + } + + } + +} 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..d076f38 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Ready/Navbar.swift @@ -0,0 +1,110 @@ +// +// Navbar.swift +// ShenYin +// +// Created by liangshi on 2026/2/3. +// + +import UIKit + +extension ReadyVC{ + class Navbar: BaseView { + let backItem = BackItem() + let icon = UIImageView() + let groupBtn = UIButton() + let setBtn = UIButton() + + // + + override func setupViews() { + backgroundColor = .color(.bgColor) + addSubviews([backItem,icon,groupBtn,setBtn]) + backItem.do { v in + v.titleLabel.text = SportManager.shared.sportType.name + v.yc.addTap(target: self, action: #selector(backAction)) + } + 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) + } + backItem.snp.makeConstraints { make in + make.left.equalToSuperview() + make.centerY.equalTo(icon) + make.height.equalTo(32) + make.width.equalTo(100) + } + } + @objc func backAction(){ + App.shared.popPage() + } + @objc func groupAction(){ + App.shared.goToPage(SportSetVC()) + } + @objc func setAction(){ + App.shared.goToPage(SportSetVC()) + } + + class BackItem: BaseView { + let icon = UIImageView() + let titleLabel = UILabel() + + override func setupViews() { + addSubviews([titleLabel,icon]) + icon.do { v in + v.image = UIImage(named: "arrow_back") + } + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .bold(16) + } + } + + override func setupLayout() { + icon.snp.makeConstraints { make in + make.width.equalTo(10) + make.height.equalTo(19) + make.left.equalToSuperview().inset(18) + make.centerY.equalToSuperview() + } + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(42) + } + } + } + } + + +} + + 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..a73da88 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Ready/ReadyHeader.swift @@ -0,0 +1,107 @@ +// +// 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 = SportManager.shared.sportType.readyText + v.textAlignment = .left + } + totalLabel.do { v in + v.textColor = .color(.first) + v.font = .bold(20) + v.text = "00" + 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 new file mode 100644 index 0000000..a50f35c --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Ready/ReadyVC.swift @@ -0,0 +1,76 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/1/21. +// +import MAMapKit + +class ReadyVC: BaseVC { + let bar = Navbar() + let headerView = ReadyHeader() + var mapView:MAMapView! + let footerView = FooterView() + let deviceView = DeviceStateView() + + override func setupViews() { + navBar.do { v in + v.addSubview(bar) + } + + mapView = MAMapView(frame: .zero) + mapView.do { v in + v.delegate = self + v.showsUserLocation = true + v.zoomLevel = 16 + v.userTrackingMode = .followWithHeading + } + + + view.addSubviews([headerView,mapView,footerView]) + mapView.addSubview(deviceView) + + headerView.do { v in + v.backgroundColor = .clear + } + mapView.do { v in + v.yc.setCornerRadius(12) + } + footerView.do { v in + } + } + 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) + make.height.equalTo(100) + } + footerView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.bottom.equalToSuperview() + make.height.equalTo(200) + } + mapView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.top.equalTo(headerView.snp.bottom) + make.bottom.equalTo(footerView.snp.top) + } + deviceView.snp.makeConstraints { make in + make.top.right.equalToSuperview().inset(12) + make.height.equalTo(26) + } + } + override func setupData() { + + } + +} + +extension ReadyVC:MAMapViewDelegate{ + func mapViewRequireLocationAuth(_ locationManager: CLLocationManager!) { +// locationManager.requestAlwaysAuthorization() + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Root/DataView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Root/DataView.swift new file mode 100644 index 0000000..1b2c5c5 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Root/DataView.swift @@ -0,0 +1,136 @@ +// +// DataView.swift +// ShenYin +// +// Created by liangshi on 2026/1/21. +// + +extension SportRootView{ + class DataView: BaseView { + let header = headItem() + let line = UIView() + let lastView = ItemView() + + override func setupViews() { + backgroundColor = .color(.cellBg) + addSubviews([header,lastView,line]) + line.backgroundColor = .color(.separateLine) + header.do { v in + v.yc.addTap(target: self, action: #selector(allAction)) + } + lastView.do { v in + v.yc.addTap(target: self, action: #selector(lastAction)) + } + } + + override func setupLayout() { + header.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + make.height.equalTo(51) + } + lastView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.height.equalTo(72) + } + line.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(14) + make.height.equalTo(1) + make.bottom.equalTo(header) + } + } + @objc func allAction(){ + App.shared.goToPage(SportHistoryVC()) + } + @objc func lastAction(){ + App.shared.goToPage(SportDetailVC()) + + } + + class headItem: BaseView { + let icon = UIImageView() + let detailLabel = UILabel() + let titleLabel = UILabel() + + override func setupViews() { + yc.setCornerRadius(12) + addSubviews([icon,titleLabel,detailLabel]) + + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .medium(14) + v.text = "运动数据".l10n + } + detailLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(12) + v.text = "全部".l10n + } + icon.do { v in + v.image = UIImage(named: "arrow_2") + } + backgroundColor = .white + } + + override func setupLayout() { + + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(14) + } + detailLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().inset(27) + } + icon.snp.makeConstraints { make in + make.width.equalTo(7) + make.height.equalTo(13) + make.centerY.equalToSuperview() + make.right.equalToSuperview().inset(12) + } + } + } + + class ItemView: BaseView { + let icon = UIImageView() + let detailLabel = UILabel() + let titleLabel = UILabel() + + override func setupViews() { + yc.setCornerRadius(12) + addSubviews([icon,titleLabel,detailLabel]) + + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .medium(14) + v.text = "户外跑步 6.21公里" + } + detailLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(10) + v.text = "30分26秒" + } + icon.do { v in + v.image = UIImage(named: "add_2") + } + backgroundColor = .white + } + + override func setupLayout() { + + titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview().inset(16) + make.left.equalToSuperview().inset(64) + } + detailLabel.snp.makeConstraints { make in + make.bottom.equalToSuperview().inset(16) + make.left.equalTo(titleLabel) + } + icon.snp.makeConstraints { make in + make.size.equalTo(40) + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(14) + } + } + } + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Root/GroupView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Root/GroupView.swift new file mode 100644 index 0000000..620dc8a --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Root/GroupView.swift @@ -0,0 +1,102 @@ +// +// GroupView.swift +// ShenYin +// +// Created by liangshi on 2026/1/21. +// + +extension SportRootView{ + class GroupView: BaseView { + let creatGroup = ItemView() + let addGroup = ItemView() + let myGroup = ItemView() + + override func setupViews() { + addSubviews([creatGroup,addGroup,myGroup]) + creatGroup.do { v in + v.titleLabel.text = "创建团队".l10n + v.detailLabel.text = "创建我的团队".l10n + v.yc.addTap(target: self, action: #selector(creatAction)) + } + addGroup.do { v in + v.titleLabel.text = "加入团队".l10n + v.detailLabel.text = "加入已创建的团队".l10n + v.yc.addTap(target: self, action: #selector(joinAction)) + + } + myGroup.do { v in + v.titleLabel.text = "我的团队".l10n + v.detailLabel.text = "我创建和已加入的团队".l10n + v.icon.isHidden = true + v.yc.addTap(target: self, action: #selector(myAction)) + } + } + + override func setupLayout() { + creatGroup.snp.makeConstraints { make in + make.left.top.right.equalToSuperview() + make.height.equalTo(65) + } + addGroup.snp.makeConstraints { make in + make.left.bottom.equalToSuperview() + make.height.equalTo(65) + make.width.equalTo((kScreenWid - 36 - 10)/2) + } + myGroup.snp.makeConstraints { make in + make.right.bottom.equalToSuperview() + make.height.equalTo(addGroup) + make.left.equalTo(addGroup.snp.right).offset(10) + } + } + @objc func creatAction(){ + App.shared.goToPage(CreateGroupVC()) + } + @objc func joinAction(){ + App.shared.goToPage(AddGroupVC()) + } + @objc func myAction(){ + App.shared.goToPage(MyGroupVC()) + } + } + class ItemView: BaseView { + let icon = UIImageView() + let detailLabel = UILabel() + let titleLabel = UILabel() + + override func setupViews() { + backgroundColor = .color(.cellBg) + yc.setCornerRadius(12) + addSubviews([icon,titleLabel,detailLabel]) + + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .medium(14) + } + detailLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(10) + } + icon.do { v in + v.image = UIImage(named: "add_2") + } + backgroundColor = .white + } + + override func setupLayout() { + + titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview().inset(16) + make.left.equalToSuperview().inset(14) + } + detailLabel.snp.makeConstraints { make in + make.bottom.equalToSuperview().inset(16) + make.left.equalTo(titleLabel) + } + icon.snp.makeConstraints { make in + make.size.equalTo(36) + make.centerY.equalToSuperview() + make.right.equalToSuperview().inset(12) + } + } + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Root/PlanView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Root/PlanView.swift new file mode 100644 index 0000000..d29ffed --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Root/PlanView.swift @@ -0,0 +1,26 @@ +// +// PlanView.swift +// ShenYin +// +// Created by liangshi on 2026/1/21. +// + +extension SportRootView{ + class PlanView: BaseView { + let icon = ItemView() + + override func setupViews() { + addSubviews([icon]) + icon.do { v in + v.titleLabel.text = "智能运动计划建议".l10n + v.detailLabel.text = "AI规划个人定制运动计划".l10n + } + } + + override func setupLayout() { + icon.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Root/RouteView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Root/RouteView.swift new file mode 100644 index 0000000..dadc7a4 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Root/RouteView.swift @@ -0,0 +1,98 @@ +// +// RouteView.swift +// ShenYin +// +// Created by liangshi on 2026/1/21. +// + +extension SportRootView{ + class RouteView: BaseView { + let header = headItem() + let line = UIView() + let routeView1 = UIView() + let routeView2 = UIView() + + + override func setupViews() { + backgroundColor = .color(.cellBg) + addSubviews([header,routeView1,routeView2,line]) + line.backgroundColor = .color(.separateLine) + + routeView1.do { v in + v.backgroundColor = .lightGray + v.yc.setCornerRadius(12) + } + routeView2.do { v in + v.backgroundColor = .lightGray + v.yc.setCornerRadius(12) + } + } + + override func setupLayout() { + header.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + make.height.equalTo(51) + } + routeView1.snp.makeConstraints { make in + make.left.bottom.equalToSuperview().inset(14) + make.height.equalTo(96) + make.width.equalTo(((kScreenWid - 64 - 10)/2)) + } + routeView2.snp.makeConstraints { make in + make.right.bottom.equalToSuperview().inset(14) + make.height.equalTo(96) + make.width.equalTo(((kScreenWid - 64 - 10)/2)) + } + line.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(14) + make.height.equalTo(1) + make.bottom.equalTo(header) + } + } + class headItem: BaseView { + let icon = UIImageView() + let detailLabel = UILabel() + let titleLabel = UILabel() + + override func setupViews() { + yc.setCornerRadius(12) + addSubviews([icon,titleLabel,detailLabel]) + + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .medium(14) + v.text = "路线探索".l10n + } + detailLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(12) + v.text = "全部".l10n + } + icon.do { v in + v.image = UIImage(named: "arrow_2") + } + backgroundColor = .white + } + + override func setupLayout() { + + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(14) + } + detailLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().inset(27) + } + icon.snp.makeConstraints { make in + make.width.equalTo(7) + make.height.equalTo(13) + make.centerY.equalToSuperview() + make.right.equalToSuperview().inset(12) + } + } + } + + + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Root/SportRootView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Root/SportRootView.swift new file mode 100644 index 0000000..29ea350 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Root/SportRootView.swift @@ -0,0 +1,66 @@ +// +// SportVC.swift +// ShenYin +// +// Created by liangshi on 2026/1/20. +// + +import UIKit +import JXSegmentedView + +class SportRootView: BaseView{ + let sportType = SportTypeView() + let dataView = DataView() + let groupView = GroupView() + let planView = PlanView() + let routeView = RouteView() + + override func setupViews() { + addSubviews([sportType,dataView,groupView,planView,routeView]) + dataView.do { v in + v.yc.setCornerRadius(12) + } + groupView.do { v in + v.yc.setCornerRadius(12) + } + planView.do { v in + v.yc.setCornerRadius(12) + } + routeView.do { v in + v.yc.setCornerRadius(12) + } + } + override func setupLayout() { + sportType.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalToSuperview().inset(20) + make.height.equalTo(80) + } + dataView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.top.equalTo(sportType.snp.bottom).offset(10) + make.height.equalTo(123) + } + groupView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.top.equalTo(dataView.snp.bottom).offset(10) + make.height.equalTo(140) + } + planView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.top.equalTo(groupView.snp.bottom).offset(10) + make.height.equalTo(65) + } + routeView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.top.equalTo(planView.snp.bottom).offset(10) + make.height.equalTo(180) + } + } +} + +extension SportRootView: JXSegmentedListContainerViewListDelegate { + func listView() -> UIView { + return self + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Root/SportTypeView.swift b/ShenYin/ShenYin/UI/Sport/Sport/Root/SportTypeView.swift new file mode 100644 index 0000000..3ca5d5f --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Root/SportTypeView.swift @@ -0,0 +1,134 @@ +// +// SportTypeView.swift +// ShenYin +// +// Created by liangshi on 2026/1/20. +// +import HandyJSON + +enum SportType:String,HandyJSONEnum{ + case run = "running" + case ride = "cycling" + case walk = "walking" + + var name:String{ + switch self { + case .run: + return "户外跑步" + case .ride: + return "户外骑行" + case .walk: + return "健走" + } + } + var icon:String{ + switch self { + case .run: + return "sport_run" + case .ride: + return "sport_ride" + case .walk: + return "sport_walk" + } + } + var readyText:String{ + switch self { + case .run: + return "累积跑步距离".l10n + case .ride: + return "累积骑行距离".l10n + case .walk: + return "累积健走距离".l10n + } + } + + static var all:[SportType] = [.run,.ride,.walk] +} + +class SportTypeView: BaseView { + let run = SportTypeItem() + let ride = SportTypeItem() + let walk = SportTypeItem() + + override func setupViews() { + addSubviews([run,ride,walk]) + run.do { v in + v.type = .run + v.yc.addTap(target: self, action: #selector(runAction)) + } + ride.do { v in + v.type = .ride + v.yc.addTap(target: self, action: #selector(rideAction)) + } + walk.do { v in + v.type = .walk + v.yc.addTap(target: self, action: #selector(walkAction)) + } + } + + override func setupLayout() { + run.snp.makeConstraints { make in + make.left.equalToSuperview().inset(38) + make.width.equalTo(50) + make.height.equalTo(80) + make.top.equalToSuperview() + } + ride.snp.makeConstraints { make in + make.left.equalTo(run.snp.right).offset(32) + make.width.equalTo(50) + make.height.equalTo(80) + make.top.equalToSuperview() + } + walk.snp.makeConstraints { make in + make.left.equalTo(ride.snp.right).offset(32) + make.width.equalTo(50) + make.height.equalTo(80) + make.top.equalToSuperview() + } + } + @objc func runAction(){ + SportManager.shared.sportType = .run + App.shared.goToPage(ReadyVC()) + } + @objc func rideAction(){ + SportManager.shared.sportType = .ride + App.shared.goToPage(ReadyVC()) + } + @objc func walkAction(){ + SportManager.shared.sportType = .walk + App.shared.goToPage(ReadyVC()) + } +} + +class SportTypeItem: BaseView { + private let icon = UIImageView() + private let titleLabel = UILabel() + var type:SportType = .run{ + didSet{ + titleLabel.text = type.name + icon.image = UIImage(named: type.icon) + } + } + override func setupViews() { + addSubviews([titleLabel,icon]) + icon.do { v in + + } + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .regular(10) + } + } + + override func setupLayout() { + icon.snp.makeConstraints { make in + make.size.equalTo(50) + make.top.equalToSuperview() + make.centerX.equalToSuperview() + } + titleLabel.snp.makeConstraints { make in + make.top.equalTo(icon.snp.bottom).offset(5) + make.centerX.equalTo(icon) + } + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetCell.swift b/ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetCell.swift new file mode 100644 index 0000000..ce6aeb3 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetCell.swift @@ -0,0 +1,321 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/1/23. +// + + +public enum SportSetCellType: Int { + case light + case voiceReport + case lcReport + case lcReportRate + case xlfdReport + case guideReport + case guideReportSet + case metronome + case metronomeRate + case metronomeAudio + case background + case permisson + case realData + + var title:String{ + switch self { + case .light: + return "灯带设置".l10n + case .voiceReport: + return "语音播报".l10n + case .lcReport: + return "里程播报".l10n + case .lcReportRate: + return "里程播报频次".l10n + case .xlfdReport: + return "训练分段播报".l10n + case .guideReport: + return "指导播报".l10n + case .guideReportSet: + return "指导语音设置".l10n + case .metronome: + return "节拍器".l10n + case .metronomeRate: + return "节拍器频率".l10n + case .metronomeAudio: + return "节拍器声音".l10n + case .background: + return "后台保护".l10n + case .permisson: + return "运动权限".l10n + case .realData: + return "运动实况".l10n + } + } + var detail:String{ + switch self { + case .lcReport: + return "当前已跑里程播报".l10n + case .xlfdReport: + return "训练分段开始和结束提示音".l10n + case .guideReport: + return "跑步过程中的指导语音".l10n + case .realData: + return "组内成员基本运动信息实时显示".l10n + default: + return "" + } + } + var showSwitch:Bool{ + switch self { + case .voiceReport,.lcReport,.xlfdReport,.guideReport,.metronome,.background,.realData: + return true + default: + return false + } + } +} + +protocol SportSetCellDelegate:NSObject{ + func sportSetCellSwitchAction(cell:SportSetCell) +} +class SportSetCell: BaseTableViewCell { + let bgView = UIView() + let titleLabel = UILabel() + let detailLabel = UILabel() + let valueLabel = UILabel() + let iSwitch = UISwitch() + let nextIcon = UIImageView(image: UIImage(named: "arrow_next")) + let line = UIView() + weak var delegate:SportSetCellDelegate? + var model:SportSetModel!{ + didSet{ + switch type { + case .light: + break + case .voiceReport: + iSwitch.isOn = model.voiceReport + case .lcReport: + iSwitch.isOn = model.lcReport + + case .lcReportRate: + valueLabel.text = model.lcReportRate + case .xlfdReport: + iSwitch.isOn = model.xlfdReport + case .guideReport: + iSwitch.isOn = model.guideReport + case .guideReportSet: + valueLabel.text = model.guideReportSet + case .metronome: + iSwitch.isOn = model.metronome + case .metronomeRate: + valueLabel.text = model.metronomeRate + case .metronomeAudio: + valueLabel.text = model.metronomeAudio + case .background: + iSwitch.isOn = model.background + case .permisson: + break + case .realData: + iSwitch.isOn = model.realData + } + + if type == .lcReport { + line.isHidden = model.lcReport + } + else if type == .guideReport { + line.isHidden = model.guideReport + } + else if type == .metronome { + line.isHidden = model.metronome + } + else if type == .permisson || type == .realData || type == .metronomeRate{ + line.isHidden = true + } + else{ + line.isHidden = false + } + } + } + + var type:SportSetCellType = .light{ + didSet{ + titleLabel.text = type.title + detailLabel.text = type.detail + iSwitch.isHidden = !type.showSwitch + nextIcon.isHidden = !iSwitch.isHidden + + if type.detail.isEmpty { + titleLabel.snp.remakeConstraints { (m) in + m.centerY.equalToSuperview() + m.left.equalToSuperview().offset(16) + } + } + else{ + titleLabel.snp.remakeConstraints { (m) in + m.top.equalToSuperview().inset(16) + m.left.equalToSuperview().offset(16) + } + } + + if type == .metronomeRate || type == .metronomeAudio { + valueLabel.isHidden = false + } + else{ + valueLabel.isHidden = true + } + } + } + + + 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,detailLabel,iSwitch,nextIcon,valueLabel,line]) + + backgroundColor = .color(.bgColor) + contentView.backgroundColor = .clear + selectionStyle = .none + bgView.do { v in + v.backgroundColor = .color(.cellBg) + v.yc.addTap(target: self, action: #selector(tapAction)) + } + titleLabel.do { (v) in + v.textColor = .color(.first) + v.font = .bold(14) + v.textAlignment = .left + } + detailLabel.do { (v) in + v.textColor = .color(.secondary) + v.font = .regular(10) + v.textAlignment = .left + } + iSwitch.do { v in +// v.backgroundColor = .red + v.addTarget(self, action: #selector(swicthAction), for: .valueChanged) + } + valueLabel.do { v in + v.textColor = .color(.secondary) + v.font = .regular(14) + v.textAlignment = .right + } + line.do { v in + v.backgroundColor = .color(.separateLine) + } + } + + override func setupLayout() { + bgView.snp.makeConstraints { (m) in + m.left.right.equalToSuperview().inset(18) + m.top.equalToSuperview() + m.bottom.equalToSuperview() + } + titleLabel.snp.makeConstraints { (m) in + m.top.equalToSuperview().inset(16) + m.left.equalToSuperview().offset(16) + } + detailLabel.snp.makeConstraints { (m) in + m.bottom.equalToSuperview().inset(16) + m.left.equalToSuperview().offset(16) + } + iSwitch.snp.makeConstraints { (m) in + m.right.equalToSuperview().inset(15) + m.centerY.equalToSuperview() + } + nextIcon.snp.makeConstraints { (m) in + m.width.equalTo(8) + m.height.equalTo(12) + m.right.equalToSuperview().inset(15) + m.centerY.equalToSuperview() + } + valueLabel.snp.makeConstraints { (m) in + m.centerY.equalToSuperview() + m.right.equalToSuperview().inset(38) + m.height.equalToSuperview() + m.left.equalToSuperview().inset(90) + } + line.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.bottom.equalToSuperview() + make.height.equalTo(0.4) + } + } + @objc func tapAction(){ + switch type { + case .light: + App.shared.goToPage(LightSetVC()) + case .lcReportRate: + lcReportAction() + break + case .guideReportSet: + guideReportSetAction() + break + case .metronomeRate: + metronomeRateAction() + break + case .metronomeAudio: + metronomeAudioAction() + break + + case .background: + backgroundAction() + break + default: + break + } + } + func lcReportAction(){ + let list = Array(stride(from: 500, to: 1500, by: 500)).map { String($0) + "m" } + let v = CustomPicker2(title: "选择播报间距".l10n, list:list ) + v.selectedIndex = 50 + v.show() + v.didSelected = {[weak self] (index,value) in + guard let self = self else { return } + + } + } + func guideReportSetAction(){ + let list = [ + CustomPickerModel(title: "详细".l10n, detail: "1公里配速、总平均配速、1公里用时、总运动时长".l10n), + CustomPickerModel(title: "简洁".l10n, detail: "1公里配速、1公里用时".l10n), + CustomPickerModel(title: "极简".l10n, detail: "总距离、总时长".l10n)] + let v = CustomPicker4(title: "指导语音密度".l10n, list:list ) + v.selectedIndex = 0 + v.show() + v.didSelected = {[weak self] (index,value) in + guard let self = self else { return } + + } + } + func metronomeRateAction(){ + let list = Array(stride(from: 100, to: 250, by: 1)).map { String($0) + "次/分钟".l10n } + let v = CustomPicker2(title: "节拍器频率".l10n, list:list ) + v.selectedIndex = 50 + v.show() + v.didSelected = {[weak self] (index,value) in + guard let self = self else { return } + + } + } + func metronomeAudioAction(){ + let list = ["默认".l10n,"清脆".l10n,"重击".l10n] + let v = CustomPicker2(title: "节拍器声音".l10n, list:list ) + v.selectedIndex = 50 + v.show() + v.didSelected = {[weak self] (index,value) in + guard let self = self else { return } + + } + } + func backgroundAction(){ + + } + @objc func swicthAction(){ + delegate?.sportSetCellSwitchAction(cell: self) + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetModel.swift b/ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetModel.swift new file mode 100644 index 0000000..0310fa4 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetModel.swift @@ -0,0 +1,22 @@ +// +// SportSetModel.swift +// ShenYin +// +// Created by liangshi on 2026/1/30. +// + +class SportSetModel{ + var light = "" + var voiceReport = true + var lcReport = false + var lcReportRate = "" + var xlfdReport = false + var guideReport = false + var guideReportSet = "" + var metronome = false + var metronomeRate = "" + var metronomeAudio = "" + var background = false + var permisson = "" + var realData = false +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetVC.swift new file mode 100644 index 0000000..4347064 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetVC.swift @@ -0,0 +1,149 @@ +// +// SportSetVC.swift +// ShenYin +// +// Created by liangshi on 2026/1/23. +//,, + + +import UIKit +class SportSetVC: BaseVC{ + let tableView = UITableView(frame: .zero, style: .plain) + var list:[[SportSetCellType]] = [[.light],[.voiceReport],[.lcReport],[.xlfdReport],[.guideReport],[.metronome],[.background],[.permisson],[.realData]] + let model = SportSetModel() + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + override func setupViews() { + self.navBar.title = "设置".l10n + tableView.do { v in + v.backgroundColor = .color(.bgColor) + v.delegate = self + v.dataSource = self + v.rowHeight = 65 + v.sectionHeaderHeight = 52 + v.separatorStyle = .none + v.register(SportSetCell.self, forCellReuseIdentifier: SportSetCell.reuseID) + v.register(CustomSectionHeader.self, forHeaderFooterViewReuseIdentifier: CustomSectionHeader.reuseID) + if #available(iOS 15.0, *) { + tableView.sectionHeaderTopPadding = 0 + } + } + + view.addSubview(tableView) + } + override func setupLayout() { + tableView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalTo(kNavBtm) + } + } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) +// CacheManager.shared.user.tokenIsValid(){[weak self] sucess in +// self?.header.updateUI() +// } + } +} + +extension SportSetVC:UITableViewDelegate,UITableViewDataSource { + func numberOfSections(in tableView: UITableView) -> Int { + return list.count + } + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return list[section].count + } + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + if section == list.count - 1 { + let view = CustomSectionHeader.header(withTableView: tableView) + view.titleLabel.text = "组队设置".l10n + view.titleLabel.textColor = .color(.secondary) + return view + } + else{ + return nil + } + } + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + if section == list.count - 1 { + return 52 + } + else{ + return 0 + } + } + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = SportSetCell.cell(withTableView: tableView, indexPath: indexPath) + cell.type = list[indexPath.section][indexPath.row] + cell.model = model + cell.delegate = self + let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 65) + if cell.type == .light { + cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 12, height: 12)) + } + else { + if cell.type == .realData{ + cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 12, height: 12)) + } + else if cell.type == .permisson { + cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.bottomLeft,.bottomRight], cornerRadii: CGSize(width: 12, height: 12)) + } + else{ + cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 0, height: 0)) + } + } + + return cell + } +// func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { +// let data = list[indexPath.section][indexPath.row] +// switch data { +// case .light: +// +// default: +// break +// } +// } +} +extension SportSetVC:SportSetCellDelegate { + func sportSetCellSwitchAction(cell:SportSetCell){ + let state = cell.iSwitch.isOn + switch cell.type { + case .voiceReport: + model.voiceReport = state + tableView.reloadData() + break + case .lcReport: + model.lcReport = state + list[2] = state ? [.lcReport,.lcReportRate]:[.lcReport] + tableView.reloadData() + break + case .xlfdReport: + model.xlfdReport = state + tableView.reloadData() + break + case .guideReport: + model.guideReport = state + list[4] = state ? [.guideReport,.guideReportSet]:[.guideReport] + tableView.reloadData() + break + case .metronome: + model.metronome = state + list[5] = state ? [.metronome,.metronomeRate,.metronomeAudio]:[.metronome] + tableView.reloadData() + break + case .background: + model.background = state + tableView.reloadData() + break + case .realData: + model.realData = state + tableView.reloadData() + break + default: + break + } + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/CadenceView.swift b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/CadenceView.swift new file mode 100644 index 0000000..8e41a47 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/CadenceView.swift @@ -0,0 +1,7 @@ +// +// CadenceView.swift +// ShenYin +// +// Created by liangshi on 2026/2/10. +// + diff --git a/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/ElevationView.swift b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/ElevationView.swift new file mode 100644 index 0000000..d323eea --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/ElevationView.swift @@ -0,0 +1,7 @@ +// +// elevationView.swift +// ShenYin +// +// Created by liangshi on 2026/2/10. +// + diff --git a/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/FeelView.swift b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/FeelView.swift new file mode 100644 index 0000000..0d7a8ed --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/FeelView.swift @@ -0,0 +1,57 @@ +// +// FeelView.swift +// ShenYin +// +// Created by liangshi on 2026/2/10. +// + +class FeelView: BaseView { + let header = SportDetailTitleView() + + override func setupViews() { + backgroundColor = .white + addSubviews([header]) + header.do { v in + v.titleLabel.text = "运动感受".l10n + v.icon.image = UIImage(named: "sport_feel") + } + } + + override func setupLayout() { + header.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.height.equalTo(55) + make.left.right.equalToSuperview() + } + + } +} +class SportDetailTitleView: BaseView { + let icon = UIImageView() + let titleLabel = UILabel() + + override func setupViews() { + addSubviews([icon,titleLabel]) + + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .medium(16) + } + icon.do { v in + v.image = UIImage(named: "") + } + } + + override func setupLayout() { + + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(48) + } + icon.snp.makeConstraints { make in + make.size.equalTo(24) + make.centerY.equalToSuperview() + make.left.equalToSuperview().inset(18) + } + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/PacePerView.swift b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/PacePerView.swift new file mode 100644 index 0000000..7945489 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/PacePerView.swift @@ -0,0 +1,7 @@ +// +// PacePerView.swift +// ShenYin +// +// Created by liangshi on 2026/2/10. +// + diff --git a/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/PaceView.swift b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/PaceView.swift new file mode 100644 index 0000000..9c69b83 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/PaceView.swift @@ -0,0 +1,7 @@ +// +// SpeepView.swift +// ShenYin +// +// Created by liangshi on 2026/2/10. +// + diff --git a/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/SportDetailBoard.swift b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/SportDetailBoard.swift new file mode 100644 index 0000000..0b88799 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/SportDetailBoard.swift @@ -0,0 +1,309 @@ +//// +//// Untitled.swift +//// ShenYin +//// +//// Created by liangshi on 2026/2/5. +//// +// +import UIKit + +extension SportDetailVC{ + class SportDetailBoard: BaseView { + let topIcon = UIImageView() + let header = UILabel() + let distanceItem = DistanceItem() + let item1 = LabelItem() + let item2 = LabelItem() + let item3 = LabelItem() + let item4 = LabelItem() + let item5 = LabelItem() + let item6 = LabelItem() + let item7 = LabelItem() + let item8 = LabelItem() + let item9 = LabelItem() + func updateUI(){ + if SportManager.shared.sportType == .walk{ + header.do { v in + v.text = "健走".l10n + } + distanceItem.do { v in + v.title = SportManager.shared.distanceStr + } + item1.do { v in + v.title = SportManager.shared.durationStr + v.detail = "时长".l10n + } + item2.do { v in + v.title = SportManager.shared.peisuStr + v.detail = "平均配速".l10n + } + item3.do { v in + v.title = SportManager.shared.calorieStr + v.detail = "运动消耗".l10n + } + item4.do { v in + v.title = SportManager.shared.bushuStr + v.detail = "步数".l10n + } + item5.do { v in + v.title = SportManager.shared.avgBuFuStr + v.detail = "平均步幅".l10n + } + item6.do { v in + v.title = SportManager.shared.avgBuPinStr + v.detail = "平均步频".l10n + } + item7.do { v in + v.title = SportManager.shared.upStr + v.detail = "积累爬升".l10n + } + item8.do { v in + v.title = SportManager.shared.downStr + v.detail = "积累下降".l10n + } + + } + else if SportManager.shared.sportType == .run{ + header.do { v in + v.text = "户外跑步".l10n + } + distanceItem.do { v in + v.title = SportManager.shared.distanceStr + } + item1.do { v in + v.title = SportManager.shared.durationStr + v.detail = "时长".l10n + } + item2.do { v in + v.title = SportManager.shared.peisuStr + v.detail = "平均配速".l10n + } + item3.do { v in + v.title = SportManager.shared.calorieStr + v.detail = "运动消耗".l10n + } + item4.do { v in + v.title = SportManager.shared.bushuStr + v.detail = "步数".l10n + } + item5.do { v in + v.title = SportManager.shared.avgBuFuStr + v.detail = "平均步幅".l10n + } + item6.do { v in + v.title = SportManager.shared.avgBuPinStr + v.detail = "平均步频".l10n + } + item7.do { v in + v.title = SportManager.shared.upStr + v.detail = "积累爬升".l10n + } + item8.do { v in + v.title = SportManager.shared.downStr + v.detail = "积累下降".l10n + } + + } + else if SportManager.shared.sportType == .ride{ + header.do { v in + v.text = "户外骑行".l10n + } + distanceItem.do { v in + v.title = SportManager.shared.distanceStr + } + item1.do { v in + v.title = SportManager.shared.durationStr + v.detail = "时长".l10n + } + item2.do { v in + v.title = SportManager.shared.peisuStr + v.detail = "平均配速".l10n + } + item3.do { v in + v.title = SportManager.shared.calorieStr + v.detail = "运动消耗".l10n + } + item4.do { v in + v.title = SportManager.shared.bushuStr + v.detail = "步数".l10n + } + item5.do { v in + v.title = SportManager.shared.avgBuFuStr + v.detail = "平均步幅".l10n + } + item6.do { v in + v.title = SportManager.shared.avgBuPinStr + v.detail = "平均步频".l10n + } + item7.do { v in + v.title = SportManager.shared.upStr + v.detail = "积累爬升".l10n + } + item8.do { v in + v.title = SportManager.shared.downStr + v.detail = "积累下降".l10n + } + + } + } + override func setupViews() { + backgroundColor = .white + addSubviews([topIcon,header,distanceItem,item1,item2,item3,item4,item5,item6,item7,item8,item9]) + topIcon.do { v in + v.backgroundColor = .color(.secondary) + v.yc.setCornerRadius(12) + } + header.do { v in + v.font = .medium(16) + v.textColor = .color(.first) + } + item9.do { v in + v.isHidden = true + } + updateUI() + } + + override func setupLayout() { + topIcon.snp.makeConstraints { make in + make.width.equalTo(42) + make.height.equalTo(42) + make.top.equalToSuperview().inset(18) + make.right.equalToSuperview().inset(18) + } + header.snp.makeConstraints { make in + make.top.leading.equalToSuperview().inset(18) + } + distanceItem.snp.makeConstraints { make in + make.left.equalToSuperview().inset(18) + make.top.equalToSuperview().inset(50) +// make.height.equalTo(64) + } + item1.snp.makeConstraints { make in + make.left.equalToSuperview().inset(18) + make.top.equalToSuperview().inset(130) + make.height.equalTo(50) + make.width.equalTo(kScreenWid/3) + } + item2.snp.makeConstraints { make in + make.centerX.equalToSuperview().offset(20) + make.width.top.height.equalTo(item1) + } + item3.snp.makeConstraints { make in + make.right.equalToSuperview() + make.width.top.height.equalTo(item1) + } + item4.snp.makeConstraints { make in + make.top.equalToSuperview().inset(200) + make.width.left.height.equalTo(item1) + } + item5.snp.makeConstraints { make in + make.centerX.equalTo(item2) + make.width.top.height.equalTo(item4) + } + item6.snp.makeConstraints { make in + make.right.equalToSuperview() + make.width.height.equalTo(item1) + make.top.equalTo(item4) + } + item7.snp.makeConstraints { make in + make.top.equalToSuperview().inset(270) + make.width.left.height.equalTo(item1) + } + item8.snp.makeConstraints { make in + make.centerX.equalTo(item2) + make.width.top.height.equalTo(item7) + } + item9.snp.makeConstraints { make in + make.right.equalToSuperview() + make.width.height.equalTo(item1) + make.top.equalTo(item7) + } + } + + class LabelItem: BaseView { + let detailLabel = UILabel() + let titleLabel = UILabel() + + var title:String = ""{ + didSet{ + titleLabel.text = title + } + } + var detail:String = ""{ + didSet{ + detailLabel.text = detail + } + } + + override func setupViews() { + addSubviews([titleLabel,detailLabel]) + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .semiBold(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() + } + } + } + class DistanceItem: BaseView { + let detailLabel = UILabel() + let titleLabel = UILabel() + + var title:String = ""{ + didSet{ + titleLabel.text = title + } + } + var detail:String = ""{ + didSet{ + detailLabel.text = detail + } + } + + override func setupViews() { + addSubviews([titleLabel,detailLabel]) + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .bold(48) + v.textAlignment = .left + } + detailLabel.do { v in + v.textColor = .color(.first) + v.font = .bold(20) + v.textAlignment = .left + v.text = "公里".l10n + } + } + + override func setupLayout() { + titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview().inset(0) + make.left.equalToSuperview() + } + detailLabel.snp.makeConstraints { make in + make.bottom.equalTo(titleLabel.snp.bottom).inset(8) + make.left.equalTo(titleLabel.snp.right).offset(10) + } + } + } + } + + + +} + diff --git a/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/SportDetailVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/SportDetailVC.swift new file mode 100644 index 0000000..3c758ef --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/SportDetailVC.swift @@ -0,0 +1,99 @@ +// +// SportDetailVC.swift +// ShenYin +// +// Created by liangshi on 2026/2/5. +// + +import MAMapKit + +class SportDetailVC: BaseVC { + let scrollView = UIScrollView() + var mapView:MAMapView! + let detailView = SportDetailBoard() + let pace = FeelView() + let pacePer = FeelView() + let cadence = FeelView() + let elevation = FeelView() + let feel = FeelView() + + + override func setupViews() { + MapManager.shared.setupSdk() + navBar.do { v in + v.title = "运动数据".l10n + } + + + mapView = MAMapView(frame: .zero) + mapView.do { v in + v.delegate = self + v.showsUserLocation = true + v.zoomLevel = 16 + v.userTrackingMode = .followWithHeading + } + view.addSubview(scrollView) + scrollView.addSubviews([mapView,detailView,pace,pacePer,cadence,elevation,feel]) + + scrollView.do { v in + + } + detailView.do { v in + v.yc.setCorners(withRoundedRect: CGRect(x: 0, y: 0, width: kScreenWid, height: 460), roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 20, height: 20)) + } + } + override func setupLayout() { + scrollView.snp.makeConstraints { make in + make.top.equalToSuperview().inset(kNavBtm) + make.left.equalToSuperview() + make.width.equalTo(kScreenWid) + make.height.equalTo(kScreenHei - kNavBtm) + } + mapView.snp.makeConstraints { make in + make.width.equalTo(kScreenWid) + make.left.top.equalToSuperview() + make.height.equalTo(300) + } + detailView.snp.makeConstraints { make in + make.left.equalToSuperview() + make.width.equalTo(kScreenWid) + make.top.equalTo(mapView.snp.bottom) + make.height.equalTo(342) + } + pace.snp.makeConstraints { make in + make.left.width.equalTo(detailView) + make.top.equalTo(detailView.snp.bottom) + make.height.equalTo(100) + } + pacePer.snp.makeConstraints { make in + make.left.width.equalTo(detailView) + make.top.equalTo(pace.snp.bottom) + make.height.equalTo(100) + } + cadence.snp.makeConstraints { make in + make.left.width.equalTo(detailView) + make.top.equalTo(pacePer.snp.bottom) + make.height.equalTo(100) + } + elevation.snp.makeConstraints { make in + make.left.width.equalTo(detailView) + make.top.equalTo(cadence.snp.bottom) + make.height.equalTo(100) + } + feel.snp.makeConstraints { make in + make.left.width.equalTo(detailView) + make.top.equalTo(elevation.snp.bottom) + make.height.equalTo(100) + make.bottom.equalToSuperview() + } + } + override func setupData() { + + } +} + +extension SportDetailVC:MAMapViewDelegate{ + func mapViewRequireLocationAuth(_ locationManager: CLLocationManager!) { + // locationManager.requestAlwaysAuthorization() + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/StepRateView.swift b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/StepRateView.swift new file mode 100644 index 0000000..7913a83 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/StepRateView.swift @@ -0,0 +1,7 @@ +// +// stepRateView.swift +// ShenYin +// +// Created by liangshi on 2026/2/10. +// + diff --git a/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/StrideLengthView.swift b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/StrideLengthView.swift new file mode 100644 index 0000000..3266d83 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/SportDetail/StrideLengthView.swift @@ -0,0 +1,7 @@ +// +// strideLengthView.swift +// ShenYin +// +// Created by liangshi on 2026/2/10. +// + 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..74d6349 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Sporting/DetailBoard.swift @@ -0,0 +1,377 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/2/3. +// + +import UIKit + +extension SportingVC{ + class DetailBoard: BaseView { + let topIcon = UIImageView() + let header = headerItem() + let distanceItem = DistanceItem() + let item1 = LabelItem() + let item2 = LabelItem() + let item3 = LabelItem() + let item4 = LabelItem() + let btnView = ButtonView() + func updateUI(){ + if SportManager.shared.sportType == .walk{ + header.do { v in + v.title = "健走".l10n + } + distanceItem.do { v in + v.title = SportManager.shared.distanceStr + } + item1.do { v in + v.title = SportManager.shared.durationStr + v.detail = "时长".l10n + } + item2.do { v in + v.title = SportManager.shared.peisuStr + v.detail = "配速".l10n + } + item3.do { v in + v.title = SportManager.shared.calorieStr + v.detail = "卡路里".l10n + } + item4.do { v in + v.title = SportManager.shared.bushuStr + v.detail = "步数".l10n + } + } + else if SportManager.shared.sportType == .run{ + header.do { v in + v.title = "户外跑步".l10n + } + distanceItem.do { v in + v.title = SportManager.shared.distanceStr + } + item1.do { v in + v.title = SportManager.shared.durationStr + v.detail = "时长".l10n + } + item2.do { v in + v.title = SportManager.shared.peisuStr + v.detail = "配速".l10n + } + item3.do { v in + v.title = SportManager.shared.calorieStr + v.detail = "卡路里".l10n + } + item4.do { v in + v.title = SportManager.shared.bushuStr + v.detail = "步数".l10n + } + } + else if SportManager.shared.sportType == .ride{ + header.do { v in + v.title = "户外骑行".l10n + } + distanceItem.do { v in + v.title = SportManager.shared.distanceStr + } + item1.do { v in + v.title = SportManager.shared.durationStr + v.detail = "时长".l10n + } + item2.do { v in + v.title = SportManager.shared.speedStr + v.detail = "时速".l10n + } + item3.do { v in + v.title = SportManager.shared.calorieStr + v.detail = "卡路里".l10n + } + item4.do { v in + + } + } + } + override func setupViews() { + backgroundColor = .white + addSubviews([topIcon,header,distanceItem,item1,item2,item3,item4,btnView]) + topIcon.do { v in + v.backgroundColor = .color(.secondary) + v.yc.setCornerRadius(2) + } + updateUI() + } + + override func setupLayout() { + topIcon.snp.makeConstraints { make in + make.width.equalTo(50) + make.height.equalTo(4) + make.top.equalToSuperview().inset(6) + make.centerX.equalToSuperview() + } + header.snp.makeConstraints { make in + make.top.equalToSuperview().inset(28) + make.left.right.equalToSuperview() + make.height.equalTo(25) + } + distanceItem.snp.makeConstraints { make in + make.left.equalToSuperview().inset(18) + make.top.equalToSuperview().inset(100) + make.height.equalTo(50) + } + item1.snp.makeConstraints { make in + make.left.equalToSuperview().inset(18) + make.top.equalToSuperview().inset(180) + make.height.equalTo(50) + } + item2.snp.makeConstraints { make in + make.left.equalToSuperview().inset(kScreenWid/2 + 18) + make.top.equalTo(item1.snp.top) + make.height.equalTo(50) + } + item3.snp.makeConstraints { make in + make.left.equalToSuperview().inset(18) + make.top.equalToSuperview().inset(250) + make.height.equalTo(50) + } + item4.snp.makeConstraints { make in + make.left.equalToSuperview().inset(kScreenWid/2 + 18) + make.top.equalTo(item3.snp.top) + make.height.equalTo(50) + } + btnView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.height.equalTo(140) + } + } + + + class ButtonView: BaseView { + let okBtn = UIButton(type: .custom) + let lockBtn = UIButton(type: .custom) + let setBtn = UIButton(type: .custom) + + let lockedBtn = UIButton(type: .custom) + let stopBtn = UIButton(type: .custom) + let continueBtn = UIButton(type: .custom) + + override func setupViews() { +// backgroundColor = .red + addSubviews([okBtn,lockBtn,setBtn,lockedBtn,stopBtn,continueBtn]) + okBtn.do { v in + v.setImage(UIImage(named: "btn_pause"), for: .normal) + v.addTarget(self, action: #selector(okAction), for: .touchUpInside) + } + lockBtn.do { v in + v.setImage(UIImage(named: "btn_lock"), for: .normal) + v.addTarget(self, action: #selector(lockAction), for: .touchUpInside) + } + setBtn.do { v in + v.setImage(UIImage(named: "btn_set"), for: .normal) + v.addTarget(self, action: #selector(setAction), for: .touchUpInside) + } + + lockedBtn.do { v in + v.setImage(UIImage(named: "btn_locked"), for: .normal) + v.addTarget(self, action: #selector(lockedAction), for: .touchUpInside) + v.isHidden = true + } + stopBtn.do { v in + v.setImage(UIImage(named: "btn_stop"), for: .normal) + v.addTarget(self, action: #selector(stopAction), for: .touchUpInside) + v.isHidden = true + } + continueBtn.do { v in + v.setImage(UIImage(named: "btn_continue"), for: .normal) + v.addTarget(self, action: #selector(continueAction), for: .touchUpInside) + v.isHidden = true + } + } + + override func setupLayout() { + okBtn.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().inset(24) + make.size.equalTo(90) + } + lockBtn.snp.makeConstraints { make in + make.left.equalToSuperview().inset(46) + make.centerY.equalTo(okBtn) + make.size.equalTo(50) + } + setBtn.snp.makeConstraints { make in + make.right.equalToSuperview().inset(46) + make.centerY.equalTo(okBtn) + make.size.equalTo(50) + } + + lockedBtn.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().inset(24) + make.size.equalTo(90) + } + continueBtn.snp.makeConstraints { make in + make.left.equalToSuperview().inset(55) + make.bottom.equalToSuperview().inset(24) + make.size.equalTo(90) + } + stopBtn.snp.makeConstraints { make in + make.right.equalToSuperview().inset(55) + make.bottom.equalToSuperview().inset(24) + make.size.equalTo(90) + } + } + @objc func okAction(){ + SportManager.shared.pause() + continueBtn.isHidden = false + stopBtn.isHidden = false + + okBtn.isHidden = true + setBtn.isHidden = true + lockBtn.isHidden = true + } + @objc func lockAction(){ + okBtn.isHidden = true + setBtn.isHidden = true + lockBtn.isHidden = true + lockedBtn.isHidden = false + } + @objc func setAction(){ + + } + @objc func lockedAction(){ + okBtn.isHidden = false + setBtn.isHidden = false + lockBtn.isHidden = false + lockedBtn.isHidden = true + } + @objc func stopAction(){ + SportManager.shared.stop() + App.shared.goToPage(SportDetailVC()) + } + @objc func continueAction(){ + SportManager.shared.resume() + okBtn.isHidden = false + setBtn.isHidden = false + lockBtn.isHidden = false + + continueBtn.isHidden = true + stopBtn.isHidden = true + } + } + class headerItem: BaseView { + let deviceView = DeviceStateView() + let titleLabel = UILabel() + + var title:String = ""{ + didSet{ + titleLabel.text = title + } + } + override func setupViews() { + addSubviews([titleLabel,deviceView]) + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .bold(16) + v.textAlignment = .left + } + } + + override func setupLayout() { + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview().inset(0) + make.left.equalToSuperview().inset(18) + } + deviceView.snp.makeConstraints { make in + make.height.equalTo(26) + make.left.equalToSuperview().inset(94) + make.centerY.equalToSuperview() + } + } + } + + + class LabelItem: BaseView { + let detailLabel = UILabel() + let titleLabel = UILabel() + + var title:String = ""{ + didSet{ + titleLabel.text = title + } + } + var detail:String = ""{ + didSet{ + detailLabel.text = detail + } + } + + 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() + } + } + } + class DistanceItem: BaseView { + let detailLabel = UILabel() + let titleLabel = UILabel() + + var title:String = ""{ + didSet{ + titleLabel.text = title + } + } + var detail:String = ""{ + didSet{ + detailLabel.text = detail + } + } + + override func setupViews() { + addSubviews([titleLabel,detailLabel]) + titleLabel.do { v in + v.textColor = .color(.first) + v.font = .bold(48) + v.textAlignment = .left + } + detailLabel.do { v in + v.textColor = .color(.first) + v.font = .bold(20) + v.textAlignment = .left + v.text = "公里".l10n + } + } + + override func setupLayout() { + titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview().inset(0) + make.left.equalToSuperview() + } + detailLabel.snp.makeConstraints { make in + make.bottom.equalToSuperview().inset(0) + make.left.equalTo(titleLabel.snp.right).offset(10) + } + } + } + } + + + +} 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..1fb9a22 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Sporting/SimpleBoard.swift @@ -0,0 +1,90 @@ +// +// 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() + + func updateUI(){ + distanceItem.titleLabel.text = SportManager.shared.distanceStr + durationItem.titleLabel.text = SportManager.shared.durationStr + } + + 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(.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/SportingVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/Sporting/SportingVC.swift new file mode 100644 index 0000000..e5e808a --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Sporting/SportingVC.swift @@ -0,0 +1,164 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/2/3. +// + +import MAMapKit + +class SportingVC: BaseVC { + var line: MAPolyline! + var mapView:MAMapView! + let simpleView = SimpleBoard() + let detailView = DetailBoard() + + var trackOverlay: MAPolyline? + + override func setupViews() { + mapView = MAMapView(frame: .zero) + mapView.delegate = self + mapView.showsUserLocation = true + mapView.zoomLevel = 16 + mapView.userTrackingMode = .followWithHeading +// mapView.desiredAccuracy = kCLLocationAccuracyBest + mapView.desiredAccuracy = kCLLocationAccuracyKilometer + mapView.distanceFilter = 0.001 + + + view.addSubviews([mapView,simpleView,detailView]) + mapView.do { v in + + } + simpleView.do { v in + v.yc.setCorners(withRoundedRect: CGRect(x: 0, y: 0, width: kScreenWid, height: 100), roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 20, height: 20)) + v.yc.addTap(target: self, action: #selector(simpleTapAction)) + } + detailView.do { v in + v.yc.setCorners(withRoundedRect: CGRect(x: 0, y: 0, width: kScreenWid, height: 460), roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 20, height: 20)) + 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).offset(20) + } + } + override func setupData() { + NotificationManager.add(self, selector: #selector(updateLocation(_:)), forName: .updateLocation) + SportManager.shared.start() + } + @objc func updateLocation(_ noti:Notification){ + if let userLocation = mapView.userLocation { + let latitude = userLocation.location.coordinate.latitude + let longitude = userLocation.location.coordinate.longitude + let altitude = userLocation.location.altitude + let point = TrackPoint(latitude: latitude, longitude: longitude, altitude: altitude) + + + SportManager.shared.pointList.append(point) + updateUI() + } + + simpleView.updateUI() + detailView.updateUI() + } + @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).offset(20) + } + } + func showDetail(){ + mapView.snp.remakeConstraints { make in + make.left.right.top.equalToSuperview() + make.bottom.equalTo(detailView.snp.top).offset(20) + } + } + func updateUI(){ + // 移除旧轨迹 + if let oldTrackOverlay = trackOverlay { + mapView.remove(oldTrackOverlay) + } + + var trackCoordinates = [CLLocationCoordinate2D]() + for point in SportManager.shared.pointList { + let coordinate = CLLocationCoordinate2D(latitude: point.latitude, longitude: point.longitude) + trackCoordinates.append(coordinate) + } + + + + // 只有当有足够的点时才绘制轨迹线 + if trackCoordinates.count >= 2 { + let coordinatesPointer = UnsafeMutablePointer.allocate(capacity: trackCoordinates.count) + coordinatesPointer.initialize(from: trackCoordinates, count: trackCoordinates.count) + + // 创建新的轨迹线 + let newTrackOverlay = MAPolyline(coordinates: coordinatesPointer, count: UInt(trackCoordinates.count)) + + // 添加到地图 + mapView.add(newTrackOverlay) + + // 保存引用 + trackOverlay = newTrackOverlay + +// // 调整地图视野以适应轨迹 +// mapView.showOverlays([newTrackOverlay], edgePadding: UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50), animated: true) + } + } +} + +extension SportingVC:MAMapViewDelegate{ + func mapViewRequireLocationAuth(_ locationManager: CLLocationManager!) { + // locationManager.requestAlwaysAuthorization() + } + func mapView(_ mapView: MAMapView!, rendererFor overlay: MAOverlay!) -> MAOverlayRenderer! { + + if (overlay.isKind(of: MAPolyline.self)) + { + let renderer = MAPolylineRenderer(polyline: overlay as! MAPolyline) + renderer?.lineWidth = 8.0 + + + return renderer; + } + + return nil; + } + + func mapView(_ mapView: MAMapView!, didUpdate userLocation: MAUserLocation!, updatingLocation: Bool) { + Logger.debug(userLocation.location) + +// let latitude = userLocation.location.coordinate.latitude +// let longitude = userLocation.location.coordinate.longitude +// let altitude = userLocation.location.altitude +// let point = TrackPoint(latitude: latitude, longitude: longitude, altitude: altitude) +// +// +// SportManager.shared.pointList.append(point) +// updateUI() + } +} 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..79dcfa0 --- /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/Sport/Tools/IBSpeaker.swift b/ShenYin/ShenYin/UI/Sport/Sport/Tools/IBSpeaker.swift new file mode 100644 index 0000000..8175151 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Tools/IBSpeaker.swift @@ -0,0 +1,87 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/2/26. +// + + +import AVFoundation + +class IBSpeaker: NSObject, AVSpeechSynthesizerDelegate { + static let shared = IBSpeaker() + // MARK: - Properties + private let synthesizer = AVSpeechSynthesizer() + private var textBuffer: String = "" // 用于存储待播放的文字 + private var isPlaying: Bool = false // 是否正在播放 + + + // MARK: - Initializer + override init() { + super.init() + synthesizer.delegate = self + } + + // MARK: - Public Methods + + /// 添加文字并开始播放 + func appendText(_ text: String) { + textBuffer += text + + // 如果没有正在播放,则开始播放 + if !isPlaying { + playNextChunk() + } + } + + /// 停止播放并清空缓冲区 + func stop() { + synthesizer.stopSpeaking(at: .immediate) + textBuffer = "" + isPlaying = false + } + + // MARK: - Private Methods + + /// 播放缓冲区中的下一段文字 + private func playNextChunk() { + guard !textBuffer.isEmpty else { + isPlaying = false + return + } + + // 每次播放一段文字(可以根据需要调整) + let chunkSize = textBuffer.count // 每次播放的字符数 + let endIndex = textBuffer.index(textBuffer.startIndex, offsetBy: min(chunkSize, textBuffer.count)) + let chunk = String(textBuffer[.. Double { + guard totalKm > 0 && totalTimeSec > 0 else { return 0.0 } + let totalHour = Double(totalTimeSec) / 3600.0 // 秒转小时 + let avgSpeed = totalKm / totalHour + return Double((df1.string(from: NSNumber(value: avgSpeed)) ?? "0.0")) ?? 0.0 + } + + // ====================== 1. 总里程(同之前,改用TrackPoint) ====================== + static func calculateTotalDistanceMeter(pointList: [TrackPoint]?) -> Double { + guard let pointList = pointList, pointList.count >= 2 else { return 0.0 } + var total: Double = 0 + for i in 0.. Int { + guard totalDistanceMeter > 0 && stride > 0 else { return 0 } + // 总里程 ÷ 步幅 = 步数(四舍五入取整) + return Int(round(totalDistanceMeter / Double(stride))) + } + + static func calculateTotalDistanceKm(pointList: [TrackPoint]?) -> Double { + let meter = calculateTotalDistanceMeter(pointList: pointList) + return Double((df.string(from: NSNumber(value: meter / 1000)) ?? "0.00")) ?? 0.0 + } + + // 两点球面距离(米) + private static func calculateDistanceBetweenTwoPoints(lat1: Double, lng1: Double, lat2: Double, lng2: Double) -> Double { + let radLat1 = lat1 * Double.pi / 180.0 + let radLng1 = lng1 * Double.pi / 180.0 + let radLat2 = lat2 * Double.pi / 180.0 + let radLng2 = lng2 * Double.pi / 180.0 + let dLat = radLat2 - radLat1 + let dLng = radLng2 - radLng1 + + let a = sin(dLat / 2) * sin(dLat / 2) + + cos(radLat1) * cos(radLat2) * + sin(dLng / 2) * sin(dLng / 2) + let c = 2 * atan2(sqrt(a), sqrt(1 - a)) + return EARTH_RADIUS * c + } + + // ====================== 2. 配速(分/公里) ====================== + static func calculatePace(totalTimeSec: Int, totalDistanceKm: Double) -> String { + guard totalDistanceKm > 0 && totalTimeSec > 0 else { return "--'--''" } + let secPerKm = Double(totalTimeSec) / totalDistanceKm + let min = Int(secPerKm / 60) + let sec = Int(secPerKm.truncatingRemainder(dividingBy: 60)) + return String(format: "%d'%02d''", min, sec) + } + + static func calculatePaceNum(totalTimeSec: Int, totalDistanceKm: Double) -> Double { + guard totalDistanceKm > 0 && totalTimeSec > 0 else { return 0 } + return Double(totalTimeSec) / totalDistanceKm + } + + // ====================== 3. 卡路里 ====================== + static func calculateCalorie(totalDistanceKm: Double, weightKg: Float, sportType: Int) -> Int { + guard totalDistanceKm > 0 && weightKg > 0 else { return 0 } + let coef: Double + switch sportType { + case 1: coef = 1.0; break // 跑 + case 2: coef = 0.8; break // 慢跑 + default: coef = 0.5; break // 走 + } + return Int(round(Double(weightKg) * totalDistanceKm * coef)) + } + + // ====================== 4. 步频(步/分钟) ====================== + static func calculateCadence(stepCount: Int, totalTimeSec: Int) -> Double { + guard stepCount > 0 && totalTimeSec > 0 else { return 0 } + let min = Double(totalTimeSec) / 60.0 + let cadence = Double(stepCount) / min + return Double((df1.string(from: NSNumber(value: cadence)) ?? "0.0")) ?? 0.0 + } + + // ====================== 5. 步幅(米/步) ====================== + static func calculateStrideLength(totalDistanceMeter: Double, stepCount: Int) -> Double { + guard totalDistanceMeter > 0 && stepCount > 0 else { return 0 } + let stride = totalDistanceMeter / Double(stepCount) + return Double((df1.string(from: NSNumber(value: stride)) ?? "0.0")) ?? 0.0 + } + + // ====================== 6. 海拔变化(累计上升、累计下降、净变化) ====================== + static func calculateAltitudeChange(pointList: [TrackPoint]?) -> AltitudeResult { + let res = AltitudeResult() + guard let pointList = pointList,!pointList.isEmpty else { return res } + + let firstAlt = pointList[0].altitude + let lastAlt = pointList[pointList.count - 1].altitude + res.netChange = lastAlt - firstAlt + + var up: Double = 0, down: Double = 0 + for i in 1.. 0.5 { + up += delta + } else if delta < -0.5 { + down -= delta + } + } + res.totalUp = Double((df1.string(from: NSNumber(value: up)) ?? "0.0")) ?? 0.0 + res.totalDown = Double((df1.string(from: NSNumber(value: down)) ?? "0.0")) ?? 0.0 + res.netChange = Double((df1.string(from: NSNumber(value: res.netChange)) ?? "0.0")) ?? 0.0 + return res + } + + +} + + + +// 存储轨迹点,包含经纬度 + 海拔 +class TrackPoint { + var latitude: Double + var longitude: Double + var altitude: Double // 海拔(米) + + init(latitude: Double, longitude: Double, altitude: Double) { + self.latitude = latitude + self.longitude = longitude + self.altitude = altitude + } + } +// 海拔结果实体 +class AltitudeResult { + var totalUp: Double = 0.0 // 累计上升(米) + var totalDown: Double = 0.0 // 累计下降(米) + var netChange: Double = 0.0 // 净变化(米) + } diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Tools/SportManager.swift b/ShenYin/ShenYin/UI/Sport/Sport/Tools/SportManager.swift new file mode 100644 index 0000000..ba6a749 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Sport/Tools/SportManager.swift @@ -0,0 +1,126 @@ +// +// SportManager.swift +// ShenYin +// +// Created by liangshi on 2026/2/10. +// + +class SportManager { + static let shared = SportManager() + var sportType = SportType.ride + + var pointList:[TrackPoint] = [TrackPoint]() + + var timer:Timer? + /// 运动时间 + var duration = 0 + /// 运动距离 + var distance = 0.0 + /// 当前配速 + var peisu = 0.0 + /// 步数 + var bushu = 0 + + private var hasPlayed100M = false + private var hasPlayed500M = false + private var hasPlayed1000M = false + + func start(){ + duration = 0 + if let t = timer,t.isValid { + t.invalidate() + } + timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true) + } + func stop(){ + if let t = timer,t.isValid { + t.invalidate() + } + SportManager.shared.play(text: "运动已结束") + } + func pause(){ + if let t = timer,t.isValid { + t.invalidate() + } + SportManager.shared.play(text: "运动已暂停") + } + func resume(){ + if let t = timer,t.isValid { + t.invalidate() + } + timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true) + } + + @objc func timerAction(){ + self.distance = SportCalculateUtil.calculateTotalDistanceMeter(pointList: pointList) + if self.distance == 0 { + self.peisu = 0 + self.bushu = 0 + } + else{ + self.peisu = (Double(duration)/60.0) / (Double(distance) / 1000.0) + self.bushu = Int(distance/0.6) + } + + duration += 1 + NotificationManager.post(type: .updateLocation, objtect: nil) + + if self.distance > 100.0 && hasPlayed100M == false{ + hasPlayed100M = true + SportManager.shared.play(text: "您已经跑了100米,继续加油") + } + else if self.distance > 500.0 && hasPlayed500M == false{ + hasPlayed500M = true + SportManager.shared.play(text: "您已经跑了500米,继续加油") + } + else if self.distance > 1000.0 && hasPlayed1000M == false { + hasPlayed1000M = true + SportManager.shared.play(text: "您已经跑了1000米,继续加油") + } + } + + var durationStr:String{ + return YLToos.formatSeconds(duration) + } + var distanceStr:String { + return String(format: "%.2f", distance/1000.0) + } + var peisuStr:String { + let minutes = Int(peisu) + let seconds = Int(peisu.truncatingRemainder(dividingBy: 1)*60) + return String(format: "%d'%02d''", minutes, seconds) + } + var calorieStr:String { + let value = SportCalculateUtil.calculateCalorie(totalDistanceKm: distance, weightKg: 50, sportType: 1) + return "\(value)" + } + var bushuStr:String { + return "\(bushu)" + } + var speedStr:String { + let value = distance/Double(duration) + return String(format: "%.1f", value) + } + var avgBuFuStr:String { + let value = distance/Double(bushu) + return String(format: "%.1f", value) + } + var avgBuPinStr:String { + let bupin = Double(bushu)/(Double(duration)/60.0) + return "\(Int(bupin))" + } + var upStr:String { + let value = SportCalculateUtil.calculateAltitudeChange(pointList: pointList) + return String(format: "%.1f", value.totalUp) + } + var downStr:String { + let value = SportCalculateUtil.calculateAltitudeChange(pointList: pointList) + return String(format: "%.1f", value.totalDown) + } +} +extension SportManager{ + func play(text:String ){ + IBSpeaker.shared.appendText(text) + } +} + diff --git a/ShenYin/ShenYin/UI/Sport/SportHomeVC.swift b/ShenYin/ShenYin/UI/Sport/SportHomeVC.swift new file mode 100644 index 0000000..cedefa5 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/SportHomeVC.swift @@ -0,0 +1,131 @@ +// +// SportHomeVC.swift +// ShenYin +// +// Created by liangshi on 2026/1/20. +// + +import JXSegmentedView + + +enum SportHomeType{ + case sport + case record + case translate + case music + + var name:String{ + switch self { + case .sport: + return "运动" + case .record: + return "录音" + case .translate: + return "翻译" + case .music: + return "音乐" + } + } + static var all:[SportHomeType] = [.sport,.record,.translate,.music] +} + +class SportHomeVC: BaseVC { + let icon = UIImageView() + private let segmentedDataSource = JXSegmentedTitleDataSource() + private let segmentedView = JXSegmentedView() + private lazy var listContainerView: JXSegmentedListContainerView! = { + let view = JXSegmentedListContainerView(dataSource: self, type: .scrollView) + return view + }() + + var list:[SportHomeType] = SportHomeType.all + override func viewDidLoad() { + super.viewDidLoad() + + // AudioPlayView.shared.show() + self.view.addSubview(AudioPlayView.shared) + MapManager.shared.setupSdk() + } + + override func setupLayout(){ + icon.snp.makeConstraints { make in + make.size.equalTo(32) + make.bottom.equalToSuperview().inset(10) + make.centerX.equalToSuperview() + } + 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() + } + } + override func setupViews(){ + icon.do { v in + v.image = UIImage(named: "sport_ai") + } + navBar.addSubview(icon) + view.addSubview(segmentedView) + view.addSubview(listContainerView) + + 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(.theme) + segmentedView.indicators = [indicator] + + v.dataSource = segmentedDataSource + v.listContainer = listContainerView + } + } +} + +extension SportHomeVC: JXSegmentedListContainerViewListDelegate { + func listView() -> UIView { + return view + } +} + +extension SportHomeVC: 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 .sport: + return SportRootView() + case .record: + return RecordRootView() + case .translate: + return TranslateRootView() + case .music: + return MusicRootView() + } + + } +} diff --git a/ShenYin/ShenYin/UI/Sport/Translate/TranslateRootView.swift b/ShenYin/ShenYin/UI/Sport/Translate/TranslateRootView.swift new file mode 100644 index 0000000..f321243 --- /dev/null +++ b/ShenYin/ShenYin/UI/Sport/Translate/TranslateRootView.swift @@ -0,0 +1,49 @@ +// +// TranslateVC.swift +// ShenYin +// +// Created by liangshi on 2026/1/20. +// + +import UIKit +import JXSegmentedView + +class TranslateRootView: BaseView{ + let content = SprotRocrodView() + + override func setupViews() { + addSubviews([content]) + content.do { v in + v.header.titleLabel.text = "翻译".l10n + v.item1.titleLabel.text = "对话翻译".l10n + v.item1.icon.image = UIImage(named: "sport_record") + v.item2.titleLabel.text = "同传听译".l10n + v.item2.icon.image = UIImage(named: "sport_call") + v.header.yc.addTap(target: self, action: #selector(historyAction)) + v.item1.yc.addTap(target: self, action: #selector(dialogTranslateAction)) + v.item2.yc.addTap(target: self, action: #selector(tctyAction)) + } + } + + override func setupLayout() { + content.snp.makeConstraints { make in + make.left.right.top.equalToSuperview().inset(18) + make.height.equalTo(209) + } + } + @objc func historyAction(){ + App.shared.goToPage(HistoryRecordVC()) + } + @objc func dialogTranslateAction(){ + CCPage.gotoDialogTranslate() + } + @objc func tctyAction(){ + CCPage.gotoTcty() + } +} + +extension TranslateRootView: JXSegmentedListContainerViewListDelegate { + func listView() -> UIView { + return self + } +} diff --git a/ShenYin/ShenYin/UI/common/NotificationManager.swift b/ShenYin/ShenYin/UI/common/NotificationManager.swift index 21b1732..7596e8d 100644 --- a/ShenYin/ShenYin/UI/common/NotificationManager.swift +++ b/ShenYin/ShenYin/UI/common/NotificationManager.swift @@ -11,6 +11,7 @@ enum NotificationType:String{ case deviceValidChange case login case logout + case updateLocation var value:String{ switch self { @@ -20,6 +21,8 @@ enum NotificationType:String{ return "login" case .logout: return "logout" + case .updateLocation: + return "updateLocation" default: return "unknown" } @@ -36,9 +39,9 @@ class NotificationManager { NotificationCenter.default.post(name: Notification.Name(type.value), object: objtect, userInfo:nil) } - @objc func handleNotification(_ notification: Notification) { - if let data = notification.userInfo?["data"] as? String { - print("Received data: \(data)") - } - } +// @objc func handleNotification(_ notification: Notification) { +// if let data = notification.userInfo?["data"] as? String { +// print("Received data: \(data)") +// } +// } } diff --git a/ShenYin/podfile b/ShenYin/podfile index 72904a7..d068380 100644 --- a/ShenYin/podfile +++ b/ShenYin/podfile @@ -23,7 +23,7 @@ target 'ShenYin' do pod 'SwiftDate'#时间转换 pod 'SVProgressHUD'#蒙版 pod 'MBProgressHUD'#蒙版 - pod 'Kingfisher', '~> 6.0'#图片下载K + pod 'Kingfisher'#图片下载 pod 'FSPagerView' pod "TabPageViewController" #tabpage pod 'JXSegmentedView' @@ -51,9 +51,9 @@ target 'ShenYin' do pod 'HorizonCalendar'#日历 #高德地图 -# pod 'AMap3DMap-NO-IDFA', '10.0.700' -# pod 'AMapLocation-NO-IDFA' -# pod 'AMapSearch-NO-IDFA', '9.7.0' + pod 'AMap3DMap-NO-IDFA', '10.0.700' + pod 'AMapLocation-NO-IDFA' + pod 'AMapSearch-NO-IDFA', '9.7.0' end -- 2.17.1