Преглед изворни кода

4.3a改造:先去掉TSSmalCoacopods 库

kln пре 1 недеља
родитељ
комит
68f3db60cf
81 измењених фајлова са 8521 додато и 519 уклоњено
  1. 414 8
      AIPlayRingtones.xcodeproj/project.pbxproj
  2. 0 3
      AIPlayRingtones/AppDelegate.swift
  3. 2 2
      AIPlayRingtones/AppPage/APAudioToRingVC/View/ASRTRStyleView.swift
  4. 2 2
      AIPlayRingtones/AppPage/APRingTonesVC/APRingStyleVC/APRingStyleVC.swift
  5. 2 2
      AIPlayRingtones/AppPage/APRingTonesVC/APRingTonesVC/View/ASDurationColView.swift
  6. 1 1
      AIPlayRingtones/AppPage/ASRingGeneratorVC/ASRingGeneratorBaseVC.swift
  7. 30 0
      AIPlayRingtones/AppPage/SimpleWebViewController/SimpleWebViewController.h
  8. 213 0
      AIPlayRingtones/AppPage/SimpleWebViewController/SimpleWebViewController.m
  9. 143 0
      AIPlayRingtones/Classes/BaseClass/TSBaseCollectionCell.swift
  10. 52 0
      AIPlayRingtones/Classes/BaseClass/TSBaseModel.swift
  11. 173 0
      AIPlayRingtones/Classes/BaseClass/TSBaseNavigationBarView.swift
  12. 110 0
      AIPlayRingtones/Classes/BaseClass/TSBaseNavigationC.swift
  13. 40 0
      AIPlayRingtones/Classes/BaseClass/TSBaseTabViewCell.swift
  14. 224 0
      AIPlayRingtones/Classes/BaseClass/TSBaseVC.swift
  15. 41 0
      AIPlayRingtones/Classes/BaseClass/TSBaseView.swift
  16. 24 0
      AIPlayRingtones/Classes/BaseClass/TSBaseViewModel.swift
  17. 152 0
      AIPlayRingtones/Classes/BaseClass/TSBasicItemModel.swift
  18. 78 0
      AIPlayRingtones/Classes/Ex/AVAsset+Ex.swift
  19. 47 0
      AIPlayRingtones/Classes/Ex/Array+Ex.swift
  20. 32 0
      AIPlayRingtones/Classes/Ex/Bundle+Ex.swift
  21. 55 0
      AIPlayRingtones/Classes/Ex/CGFloat+Ex.swift
  22. 49 0
      AIPlayRingtones/Classes/Ex/Date+Ex.swift
  23. 61 0
      AIPlayRingtones/Classes/Ex/Dictionary+Ex.swift
  24. 98 0
      AIPlayRingtones/Classes/Ex/Int+Ex.swift
  25. 370 0
      AIPlayRingtones/Classes/Ex/NSString+Ex.swift
  26. 47 0
      AIPlayRingtones/Classes/Ex/SwiftUI/Color+Ex.swift
  27. 16 0
      AIPlayRingtones/Classes/Ex/SwiftUI/Font+Ex.swift
  28. 34 0
      AIPlayRingtones/Classes/Ex/SwiftUI/Text+Ex.swift
  29. 49 0
      AIPlayRingtones/Classes/Ex/UIApplication+Ex.swift
  30. 164 0
      AIPlayRingtones/Classes/Ex/UIButton+Ex.swift
  31. 89 0
      AIPlayRingtones/Classes/Ex/UIColor+Ex.swift
  32. 128 0
      AIPlayRingtones/Classes/Ex/UIDevice+Extension.swift
  33. 66 0
      AIPlayRingtones/Classes/Ex/UIFont+Ex.swift
  34. 319 0
      AIPlayRingtones/Classes/Ex/UIImage+Ex.swift
  35. 231 0
      AIPlayRingtones/Classes/Ex/UIImageView+Ex.swift
  36. 154 0
      AIPlayRingtones/Classes/Ex/UILabel+Ex.swift
  37. 43 0
      AIPlayRingtones/Classes/Ex/UIScreen.swift
  38. 196 0
      AIPlayRingtones/Classes/Ex/UITableView+Ex.swift
  39. 7 0
      AIPlayRingtones/Classes/Ex/UITextView+Ex.swift
  40. 422 0
      AIPlayRingtones/Classes/Ex/UIView+Ex.swift
  41. 132 0
      AIPlayRingtones/Classes/Ex/UIView+Frame.swift
  42. 73 0
      AIPlayRingtones/Classes/Ex/UIView+Rotating.swift
  43. 154 0
      AIPlayRingtones/Classes/Ex/UIViewController+Ex.swift
  44. 30 0
      AIPlayRingtones/Classes/Ex/UserDefault+Ex.swift
  45. 167 0
      AIPlayRingtones/Classes/GlobalImports/GlobalImports.swift
  46. 147 0
      AIPlayRingtones/Classes/Tool/PhotoManager.swift
  47. 63 0
      AIPlayRingtones/Classes/Tool/SVProgressHUD+Ex.swift
  48. 151 0
      AIPlayRingtones/Classes/Tool/TSBusinessFileManager.swift
  49. 78 0
      AIPlayRingtones/Classes/Tool/TSCollectionViewObserver.swift
  50. 182 0
      AIPlayRingtones/Classes/Tool/TSCommonTool/TSCommonTool+Down.swift
  51. 89 0
      AIPlayRingtones/Classes/Tool/TSCommonTool/TSCommonTool+MultDown.swift
  52. 191 0
      AIPlayRingtones/Classes/Tool/TSCommonTool/TSCommonTool.swift
  53. 83 0
      AIPlayRingtones/Classes/Tool/TSCommonTool/TSMultiTaskDownloader.swift
  54. 27 0
      AIPlayRingtones/Classes/Tool/TSCommonTool/TSRandomTextTool.swift
  55. 76 0
      AIPlayRingtones/Classes/Tool/TSCustomAlertController.swift
  56. 111 0
      AIPlayRingtones/Classes/Tool/TSDownloadManager.swift
  57. 173 0
      AIPlayRingtones/Classes/Tool/TSFileManagerTool.swift
  58. 50 0
      AIPlayRingtones/Classes/Tool/TSGCDTimer.swift
  59. 190 0
      AIPlayRingtones/Classes/Tool/TSImageCompress.swift
  60. 40 0
      AIPlayRingtones/Classes/Tool/TSNetworkTool.swift
  61. 107 0
      AIPlayRingtones/Classes/Tool/TSToastTool.swift
  62. 78 0
      AIPlayRingtones/Classes/Tool/WindowHelper.swift
  63. 120 0
      AIPlayRingtones/Classes/View/TSCircularProgressView.swift
  64. 194 0
      AIPlayRingtones/Classes/View/TSPlaceholderTextView/TSPlaceholderTextView.swift
  65. 68 0
      AIPlayRingtones/Classes/View/TSProgressSlider/TSProgressSlider.swift
  66. 201 0
      AIPlayRingtones/Classes/View/TSReusableCollectionView/TSSimpleCollectionView.swift
  67. 127 0
      AIPlayRingtones/Classes/View/TSSaveSuccessTool/TSSaveSuccessTool.swift
  68. 310 0
      AIPlayRingtones/Classes/View/UICollectionView+Component/CollectionViewComponent.swift
  69. 63 0
      AIPlayRingtones/Classes/View/UICollectionView+Component/CommonSectionComponent.swift
  70. 64 0
      AIPlayRingtones/Classes/View/UICollectionView+Component/Component.swift
  71. 66 0
      AIPlayRingtones/Classes/View/UILabel/PaddedLabel.swift
  72. 148 0
      AIPlayRingtones/Classes/View/UIStackView/KLMultiScrollContainer.swift
  73. 164 0
      AIPlayRingtones/Classes/View/UIStackView/TSCustomStackView.swift
  74. 83 0
      AIPlayRingtones/Classes/View/UITableView+TSItemModel/TSSimpleTableView.swift
  75. 121 2
      AIPlayRingtones/Common/Common.swift
  76. 0 394
      AIPlayRingtones/Common/CpuMapManager.swift
  77. 15 5
      AIPlayRingtones/Common/TSNetWork/TSNetWork+Business.swift
  78. 0 86
      AIPlayRingtones/Common/TSNetWork/TSNetworkManager+Loading.swift
  79. 2 0
      AIPlayRingtones/OperationQueue/Generate/ASGenerateBaseOperation.swift
  80. 3 1
      Podfile
  81. 2 13
      Podfile.lock

+ 414 - 8
AIPlayRingtones.xcodeproj/project.pbxproj

@@ -21,13 +21,79 @@
 		3DB4D4AE2DDDCED00082596A /* UIFont+TSEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB4D4AB2DDDCED00082596A /* UIFont+TSEx.swift */; };
 		3DB4D4B22DDF0B960082596A /* FakeBlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB4D4B12DDF0B940082596A /* FakeBlurView.swift */; };
 		3DB4D4B52DE025920082596A /* ASTutorialsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB4D4B42DE025910082596A /* ASTutorialsVC.swift */; };
+		3DBEA0C92DE69B2E000C6859 /* TSToastTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0AE2DE69B2E000C6859 /* TSToastTool.swift */; };
+		3DBEA0CA2DE69B2E000C6859 /* KLMultiScrollContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0BF2DE69B2E000C6859 /* KLMultiScrollContainer.swift */; };
+		3DBEA0CB2DE69B2E000C6859 /* UIImage+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0902DE69B2E000C6859 /* UIImage+Ex.swift */; };
+		3DBEA0CC2DE69B2E000C6859 /* TSBusinessFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0A62DE69B2E000C6859 /* TSBusinessFileManager.swift */; };
+		3DBEA0CD2DE69B2E000C6859 /* TSSaveSuccessTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0B72DE69B2E000C6859 /* TSSaveSuccessTool.swift */; };
+		3DBEA0CE2DE69B2E000C6859 /* TSSimpleCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0B52DE69B2E000C6859 /* TSSimpleCollectionView.swift */; };
+		3DBEA0CF2DE69B2E000C6859 /* WindowHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0AF2DE69B2E000C6859 /* WindowHelper.swift */; };
+		3DBEA0D02DE69B2E000C6859 /* Bundle+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0852DE69B2E000C6859 /* Bundle+Ex.swift */; };
+		3DBEA0D12DE69B2E000C6859 /* TSBasicItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA07A2DE69B2E000C6859 /* TSBasicItemModel.swift */; };
+		3DBEA0D22DE69B2E000C6859 /* Array+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0832DE69B2E000C6859 /* Array+Ex.swift */; };
+		3DBEA0D32DE69B2E000C6859 /* TSSimpleTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0C22DE69B2E000C6859 /* TSSimpleTableView.swift */; };
+		3DBEA0D42DE69B2E000C6859 /* TSCommonTool+Down.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA09F2DE69B2E000C6859 /* TSCommonTool+Down.swift */; };
+		3DBEA0D52DE69B2E000C6859 /* TSDownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0A92DE69B2E000C6859 /* TSDownloadManager.swift */; };
+		3DBEA0D62DE69B2E000C6859 /* AVAsset+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0842DE69B2E000C6859 /* AVAsset+Ex.swift */; };
+		3DBEA0D72DE69B2E000C6859 /* UITableView+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0942DE69B2E000C6859 /* UITableView+Ex.swift */; };
+		3DBEA0D82DE69B2E000C6859 /* TSBaseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0732DE69B2E000C6859 /* TSBaseModel.swift */; };
+		3DBEA0D92DE69B2E000C6859 /* UIImageView+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0912DE69B2E000C6859 /* UIImageView+Ex.swift */; };
+		3DBEA0DA2DE69B2E000C6859 /* CommonSectionComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0BA2DE69B2E000C6859 /* CommonSectionComponent.swift */; };
+		3DBEA0DC2DE69B2E000C6859 /* TSBaseNavigationC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0752DE69B2E000C6859 /* TSBaseNavigationC.swift */; };
+		3DBEA0DD2DE69B2E000C6859 /* SVProgressHUD+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0A52DE69B2E000C6859 /* SVProgressHUD+Ex.swift */; };
+		3DBEA0DE2DE69B2E000C6859 /* TSCommonTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA09E2DE69B2E000C6859 /* TSCommonTool.swift */; };
+		3DBEA0DF2DE69B2E000C6859 /* UIScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0932DE69B2E000C6859 /* UIScreen.swift */; };
+		3DBEA0E02DE69B2E000C6859 /* TSBaseTabViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0762DE69B2E000C6859 /* TSBaseTabViewCell.swift */; };
+		3DBEA0E12DE69B2E000C6859 /* Color+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA07F2DE69B2E000C6859 /* Color+Ex.swift */; };
+		3DBEA0E22DE69B2E000C6859 /* UIApplication+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA08B2DE69B2E000C6859 /* UIApplication+Ex.swift */; };
+		3DBEA0E32DE69B2E000C6859 /* UserDefault+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA09A2DE69B2E000C6859 /* UserDefault+Ex.swift */; };
+		3DBEA0E42DE69B2E000C6859 /* UIDevice+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA08E2DE69B2E000C6859 /* UIDevice+Extension.swift */; };
+		3DBEA0E52DE69B2E000C6859 /* TSMultiTaskDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0A12DE69B2E000C6859 /* TSMultiTaskDownloader.swift */; };
+		3DBEA0E62DE69B2E000C6859 /* CollectionViewComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0B92DE69B2E000C6859 /* CollectionViewComponent.swift */; };
+		3DBEA0E72DE69B2E000C6859 /* Int+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0892DE69B2E000C6859 /* Int+Ex.swift */; };
+		3DBEA0E82DE69B2E000C6859 /* UIView+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0962DE69B2E000C6859 /* UIView+Ex.swift */; };
+		3DBEA0E92DE69B2E000C6859 /* TSBaseVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0772DE69B2E000C6859 /* TSBaseVC.swift */; };
+		3DBEA0EA2DE69B2E000C6859 /* UIView+Rotating.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0982DE69B2E000C6859 /* UIView+Rotating.swift */; };
+		3DBEA0EB2DE69B2E000C6859 /* Text+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0812DE69B2E000C6859 /* Text+Ex.swift */; };
+		3DBEA0EC2DE69B2E000C6859 /* UILabel+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0922DE69B2E000C6859 /* UILabel+Ex.swift */; };
+		3DBEA0ED2DE69B2E000C6859 /* TSBaseCollectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0722DE69B2E000C6859 /* TSBaseCollectionCell.swift */; };
+		3DBEA0EE2DE69B2E000C6859 /* PhotoManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0A42DE69B2E000C6859 /* PhotoManager.swift */; };
+		3DBEA0EF2DE69B2E000C6859 /* UIColor+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA08D2DE69B2E000C6859 /* UIColor+Ex.swift */; };
+		3DBEA0F02DE69B2E000C6859 /* Font+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0802DE69B2E000C6859 /* Font+Ex.swift */; };
+		3DBEA0F12DE69B2E000C6859 /* TSFileManagerTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0AA2DE69B2E000C6859 /* TSFileManagerTool.swift */; };
+		3DBEA0F22DE69B2E000C6859 /* TSCircularProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0C42DE69B2E000C6859 /* TSCircularProgressView.swift */; };
+		3DBEA0F32DE69B2E000C6859 /* TSRandomTextTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0A22DE69B2E000C6859 /* TSRandomTextTool.swift */; };
+		3DBEA0F42DE69B2E000C6859 /* TSBaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0782DE69B2E000C6859 /* TSBaseView.swift */; };
+		3DBEA0F52DE69B2E000C6859 /* TSBaseViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0792DE69B2E000C6859 /* TSBaseViewModel.swift */; };
+		3DBEA0F62DE69B2E000C6859 /* TSCustomAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0A82DE69B2E000C6859 /* TSCustomAlertController.swift */; };
+		3DBEA0F72DE69B2E000C6859 /* TSImageCompress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0AC2DE69B2E000C6859 /* TSImageCompress.swift */; };
+		3DBEA0F82DE69B2E000C6859 /* TSGCDTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0AB2DE69B2E000C6859 /* TSGCDTimer.swift */; };
+		3DBEA0FA2DE69B2E000C6859 /* TSBaseNavigationBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0742DE69B2E000C6859 /* TSBaseNavigationBarView.swift */; };
+		3DBEA0FB2DE69B2E000C6859 /* UITextView+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0952DE69B2E000C6859 /* UITextView+Ex.swift */; };
+		3DBEA0FC2DE69B2E000C6859 /* UIFont+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA08F2DE69B2E000C6859 /* UIFont+Ex.swift */; };
+		3DBEA0FD2DE69B2E000C6859 /* TSCommonTool+MultDown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0A02DE69B2E000C6859 /* TSCommonTool+MultDown.swift */; };
+		3DBEA0FE2DE69B2E000C6859 /* UIButton+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA08C2DE69B2E000C6859 /* UIButton+Ex.swift */; };
+		3DBEA0FF2DE69B2E000C6859 /* TSCustomStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0C02DE69B2E000C6859 /* TSCustomStackView.swift */; };
+		3DBEA1002DE69B2E000C6859 /* UIViewController+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0992DE69B2E000C6859 /* UIViewController+Ex.swift */; };
+		3DBEA1012DE69B2E000C6859 /* Date+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0872DE69B2E000C6859 /* Date+Ex.swift */; };
+		3DBEA1022DE69B2E000C6859 /* TSNetworkTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0AD2DE69B2E000C6859 /* TSNetworkTool.swift */; };
+		3DBEA1032DE69B2E000C6859 /* NSString+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA08A2DE69B2E000C6859 /* NSString+Ex.swift */; };
+		3DBEA1042DE69B2E000C6859 /* Dictionary+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0882DE69B2E000C6859 /* Dictionary+Ex.swift */; };
+		3DBEA1052DE69B2E000C6859 /* PaddedLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0BD2DE69B2E000C6859 /* PaddedLabel.swift */; };
+		3DBEA1062DE69B2E000C6859 /* GlobalImports.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA09C2DE69B2E000C6859 /* GlobalImports.swift */; };
+		3DBEA1072DE69B2E000C6859 /* TSPlaceholderTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0B12DE69B2E000C6859 /* TSPlaceholderTextView.swift */; };
+		3DBEA1082DE69B2E000C6859 /* UIView+Frame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0972DE69B2E000C6859 /* UIView+Frame.swift */; };
+		3DBEA1092DE69B2E000C6859 /* CGFloat+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0862DE69B2E000C6859 /* CGFloat+Ex.swift */; };
+		3DBEA10A2DE69B2E000C6859 /* TSProgressSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0B32DE69B2E000C6859 /* TSProgressSlider.swift */; };
+		3DBEA10B2DE69B2E000C6859 /* TSCollectionViewObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0A72DE69B2E000C6859 /* TSCollectionViewObserver.swift */; };
+		3DBEA10C2DE69B2E000C6859 /* Component.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA0BB2DE69B2E000C6859 /* Component.swift */; };
+		3DBEA1102DE6A04B000C6859 /* SimpleWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA10F2DE6A04B000C6859 /* SimpleWebViewController.m */; };
 		3DCD56F32DDAE3E3004AAB5B /* ASRingToneCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD56F22DDAE3DF004AAB5B /* ASRingToneCellView.swift */; };
 		3DCD56F52DDAE42A004AAB5B /* ASViewTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD56F42DDAE421004AAB5B /* ASViewTool.swift */; };
 		3DCD56F92DDAE481004AAB5B /* TSBusinessAudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD56F62DDAE481004AAB5B /* TSBusinessAudioPlayer.swift */; };
 		3DCD56FA2DDAE481004AAB5B /* TSAudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD56F72DDAE481004AAB5B /* TSAudioPlayer.swift */; };
 		3DCD56FD2DDAFBE5004AAB5B /* ASDownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD56FC2DDAFBE4004AAB5B /* ASDownloadManager.swift */; };
 		3DCD56FF2DDAFC1B004AAB5B /* ASBusinessFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD56FE2DDAFC17004AAB5B /* ASBusinessFileManager.swift */; };
-		3DCD57012DDB1008004AAB5B /* CpuMapManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD57002DDB1008004AAB5B /* CpuMapManager.swift */; };
 		3DCD57052DDB1120004AAB5B /* TSPurchaseTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD57042DDB111F004AAB5B /* TSPurchaseTool.swift */; };
 		3DCD57182DDB1158004AAB5B /* libmp3lame.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DCD57092DDB1158004AAB5B /* libmp3lame.a */; };
 		3DCD57192DDB1158004AAB5B /* AudioTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD570D2DDB1158004AAB5B /* AudioTool.swift */; };
@@ -63,7 +129,6 @@
 		A848F8932DD6D52100B746EC /* AIEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = A848F8922DD6D51900B746EC /* AIEnums.swift */; };
 		A848F89E2DD6D59C00B746EC /* TSNetWork+Business.swift in Sources */ = {isa = PBXBuildFile; fileRef = A848F89A2DD6D59C00B746EC /* TSNetWork+Business.swift */; };
 		A848F89F2DD6D59C00B746EC /* TSNetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A848F8992DD6D59C00B746EC /* TSNetworkManager.swift */; };
-		A848F8A12DD6D59C00B746EC /* TSNetworkManager+Loading.swift in Sources */ = {isa = PBXBuildFile; fileRef = A848F89B2DD6D59C00B746EC /* TSNetworkManager+Loading.swift */; };
 		A848F8A42DD6D67000B746EC /* APRingTonesVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A848F8A32DD6D66D00B746EC /* APRingTonesVC.swift */; };
 		A848F8A92DD6D74A00B746EC /* APAudioToRingVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A848F8A82DD6D74900B746EC /* APAudioToRingVC.swift */; };
 		A848F8AB2DD6D75300B746EC /* ASMyRingVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A848F8AA2DD6D75200B746EC /* ASMyRingVC.swift */; };
@@ -114,13 +179,80 @@
 		3DB4D4AB2DDDCED00082596A /* UIFont+TSEx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+TSEx.swift"; sourceTree = "<group>"; };
 		3DB4D4B12DDF0B940082596A /* FakeBlurView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeBlurView.swift; sourceTree = "<group>"; };
 		3DB4D4B42DE025910082596A /* ASTutorialsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASTutorialsVC.swift; sourceTree = "<group>"; };
+		3DBEA0722DE69B2E000C6859 /* TSBaseCollectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseCollectionCell.swift; sourceTree = "<group>"; };
+		3DBEA0732DE69B2E000C6859 /* TSBaseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseModel.swift; sourceTree = "<group>"; };
+		3DBEA0742DE69B2E000C6859 /* TSBaseNavigationBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseNavigationBarView.swift; sourceTree = "<group>"; };
+		3DBEA0752DE69B2E000C6859 /* TSBaseNavigationC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseNavigationC.swift; sourceTree = "<group>"; };
+		3DBEA0762DE69B2E000C6859 /* TSBaseTabViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseTabViewCell.swift; sourceTree = "<group>"; };
+		3DBEA0772DE69B2E000C6859 /* TSBaseVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseVC.swift; sourceTree = "<group>"; };
+		3DBEA0782DE69B2E000C6859 /* TSBaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseView.swift; sourceTree = "<group>"; };
+		3DBEA0792DE69B2E000C6859 /* TSBaseViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseViewModel.swift; sourceTree = "<group>"; };
+		3DBEA07A2DE69B2E000C6859 /* TSBasicItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBasicItemModel.swift; sourceTree = "<group>"; };
+		3DBEA07F2DE69B2E000C6859 /* Color+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0802DE69B2E000C6859 /* Font+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Font+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0812DE69B2E000C6859 /* Text+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Text+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0832DE69B2E000C6859 /* Array+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0842DE69B2E000C6859 /* AVAsset+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVAsset+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0852DE69B2E000C6859 /* Bundle+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0862DE69B2E000C6859 /* CGFloat+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGFloat+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0872DE69B2E000C6859 /* Date+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0882DE69B2E000C6859 /* Dictionary+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0892DE69B2E000C6859 /* Int+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA08A2DE69B2E000C6859 /* NSString+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSString+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA08B2DE69B2E000C6859 /* UIApplication+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA08C2DE69B2E000C6859 /* UIButton+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA08D2DE69B2E000C6859 /* UIColor+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA08E2DE69B2E000C6859 /* UIDevice+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Extension.swift"; sourceTree = "<group>"; };
+		3DBEA08F2DE69B2E000C6859 /* UIFont+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0902DE69B2E000C6859 /* UIImage+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0912DE69B2E000C6859 /* UIImageView+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0922DE69B2E000C6859 /* UILabel+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0932DE69B2E000C6859 /* UIScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScreen.swift; sourceTree = "<group>"; };
+		3DBEA0942DE69B2E000C6859 /* UITableView+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0952DE69B2E000C6859 /* UITextView+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0962DE69B2E000C6859 /* UIView+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0972DE69B2E000C6859 /* UIView+Frame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Frame.swift"; sourceTree = "<group>"; };
+		3DBEA0982DE69B2E000C6859 /* UIView+Rotating.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Rotating.swift"; sourceTree = "<group>"; };
+		3DBEA0992DE69B2E000C6859 /* UIViewController+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA09A2DE69B2E000C6859 /* UserDefault+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefault+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA09C2DE69B2E000C6859 /* GlobalImports.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalImports.swift; sourceTree = "<group>"; };
+		3DBEA09E2DE69B2E000C6859 /* TSCommonTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSCommonTool.swift; sourceTree = "<group>"; };
+		3DBEA09F2DE69B2E000C6859 /* TSCommonTool+Down.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSCommonTool+Down.swift"; sourceTree = "<group>"; };
+		3DBEA0A02DE69B2E000C6859 /* TSCommonTool+MultDown.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSCommonTool+MultDown.swift"; sourceTree = "<group>"; };
+		3DBEA0A12DE69B2E000C6859 /* TSMultiTaskDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSMultiTaskDownloader.swift; sourceTree = "<group>"; };
+		3DBEA0A22DE69B2E000C6859 /* TSRandomTextTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSRandomTextTool.swift; sourceTree = "<group>"; };
+		3DBEA0A42DE69B2E000C6859 /* PhotoManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoManager.swift; sourceTree = "<group>"; };
+		3DBEA0A52DE69B2E000C6859 /* SVProgressHUD+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SVProgressHUD+Ex.swift"; sourceTree = "<group>"; };
+		3DBEA0A62DE69B2E000C6859 /* TSBusinessFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBusinessFileManager.swift; sourceTree = "<group>"; };
+		3DBEA0A72DE69B2E000C6859 /* TSCollectionViewObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSCollectionViewObserver.swift; sourceTree = "<group>"; };
+		3DBEA0A82DE69B2E000C6859 /* TSCustomAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSCustomAlertController.swift; sourceTree = "<group>"; };
+		3DBEA0A92DE69B2E000C6859 /* TSDownloadManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDownloadManager.swift; sourceTree = "<group>"; };
+		3DBEA0AA2DE69B2E000C6859 /* TSFileManagerTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSFileManagerTool.swift; sourceTree = "<group>"; };
+		3DBEA0AB2DE69B2E000C6859 /* TSGCDTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSGCDTimer.swift; sourceTree = "<group>"; };
+		3DBEA0AC2DE69B2E000C6859 /* TSImageCompress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSImageCompress.swift; sourceTree = "<group>"; };
+		3DBEA0AD2DE69B2E000C6859 /* TSNetworkTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSNetworkTool.swift; sourceTree = "<group>"; };
+		3DBEA0AE2DE69B2E000C6859 /* TSToastTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSToastTool.swift; sourceTree = "<group>"; };
+		3DBEA0AF2DE69B2E000C6859 /* WindowHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowHelper.swift; sourceTree = "<group>"; };
+		3DBEA0B12DE69B2E000C6859 /* TSPlaceholderTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPlaceholderTextView.swift; sourceTree = "<group>"; };
+		3DBEA0B32DE69B2E000C6859 /* TSProgressSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSProgressSlider.swift; sourceTree = "<group>"; };
+		3DBEA0B52DE69B2E000C6859 /* TSSimpleCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSSimpleCollectionView.swift; sourceTree = "<group>"; };
+		3DBEA0B72DE69B2E000C6859 /* TSSaveSuccessTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSSaveSuccessTool.swift; sourceTree = "<group>"; };
+		3DBEA0B92DE69B2E000C6859 /* CollectionViewComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewComponent.swift; sourceTree = "<group>"; };
+		3DBEA0BA2DE69B2E000C6859 /* CommonSectionComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonSectionComponent.swift; sourceTree = "<group>"; };
+		3DBEA0BB2DE69B2E000C6859 /* Component.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Component.swift; sourceTree = "<group>"; };
+		3DBEA0BD2DE69B2E000C6859 /* PaddedLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaddedLabel.swift; sourceTree = "<group>"; };
+		3DBEA0BF2DE69B2E000C6859 /* KLMultiScrollContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KLMultiScrollContainer.swift; sourceTree = "<group>"; };
+		3DBEA0C02DE69B2E000C6859 /* TSCustomStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSCustomStackView.swift; sourceTree = "<group>"; };
+		3DBEA0C22DE69B2E000C6859 /* TSSimpleTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSSimpleTableView.swift; sourceTree = "<group>"; };
+		3DBEA0C42DE69B2E000C6859 /* TSCircularProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSCircularProgressView.swift; sourceTree = "<group>"; };
+		3DBEA10E2DE6A04B000C6859 /* SimpleWebViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SimpleWebViewController.h; sourceTree = "<group>"; };
+		3DBEA10F2DE6A04B000C6859 /* SimpleWebViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SimpleWebViewController.m; sourceTree = "<group>"; };
 		3DCD56F22DDAE3DF004AAB5B /* ASRingToneCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASRingToneCellView.swift; sourceTree = "<group>"; };
 		3DCD56F42DDAE421004AAB5B /* ASViewTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASViewTool.swift; sourceTree = "<group>"; };
 		3DCD56F62DDAE481004AAB5B /* TSBusinessAudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBusinessAudioPlayer.swift; sourceTree = "<group>"; };
 		3DCD56F72DDAE481004AAB5B /* TSAudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAudioPlayer.swift; sourceTree = "<group>"; };
 		3DCD56FC2DDAFBE4004AAB5B /* ASDownloadManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASDownloadManager.swift; sourceTree = "<group>"; };
 		3DCD56FE2DDAFC17004AAB5B /* ASBusinessFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASBusinessFileManager.swift; sourceTree = "<group>"; };
-		3DCD57002DDB1008004AAB5B /* CpuMapManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CpuMapManager.swift; sourceTree = "<group>"; };
 		3DCD57042DDB111F004AAB5B /* TSPurchaseTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPurchaseTool.swift; sourceTree = "<group>"; };
 		3DCD57062DDB1158004AAB5B /* AudioConverter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioConverter.h; sourceTree = "<group>"; };
 		3DCD57072DDB1158004AAB5B /* AudioConverter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AudioConverter.m; sourceTree = "<group>"; };
@@ -163,7 +295,6 @@
 		A848F8922DD6D51900B746EC /* AIEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AIEnums.swift; sourceTree = "<group>"; };
 		A848F8992DD6D59C00B746EC /* TSNetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSNetworkManager.swift; sourceTree = "<group>"; };
 		A848F89A2DD6D59C00B746EC /* TSNetWork+Business.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSNetWork+Business.swift"; sourceTree = "<group>"; };
-		A848F89B2DD6D59C00B746EC /* TSNetworkManager+Loading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSNetworkManager+Loading.swift"; sourceTree = "<group>"; };
 		A848F8A32DD6D66D00B746EC /* APRingTonesVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APRingTonesVC.swift; sourceTree = "<group>"; };
 		A848F8A82DD6D74900B746EC /* APAudioToRingVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APAudioToRingVC.swift; sourceTree = "<group>"; };
 		A848F8AA2DD6D75200B746EC /* ASMyRingVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASMyRingVC.swift; sourceTree = "<group>"; };
@@ -254,6 +385,216 @@
 			path = ASTutorialsVC;
 			sourceTree = "<group>";
 		};
+		3DBEA07B2DE69B2E000C6859 /* BaseClass */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA0722DE69B2E000C6859 /* TSBaseCollectionCell.swift */,
+				3DBEA0732DE69B2E000C6859 /* TSBaseModel.swift */,
+				3DBEA0742DE69B2E000C6859 /* TSBaseNavigationBarView.swift */,
+				3DBEA0752DE69B2E000C6859 /* TSBaseNavigationC.swift */,
+				3DBEA0762DE69B2E000C6859 /* TSBaseTabViewCell.swift */,
+				3DBEA0772DE69B2E000C6859 /* TSBaseVC.swift */,
+				3DBEA0782DE69B2E000C6859 /* TSBaseView.swift */,
+				3DBEA0792DE69B2E000C6859 /* TSBaseViewModel.swift */,
+				3DBEA07A2DE69B2E000C6859 /* TSBasicItemModel.swift */,
+			);
+			path = BaseClass;
+			sourceTree = "<group>";
+		};
+		3DBEA07E2DE69B2E000C6859 /* Config */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			path = Config;
+			sourceTree = "<group>";
+		};
+		3DBEA0822DE69B2E000C6859 /* SwiftUI */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA07F2DE69B2E000C6859 /* Color+Ex.swift */,
+				3DBEA0802DE69B2E000C6859 /* Font+Ex.swift */,
+				3DBEA0812DE69B2E000C6859 /* Text+Ex.swift */,
+			);
+			path = SwiftUI;
+			sourceTree = "<group>";
+		};
+		3DBEA09B2DE69B2E000C6859 /* Ex */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA0822DE69B2E000C6859 /* SwiftUI */,
+				3DBEA0832DE69B2E000C6859 /* Array+Ex.swift */,
+				3DBEA0842DE69B2E000C6859 /* AVAsset+Ex.swift */,
+				3DBEA0852DE69B2E000C6859 /* Bundle+Ex.swift */,
+				3DBEA0862DE69B2E000C6859 /* CGFloat+Ex.swift */,
+				3DBEA0872DE69B2E000C6859 /* Date+Ex.swift */,
+				3DBEA0882DE69B2E000C6859 /* Dictionary+Ex.swift */,
+				3DBEA0892DE69B2E000C6859 /* Int+Ex.swift */,
+				3DBEA08A2DE69B2E000C6859 /* NSString+Ex.swift */,
+				3DBEA08B2DE69B2E000C6859 /* UIApplication+Ex.swift */,
+				3DBEA08C2DE69B2E000C6859 /* UIButton+Ex.swift */,
+				3DBEA08D2DE69B2E000C6859 /* UIColor+Ex.swift */,
+				3DBEA08E2DE69B2E000C6859 /* UIDevice+Extension.swift */,
+				3DBEA08F2DE69B2E000C6859 /* UIFont+Ex.swift */,
+				3DBEA0902DE69B2E000C6859 /* UIImage+Ex.swift */,
+				3DBEA0912DE69B2E000C6859 /* UIImageView+Ex.swift */,
+				3DBEA0922DE69B2E000C6859 /* UILabel+Ex.swift */,
+				3DBEA0932DE69B2E000C6859 /* UIScreen.swift */,
+				3DBEA0942DE69B2E000C6859 /* UITableView+Ex.swift */,
+				3DBEA0952DE69B2E000C6859 /* UITextView+Ex.swift */,
+				3DBEA0962DE69B2E000C6859 /* UIView+Ex.swift */,
+				3DBEA0972DE69B2E000C6859 /* UIView+Frame.swift */,
+				3DBEA0982DE69B2E000C6859 /* UIView+Rotating.swift */,
+				3DBEA0992DE69B2E000C6859 /* UIViewController+Ex.swift */,
+				3DBEA09A2DE69B2E000C6859 /* UserDefault+Ex.swift */,
+			);
+			path = Ex;
+			sourceTree = "<group>";
+		};
+		3DBEA09D2DE69B2E000C6859 /* GlobalImports */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA09C2DE69B2E000C6859 /* GlobalImports.swift */,
+			);
+			path = GlobalImports;
+			sourceTree = "<group>";
+		};
+		3DBEA0A32DE69B2E000C6859 /* TSCommonTool */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA09E2DE69B2E000C6859 /* TSCommonTool.swift */,
+				3DBEA09F2DE69B2E000C6859 /* TSCommonTool+Down.swift */,
+				3DBEA0A02DE69B2E000C6859 /* TSCommonTool+MultDown.swift */,
+				3DBEA0A12DE69B2E000C6859 /* TSMultiTaskDownloader.swift */,
+				3DBEA0A22DE69B2E000C6859 /* TSRandomTextTool.swift */,
+			);
+			path = TSCommonTool;
+			sourceTree = "<group>";
+		};
+		3DBEA0B02DE69B2E000C6859 /* Tool */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA0A32DE69B2E000C6859 /* TSCommonTool */,
+				3DBEA0A42DE69B2E000C6859 /* PhotoManager.swift */,
+				3DBEA0A52DE69B2E000C6859 /* SVProgressHUD+Ex.swift */,
+				3DBEA0A62DE69B2E000C6859 /* TSBusinessFileManager.swift */,
+				3DBEA0A72DE69B2E000C6859 /* TSCollectionViewObserver.swift */,
+				3DBEA0A82DE69B2E000C6859 /* TSCustomAlertController.swift */,
+				3DBEA0A92DE69B2E000C6859 /* TSDownloadManager.swift */,
+				3DBEA0AA2DE69B2E000C6859 /* TSFileManagerTool.swift */,
+				3DBEA0AB2DE69B2E000C6859 /* TSGCDTimer.swift */,
+				3DBEA0AC2DE69B2E000C6859 /* TSImageCompress.swift */,
+				3DBEA0AD2DE69B2E000C6859 /* TSNetworkTool.swift */,
+				3DBEA0AE2DE69B2E000C6859 /* TSToastTool.swift */,
+				3DBEA0AF2DE69B2E000C6859 /* WindowHelper.swift */,
+			);
+			path = Tool;
+			sourceTree = "<group>";
+		};
+		3DBEA0B22DE69B2E000C6859 /* TSPlaceholderTextView */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA0B12DE69B2E000C6859 /* TSPlaceholderTextView.swift */,
+			);
+			path = TSPlaceholderTextView;
+			sourceTree = "<group>";
+		};
+		3DBEA0B42DE69B2E000C6859 /* TSProgressSlider */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA0B32DE69B2E000C6859 /* TSProgressSlider.swift */,
+			);
+			path = TSProgressSlider;
+			sourceTree = "<group>";
+		};
+		3DBEA0B62DE69B2E000C6859 /* TSReusableCollectionView */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA0B52DE69B2E000C6859 /* TSSimpleCollectionView.swift */,
+			);
+			path = TSReusableCollectionView;
+			sourceTree = "<group>";
+		};
+		3DBEA0B82DE69B2E000C6859 /* TSSaveSuccessTool */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA0B72DE69B2E000C6859 /* TSSaveSuccessTool.swift */,
+			);
+			path = TSSaveSuccessTool;
+			sourceTree = "<group>";
+		};
+		3DBEA0BC2DE69B2E000C6859 /* UICollectionView+Component */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA0B92DE69B2E000C6859 /* CollectionViewComponent.swift */,
+				3DBEA0BA2DE69B2E000C6859 /* CommonSectionComponent.swift */,
+				3DBEA0BB2DE69B2E000C6859 /* Component.swift */,
+			);
+			path = "UICollectionView+Component";
+			sourceTree = "<group>";
+		};
+		3DBEA0BE2DE69B2E000C6859 /* UILabel */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA0BD2DE69B2E000C6859 /* PaddedLabel.swift */,
+			);
+			path = UILabel;
+			sourceTree = "<group>";
+		};
+		3DBEA0C12DE69B2E000C6859 /* UIStackView */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA0BF2DE69B2E000C6859 /* KLMultiScrollContainer.swift */,
+				3DBEA0C02DE69B2E000C6859 /* TSCustomStackView.swift */,
+			);
+			path = UIStackView;
+			sourceTree = "<group>";
+		};
+		3DBEA0C32DE69B2E000C6859 /* UITableView+TSItemModel */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA0C22DE69B2E000C6859 /* TSSimpleTableView.swift */,
+			);
+			path = "UITableView+TSItemModel";
+			sourceTree = "<group>";
+		};
+		3DBEA0C52DE69B2E000C6859 /* View */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA0B22DE69B2E000C6859 /* TSPlaceholderTextView */,
+				3DBEA0B42DE69B2E000C6859 /* TSProgressSlider */,
+				3DBEA0B62DE69B2E000C6859 /* TSReusableCollectionView */,
+				3DBEA0B82DE69B2E000C6859 /* TSSaveSuccessTool */,
+				3DBEA0BC2DE69B2E000C6859 /* UICollectionView+Component */,
+				3DBEA0BE2DE69B2E000C6859 /* UILabel */,
+				3DBEA0C12DE69B2E000C6859 /* UIStackView */,
+				3DBEA0C32DE69B2E000C6859 /* UITableView+TSItemModel */,
+				3DBEA0C42DE69B2E000C6859 /* TSCircularProgressView.swift */,
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
+		3DBEA0C72DE69B2E000C6859 /* Classes */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA07B2DE69B2E000C6859 /* BaseClass */,
+				3DBEA07E2DE69B2E000C6859 /* Config */,
+				3DBEA09B2DE69B2E000C6859 /* Ex */,
+				3DBEA09D2DE69B2E000C6859 /* GlobalImports */,
+				3DBEA0B02DE69B2E000C6859 /* Tool */,
+				3DBEA0C52DE69B2E000C6859 /* View */,
+			);
+			path = Classes;
+			sourceTree = "<group>";
+		};
+		3DBEA10D2DE6A02F000C6859 /* SimpleWebViewController */ = {
+			isa = PBXGroup;
+			children = (
+				3DBEA10E2DE6A04B000C6859 /* SimpleWebViewController.h */,
+				3DBEA10F2DE6A04B000C6859 /* SimpleWebViewController.m */,
+			);
+			path = SimpleWebViewController;
+			sourceTree = "<group>";
+		};
 		3DCD56F82DDAE481004AAB5B /* TSAudioPlayer */ = {
 			isa = PBXGroup;
 			children = (
@@ -379,6 +720,7 @@
 		A848F8812DD6D1AF00B746EC /* AIPlayRingtones */ = {
 			isa = PBXGroup;
 			children = (
+				3DBEA0C72DE69B2E000C6859 /* Classes */,
 				A848F88F2DD6D4E800B746EC /* Common */,
 				A800FEAF2DDAC0E9009DABDC /* CommonView */,
 				A848F8EF2DD7526600B746EC /* OperationQueue */,
@@ -398,6 +740,7 @@
 		A848F88B2DD6D2C600B746EC /* AppPage */ = {
 			isa = PBXGroup;
 			children = (
+				3DBEA10D2DE6A02F000C6859 /* SimpleWebViewController */,
 				3DB4D4A62DDC924B0082596A /* ASBusinessWebVC */,
 				A848F8B62DD6DF5500B746EC /* ASCommonView */,
 				A848F8AE2DD6D76F00B746EC /* ASTabBarVC */,
@@ -425,7 +768,6 @@
 				3DB4D4AD2DDDCED00082596A /* Ex */,
 				3DCD57172DDB1158004AAB5B /* TSBandRingTool */,
 				3DCD57032DDB1118004AAB5B /* TSPurchaseTool */,
-				3DCD57002DDB1008004AAB5B /* CpuMapManager.swift */,
 				3DCD56FE2DDAFC17004AAB5B /* ASBusinessFileManager.swift */,
 				3DCD56FB2DDAFBD8004AAB5B /* ASDownloadManager */,
 				3DCD56F82DDAE481004AAB5B /* TSAudioPlayer */,
@@ -443,7 +785,6 @@
 				A800FEB72DDAC81D009DABDC /* TSNetWork+Error.swift */,
 				A848F8992DD6D59C00B746EC /* TSNetworkManager.swift */,
 				A848F89A2DD6D59C00B746EC /* TSNetWork+Business.swift */,
-				A848F89B2DD6D59C00B746EC /* TSNetworkManager+Loading.swift */,
 			);
 			path = TSNetWork;
 			sourceTree = "<group>";
@@ -775,7 +1116,6 @@
 				A848F8E32DD7286A00B746EC /* ASGenerateStyleModel.swift in Sources */,
 				3DCD57052DDB1120004AAB5B /* TSPurchaseTool.swift in Sources */,
 				3DB4D4992DDC28E70082596A /* APAudioToRingVC+StackView.swift in Sources */,
-				A848F8A12DD6D59C00B746EC /* TSNetworkManager+Loading.swift in Sources */,
 				3DCD56F52DDAE42A004AAB5B /* ASViewTool.swift in Sources */,
 				3DB4D4972DDC28D40082596A /* APAudioToRingVC+View.swift in Sources */,
 				A800FEBD2DDACCD4009DABDC /* ASRingGeneratorBaseVC.swift in Sources */,
@@ -817,6 +1157,7 @@
 				3DCD56FF2DDAFC1B004AAB5B /* ASBusinessFileManager.swift in Sources */,
 				A848F8C42DD6E70500B746EC /* APRingTonesVC+Style.swift in Sources */,
 				A848F8F82DD752E700B746EC /* ASGenerateTextToRingOperation.swift in Sources */,
+				3DBEA1102DE6A04B000C6859 /* SimpleWebViewController.m in Sources */,
 				A800FEB42DDAC73E009DABDC /* ASGeneratorLoadingView.swift in Sources */,
 				A848F8EC2DD74EF800B746EC /* ASActionInfoModel.swift in Sources */,
 				A848F8F62DD752D700B746EC /* ASGenerateBaseOperation.swift in Sources */,
@@ -829,11 +1170,76 @@
 				3DCD573B2DDB4E61004AAB5B /* ASPageNullView.swift in Sources */,
 				A848F8F12DD7528800B746EC /* ASBaseOperationQueue.swift in Sources */,
 				A848F8CD2DD6EAD300B746EC /* ASPromptTextView.swift in Sources */,
-				3DCD57012DDB1008004AAB5B /* CpuMapManager.swift in Sources */,
 				A848F8DB2DD7231E00B746EC /* APRingStyleVC.swift in Sources */,
 				A848F8D22DD7149D00B746EC /* ASDurationColView.swift in Sources */,
 				A848F8C62DD6E72D00B746EC /* APRingTonesVC+View.swift in Sources */,
 				A848F8862DD6D1AF00B746EC /* AppDelegate.swift in Sources */,
+				3DBEA0C92DE69B2E000C6859 /* TSToastTool.swift in Sources */,
+				3DBEA0CA2DE69B2E000C6859 /* KLMultiScrollContainer.swift in Sources */,
+				3DBEA0CB2DE69B2E000C6859 /* UIImage+Ex.swift in Sources */,
+				3DBEA0CC2DE69B2E000C6859 /* TSBusinessFileManager.swift in Sources */,
+				3DBEA0CD2DE69B2E000C6859 /* TSSaveSuccessTool.swift in Sources */,
+				3DBEA0CE2DE69B2E000C6859 /* TSSimpleCollectionView.swift in Sources */,
+				3DBEA0CF2DE69B2E000C6859 /* WindowHelper.swift in Sources */,
+				3DBEA0D02DE69B2E000C6859 /* Bundle+Ex.swift in Sources */,
+				3DBEA0D12DE69B2E000C6859 /* TSBasicItemModel.swift in Sources */,
+				3DBEA0D22DE69B2E000C6859 /* Array+Ex.swift in Sources */,
+				3DBEA0D32DE69B2E000C6859 /* TSSimpleTableView.swift in Sources */,
+				3DBEA0D42DE69B2E000C6859 /* TSCommonTool+Down.swift in Sources */,
+				3DBEA0D52DE69B2E000C6859 /* TSDownloadManager.swift in Sources */,
+				3DBEA0D62DE69B2E000C6859 /* AVAsset+Ex.swift in Sources */,
+				3DBEA0D72DE69B2E000C6859 /* UITableView+Ex.swift in Sources */,
+				3DBEA0D82DE69B2E000C6859 /* TSBaseModel.swift in Sources */,
+				3DBEA0D92DE69B2E000C6859 /* UIImageView+Ex.swift in Sources */,
+				3DBEA0DA2DE69B2E000C6859 /* CommonSectionComponent.swift in Sources */,
+				3DBEA0DC2DE69B2E000C6859 /* TSBaseNavigationC.swift in Sources */,
+				3DBEA0DD2DE69B2E000C6859 /* SVProgressHUD+Ex.swift in Sources */,
+				3DBEA0DE2DE69B2E000C6859 /* TSCommonTool.swift in Sources */,
+				3DBEA0DF2DE69B2E000C6859 /* UIScreen.swift in Sources */,
+				3DBEA0E02DE69B2E000C6859 /* TSBaseTabViewCell.swift in Sources */,
+				3DBEA0E12DE69B2E000C6859 /* Color+Ex.swift in Sources */,
+				3DBEA0E22DE69B2E000C6859 /* UIApplication+Ex.swift in Sources */,
+				3DBEA0E32DE69B2E000C6859 /* UserDefault+Ex.swift in Sources */,
+				3DBEA0E42DE69B2E000C6859 /* UIDevice+Extension.swift in Sources */,
+				3DBEA0E52DE69B2E000C6859 /* TSMultiTaskDownloader.swift in Sources */,
+				3DBEA0E62DE69B2E000C6859 /* CollectionViewComponent.swift in Sources */,
+				3DBEA0E72DE69B2E000C6859 /* Int+Ex.swift in Sources */,
+				3DBEA0E82DE69B2E000C6859 /* UIView+Ex.swift in Sources */,
+				3DBEA0E92DE69B2E000C6859 /* TSBaseVC.swift in Sources */,
+				3DBEA0EA2DE69B2E000C6859 /* UIView+Rotating.swift in Sources */,
+				3DBEA0EB2DE69B2E000C6859 /* Text+Ex.swift in Sources */,
+				3DBEA0EC2DE69B2E000C6859 /* UILabel+Ex.swift in Sources */,
+				3DBEA0ED2DE69B2E000C6859 /* TSBaseCollectionCell.swift in Sources */,
+				3DBEA0EE2DE69B2E000C6859 /* PhotoManager.swift in Sources */,
+				3DBEA0EF2DE69B2E000C6859 /* UIColor+Ex.swift in Sources */,
+				3DBEA0F02DE69B2E000C6859 /* Font+Ex.swift in Sources */,
+				3DBEA0F12DE69B2E000C6859 /* TSFileManagerTool.swift in Sources */,
+				3DBEA0F22DE69B2E000C6859 /* TSCircularProgressView.swift in Sources */,
+				3DBEA0F32DE69B2E000C6859 /* TSRandomTextTool.swift in Sources */,
+				3DBEA0F42DE69B2E000C6859 /* TSBaseView.swift in Sources */,
+				3DBEA0F52DE69B2E000C6859 /* TSBaseViewModel.swift in Sources */,
+				3DBEA0F62DE69B2E000C6859 /* TSCustomAlertController.swift in Sources */,
+				3DBEA0F72DE69B2E000C6859 /* TSImageCompress.swift in Sources */,
+				3DBEA0F82DE69B2E000C6859 /* TSGCDTimer.swift in Sources */,
+				3DBEA0FA2DE69B2E000C6859 /* TSBaseNavigationBarView.swift in Sources */,
+				3DBEA0FB2DE69B2E000C6859 /* UITextView+Ex.swift in Sources */,
+				3DBEA0FC2DE69B2E000C6859 /* UIFont+Ex.swift in Sources */,
+				3DBEA0FD2DE69B2E000C6859 /* TSCommonTool+MultDown.swift in Sources */,
+				3DBEA0FE2DE69B2E000C6859 /* UIButton+Ex.swift in Sources */,
+				3DBEA0FF2DE69B2E000C6859 /* TSCustomStackView.swift in Sources */,
+				3DBEA1002DE69B2E000C6859 /* UIViewController+Ex.swift in Sources */,
+				3DBEA1012DE69B2E000C6859 /* Date+Ex.swift in Sources */,
+				3DBEA1022DE69B2E000C6859 /* TSNetworkTool.swift in Sources */,
+				3DBEA1032DE69B2E000C6859 /* NSString+Ex.swift in Sources */,
+				3DBEA1042DE69B2E000C6859 /* Dictionary+Ex.swift in Sources */,
+				3DBEA1052DE69B2E000C6859 /* PaddedLabel.swift in Sources */,
+				3DBEA1062DE69B2E000C6859 /* GlobalImports.swift in Sources */,
+				3DBEA1072DE69B2E000C6859 /* TSPlaceholderTextView.swift in Sources */,
+				3DBEA1082DE69B2E000C6859 /* UIView+Frame.swift in Sources */,
+				3DBEA1092DE69B2E000C6859 /* CGFloat+Ex.swift in Sources */,
+				3DBEA10A2DE69B2E000C6859 /* TSProgressSlider.swift in Sources */,
+				3DBEA10B2DE69B2E000C6859 /* TSCollectionViewObserver.swift in Sources */,
+				3DBEA10C2DE69B2E000C6859 /* Component.swift in Sources */,
 				3DB4D4912DDC1F100082596A /* ASRingGeneratorVC+load.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;

+ 0 - 3
AIPlayRingtones/AppDelegate.swift

@@ -51,9 +51,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
     }
 
     func initPlatform() {
-        TSColorConfigShared.naviMianTextColor = .white
-        TSColorConfigShared.mainBg = .mainBg
-        
         DispatchQueue.main.async {//先提前调用,启动下数据库.
             debugPrint("ASRMShared.ringDBHistory.istModels.count = \(ASRMShared.ringDBHistory.listModels.count)")
         }

+ 2 - 2
AIPlayRingtones/AppPage/APAudioToRingVC/View/ASRTRStyleView.swift

@@ -80,7 +80,7 @@ class ASRTRStyleView: TSBaseView {
 
 extension ASRTRStyleView: TSSimpleCollectionViewDelegate {
     
-    func collectionView(didTrigger event: TSSmalCoacopods.TSSimpleCellEvent) {
+    func collectionView(didTrigger event: TSSimpleCellEvent) {
         switch event.action {
         case .tap:
             generateStyleIndex = event.indexPath.row
@@ -97,7 +97,7 @@ extension ASRTRStyleView: TSSimpleCollectionViewDelegate {
 
 class ASRTRStyleViewCell: TSBaseCollectionCell ,TSSimpleConfigurableView {
     
-    weak var delegate: (any TSSmalCoacopods.TSSimpleCollectionViewDelegate)?
+    weak var delegate: (any TSSimpleCollectionViewDelegate)?
     var indexPath: IndexPath = IndexPath(item: 0, section: 0)
     var data: Any? {
         didSet {

+ 2 - 2
AIPlayRingtones/AppPage/APRingTonesVC/APRingStyleVC/APRingStyleVC.swift

@@ -88,7 +88,7 @@ class APRingStyleVC: TSBaseVC {
 }
 extension APRingStyleVC: TSSimpleCollectionViewDelegate {
     
-    func collectionView(didTrigger event: TSSmalCoacopods.TSSimpleCellEvent) {
+    func collectionView(didTrigger event: TSSimpleCellEvent) {
         switch event.action {
         case .tap:
             generateStyleIndex = event.indexPath.row
@@ -106,7 +106,7 @@ extension APRingStyleVC: TSSimpleCollectionViewDelegate {
 
 class APRingStyleCell: TSBaseCollectionCell ,TSSimpleConfigurableView {
     
-    weak var delegate: (any TSSmalCoacopods.TSSimpleCollectionViewDelegate)?
+    weak var delegate: (any TSSimpleCollectionViewDelegate)?
     var indexPath: IndexPath = IndexPath(item: 0, section: 0)
     var data: Any? {
         didSet {

+ 2 - 2
AIPlayRingtones/AppPage/APRingTonesVC/APRingTonesVC/View/ASDurationColView.swift

@@ -53,7 +53,7 @@ class ASGenerateDurationColView: TSBaseView {
 }
 extension ASGenerateDurationColView: TSSimpleCollectionViewDelegate {
     
-    func collectionView(didTrigger event: TSSmalCoacopods.TSSimpleCellEvent) {
+    func collectionView(didTrigger event: TSSimpleCellEvent) {
         switch event.action {
         case .tap:
             currentSelectedIndex = event.indexPath.row
@@ -71,7 +71,7 @@ extension ASGenerateDurationColView: TSSimpleCollectionViewDelegate {
 
 class ASDurationColViewCell: TSBaseCollectionCell ,TSSimpleConfigurableView {
     
-    weak var delegate: (any TSSmalCoacopods.TSSimpleCollectionViewDelegate)?
+    weak var delegate: (any TSSimpleCollectionViewDelegate)?
     var indexPath: IndexPath = IndexPath(item: 0, section: 0)
     var data: Any? {
         didSet {

+ 1 - 1
AIPlayRingtones/AppPage/ASRingGeneratorVC/ASRingGeneratorBaseVC.swift

@@ -61,7 +61,7 @@ class ASRingGeneratorBaseVC: TSBaseVC {
     //点击空白,关闭页面
     override func createView() {
 
-        view.backgroundColor = TSColorConfigShared.mainBg.withAlphaComponent(0.6)
+        view.backgroundColor = .mainBg.withAlphaComponent(0.6)
         setNavBarViewHidden(true)
         contentView.addSubview(topBgView)
         contentView.addSubview(bottomView)

+ 30 - 0
AIPlayRingtones/AppPage/SimpleWebViewController/SimpleWebViewController.h

@@ -0,0 +1,30 @@
+//
+//  SimpleWebViewController.h
+//  AIPlayRingtones
+//
+//  Created by mini on 2025/5/27.
+//
+
+#import <UIKit/UIKit.h>
+#import <WebKit/WebKit.h>
+
+@interface SimpleWebViewController : UIViewController  <WKNavigationDelegate, WKUIDelegate>
+
+@property (nonatomic, strong) WKWebView *webView;
+@property (nonatomic, strong) UIProgressView *progressView;
+@property (nonatomic, strong) NSURL *urlToLoad;
+
+// 初始化方法
+- (instancetype)initWithURL:(NSURL *)url;
+- (instancetype)initWithURLString:(NSString *)urlString;
+
+// 加载URL
+- (void)loadURL:(NSURL *)url;
+- (void)loadURLString:(NSString *)urlString;
+
+// 刷新/前进/后退
+- (void)reload;
+- (void)goForward;
+- (void)goBack;
+
+@end

+ 213 - 0
AIPlayRingtones/AppPage/SimpleWebViewController/SimpleWebViewController.m

@@ -0,0 +1,213 @@
+//
+//  SimpleWebViewController.m
+//  AIPlayRingtones
+//
+//  Created by mini on 2025/5/27.
+//
+
+#import "SimpleWebViewController.h"
+
+@interface SimpleWebViewController ()
+
+@property (nonatomic, strong) WKWebViewConfiguration *webConfig;
+@property (nonatomic, assign) BOOL isObservingProgress;
+
+@end
+
+@implementation SimpleWebViewController
+
+#pragma mark - Initialization
+
+- (instancetype)initWithURL:(NSURL *)url {
+    self = [super init];
+    if (self) {
+        _urlToLoad = url;
+    }
+    return self;
+}
+
+- (instancetype)initWithURLString:(NSString *)urlString {
+    NSURL *url = [NSURL URLWithString:urlString];
+    return [self initWithURL:url];
+}
+
+#pragma mark - Lifecycle
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    
+    [self setupWebView];
+    [self setupProgressView];
+    [self setupNavigationItems];
+    
+    if (self.urlToLoad) {
+        [self loadURL:self.urlToLoad];
+    }
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+    [super viewWillAppear:animated];
+    
+    if (!self.isObservingProgress) {
+        [self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
+        self.isObservingProgress = YES;
+    }
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+    [super viewWillDisappear:animated];
+    
+    if (self.isObservingProgress) {
+        [self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
+        self.isObservingProgress = NO;
+    }
+}
+
+- (void)dealloc {
+    if (self.isObservingProgress) {
+        [self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
+    }
+    self.webView.navigationDelegate = nil;
+    self.webView.UIDelegate = nil;
+}
+
+#pragma mark - Setup
+
+- (void)setupWebView {
+    self.webConfig = [[WKWebViewConfiguration alloc] init];
+    
+    self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:self.webConfig];
+    self.webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+    self.webView.navigationDelegate = self;
+    self.webView.UIDelegate = self;
+    
+    [self.view addSubview:self.webView];
+}
+
+- (void)setupProgressView {
+    CGFloat progressBarHeight = 2.f;
+    CGRect navigationBarBounds = self.navigationController.navigationBar.bounds;
+    CGRect barFrame = CGRectMake(0, navigationBarBounds.size.height - progressBarHeight, navigationBarBounds.size.width, progressBarHeight);
+    
+    self.progressView = [[UIProgressView alloc] initWithFrame:barFrame];
+    self.progressView.progressTintColor = [UIColor blueColor];
+    self.progressView.trackTintColor = [UIColor clearColor];
+    self.progressView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
+    
+    [self.navigationController.navigationBar addSubview:self.progressView];
+}
+
+- (void)setupNavigationItems {
+    UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"chevron.left"]
+                                                                style:UIBarButtonItemStylePlain
+                                                               target:self
+                                                               action:@selector(goBack)];
+    
+    UIBarButtonItem *forwardItem = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"chevron.right"]
+                                                                   style:UIBarButtonItemStylePlain
+                                                                  target:self
+                                                                  action:@selector(goForward)];
+    
+    UIBarButtonItem *refreshItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh
+                                                                                 target:self
+                                                                                 action:@selector(reload)];
+    
+    self.navigationItem.leftBarButtonItems = @[backItem, forwardItem];
+    self.navigationItem.rightBarButtonItem = refreshItem;
+}
+
+#pragma mark - Public Methods
+
+- (void)loadURL:(NSURL *)url {
+    if (!url) return;
+    
+    NSURLRequest *request = [NSURLRequest requestWithURL:url];
+    [self.webView loadRequest:request];
+}
+
+- (void)loadURLString:(NSString *)urlString {
+    NSURL *url = [NSURL URLWithString:urlString];
+    [self loadURL:url];
+}
+
+- (void)reload {
+    [self.webView reload];
+}
+
+- (void)goForward {
+    if ([self.webView canGoForward]) {
+        [self.webView goForward];
+    }
+}
+
+- (void)goBack {
+    if ([self.webView canGoBack]) {
+        [self.webView goBack];
+    } else {
+        [self.navigationController popViewControllerAnimated:YES];
+    }
+}
+
+#pragma mark - KVO
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
+    if ([keyPath isEqualToString:@"estimatedProgress"]) {
+        CGFloat progress = self.webView.estimatedProgress;
+        [self.progressView setProgress:progress animated:YES];
+        
+        if (progress >= 1.0) {
+            [UIView animateWithDuration:0.3 delay:0.3 options:UIViewAnimationOptionCurveEaseOut animations:^{
+                self.progressView.alpha = 0.0;
+            } completion:^(BOOL finished) {
+                [self.progressView setProgress:0.0 animated:NO];
+                self.progressView.alpha = 1.0;
+            }];
+        }
+    }
+}
+
+#pragma mark - WKNavigationDelegate
+
+- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
+    self.progressView.alpha = 1.0;
+}
+
+- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
+    self.title = webView.title;
+}
+
+- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
+    [self.progressView setProgress:0.0 animated:NO];
+    self.progressView.alpha = 0.0;
+    
+    // 处理错误
+    NSLog(@"WebView加载失败: %@", error.localizedDescription);
+}
+
+- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
+    // 可以在这里处理导航策略,比如拦截某些URL
+    decisionHandler(WKNavigationActionPolicyAllow);
+}
+
+#pragma mark - WKUIDelegate
+
+- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
+    UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert];
+    [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
+        completionHandler();
+    }]];
+    [self presentViewController:alert animated:YES completion:nil];
+}
+
+- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler {
+    UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert];
+    [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
+        completionHandler(YES);
+    }]];
+    [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
+        completionHandler(NO);
+    }]];
+    [self presentViewController:alert animated:YES completion:nil];
+}
+
+@end

+ 143 - 0
AIPlayRingtones/Classes/BaseClass/TSBaseCollectionCell.swift

@@ -0,0 +1,143 @@
+//
+//  CWBaseCollectionViewCell.swift
+//  ClockWidget
+//
+//  Created by fff on 2024/11/12.
+//
+
+
+open class TSBaseCollectionCell: UICollectionViewCell,TSComponentView  {
+    
+    public var colComponent:TSCollectionViewComponent?
+    public var colAttributes:[String : Any]?
+    
+    public lazy var bgContentView:UIView = {
+        let view = UIView()
+        view.backgroundColor = .clear
+        return view
+    }()
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        self.backgroundColor = .clear
+        self.addSubview(bgContentView)
+        bgContentView.snp.makeConstraints { make in
+            make.top.leading.trailing.bottom.equalTo(0)
+        }
+        creatUI()
+        dealThings()
+    }
+    
+    required public init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    open func creatUI(){
+        
+    }
+    
+    open func dealThings(){
+        
+    }
+    
+    open func renderView(with object: Any?, component: TSCollectionViewComponent, attributes: [String : Any]?) {
+        self.colComponent = component
+        self.colAttributes = attributes
+    }
+    
+    deinit {
+//        debugPrint("<---deinit \(String(describing: self))")
+    }
+    
+    public var cellIndexPath:IndexPath?{
+        if let attributes = colAttributes , let IndexPath = attributes[kIndexPath] as? IndexPath{
+            return IndexPath
+        }
+        return nil
+    }
+    
+    public var itemActionHandler: ((Any?, IndexPath) -> Void)?{
+        if let colComponent = colComponent,let itemActionHandler = colComponent.itemActionHandler {
+            return itemActionHandler
+        }
+        return nil
+    }
+    
+    public func actionHandler(any:Any?){
+        if let sectionActionHandler = itemActionHandler ,let indexPath = cellIndexPath{
+            sectionActionHandler(any,indexPath)
+        }
+    }
+}
+
+extension TSBaseCollectionCell{
+
+}
+
+
+
+
+
+
+open class TSBaseCollectionnReusableView : UICollectionReusableView ,TSComponentView  {
+    
+    public var colComponent:TSCollectionViewComponent?
+    public var colAttributes:[String : Any]?
+    
+    public lazy var bgContentView:UIView = {
+        let view = UIView()
+        view.backgroundColor = .clear
+        return view
+    }()
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        self.backgroundColor = .clear
+        self.addSubview(bgContentView)
+        bgContentView.snp.makeConstraints { make in
+            make.top.leading.trailing.bottom.equalTo(0)
+        }
+        creatUI()
+    }
+    
+    required public init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    open func creatUI(){
+        
+    }
+
+    open func renderView(with object: Any?, component: TSCollectionViewComponent, attributes: [String : Any]?) {
+        self.colComponent = component
+        self.colAttributes = attributes
+    }
+    
+    
+    deinit {
+//        debugPrint("<---deinit \(String(describing: self))")
+    }
+    
+}
+
+extension TSBaseCollectionnReusableView{
+    public var cellIndexPath:IndexPath?{
+        if let attributes = colAttributes , let IndexPath = attributes[kIndexPath] as? IndexPath{
+            return IndexPath
+        }
+        return nil
+    }
+    
+    public var sectionActionHandler: ((Any?, IndexPath) -> Void)?{
+        if let colComponent = colComponent,let sectionActionHandler = colComponent.sectionActionHandler {
+            return sectionActionHandler
+        }
+        return nil
+    }
+    
+    public func actionHandler(any:Any?){
+        if let sectionActionHandler = sectionActionHandler ,let indexPath = cellIndexPath{
+            sectionActionHandler(any,indexPath)
+        }
+    }
+}

+ 52 - 0
AIPlayRingtones/Classes/BaseClass/TSBaseModel.swift

@@ -0,0 +1,52 @@
+//
+//  TSBaseModel.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/22.
+//
+
+import ObjectMapper
+
+open class TSBaseModel: NSObject, Mappable, NSCopying {
+    public static func model(data: Data) -> TSBaseModel? {
+        do {
+            let object = try JSONSerialization.jsonObject(with: data)
+            if let dict = object as? [String: AnyHashable] {
+                return Self.init(json: dict)
+            }
+        } catch _ {
+            return nil
+        }
+        return nil
+    }
+    
+    open var data: Data? {
+        let json = toJSON()
+        do {
+            let data = try JSONSerialization.data(withJSONObject: json)
+            return data
+        } catch _ {
+            return nil
+        }
+    }
+    
+    required convenience public init?(map: ObjectMapper.Map) {
+        self.init()
+        mapping(map: map)
+    }
+    
+    required convenience public init?(json: [String: Any]) {
+        self.init(JSON: json)
+    }
+    
+    open func mapping(map: ObjectMapper.Map) {}
+    
+    open func copy(with zone: NSZone? = nil) -> Any {
+        let json = toJSON()
+        return Self.init(JSON: json) ?? Self.init(JSON: [:])!
+    }
+    
+//    deinit {
+//        debugPrint("♻️♻️♻️ TGRootViewController -> \(type(of: self)) deinit ♻️♻️♻️")
+//    }
+}

+ 173 - 0
AIPlayRingtones/Classes/BaseClass/TSBaseNavigationBarView.swift

@@ -0,0 +1,173 @@
+//
+//  TSBaseNavigationBarView.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+
+open class TSBaseNavContentBarView: UIView {
+    
+    open lazy var statuView: UIView = {
+       let view = UIView()
+       return view
+   }()
+
+    open lazy var barView: UIView = {
+       let view = UIView()
+       return view
+   }()
+    
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        
+        addSubview(statuView)
+        addSubview(barView)
+        
+        statuView.snp.makeConstraints { make in
+            make.top.leading.trailing.equalTo(0)
+            make.height.equalTo(k_Height_StatusBar)
+        }
+
+        barView.snp.makeConstraints { make in
+            make.leading.trailing.equalTo(0)
+            make.top.equalTo(k_Height_StatusBar)
+            make.height.equalTo(k_Height_NavBar)
+        }
+
+    }
+    
+    
+    required public init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+}
+
+open class TSNormalNavigationBarView: TSBaseNavContentBarView {
+
+    // MARK: - Properties
+     var navBgColor: UIColor!
+     var navTitleColor: UIColor!
+     var navTitleFont: UIFont!
+     var navLeftFont: UIFont!
+     var navRightFont: UIFont!
+
+    public lazy var leftNavBtn: UIButton = {
+        let button = createNavButton()
+        button.contentHorizontalAlignment = .left
+        button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
+        return button
+    }()
+
+    public lazy var titleNavBtn: UIButton = {
+        let button = createNavButton()
+        button.titleLabel?.font = navTitleFont
+        return button
+    }()
+
+    public lazy var rightNavBtn: UIButton = {
+        let button = createNavButton()
+        button.titleLabel?.font = navRightFont
+        button.titleLabel?.adjustsFontSizeToFitWidth = true
+        button.contentHorizontalAlignment = .right
+        button.setTitleColor(.themeColor, for: .normal)
+        button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
+        return button
+    }()
+
+    // MARK: - Initializers
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        createData()
+        createView()
+    }
+    
+    @MainActor required public init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+
+    // MARK: - Setup Methods
+    private func createData() {
+        navBgColor = .clear
+        navTitleColor = "#0E1550".uiColor
+        navTitleFont = .font(size: 18,weight:.medium)
+        navLeftFont = .font(size: 16,weight:.medium)
+        navRightFont = .font(size: 16,weight:.medium)
+    }
+
+    private func createView() {
+//        let space: CGFloat = 10
+        let margins: CGFloat = 16
+
+        backgroundColor = navBgColor
+
+  
+        barView.addSubview(leftNavBtn)
+        barView.addSubview(titleNavBtn)
+        barView.addSubview(rightNavBtn)
+
+        leftNavBtn.snp.makeConstraints { make in
+            make.leading.equalTo(margins)
+            make.centerY.equalToSuperview()
+            make.width.height.equalTo(44.0)
+        }
+        
+        rightNavBtn.snp.makeConstraints { make in
+            make.trailing.equalTo(-margins)
+            make.centerY.equalToSuperview()
+            make.width.height.equalTo(44.0)
+        }
+        
+         titleNavBtn.snp.makeConstraints { make in
+             make.leading.equalTo(leftNavBtn.snp.trailing).offset(margins)
+             make.trailing.equalTo(rightNavBtn.snp.leading).offset(-margins)
+             make.centerY.equalToSuperview()
+             make.height.equalTo(44.0)
+         }
+         
+    }
+
+    // MARK: - Helper Methods
+    public func setTitleName(_ name: String) -> UIButton {
+        titleNavBtn.setTitle(name, for: .normal)
+        return titleNavBtn
+    }
+
+    public func setTitleNavigationItem(name: String, imageName: String, target: Any?, action: Selector) -> UIButton {
+        setNavButton(button: titleNavBtn, name: name, imageName: imageName, target: target, action: action)
+        return titleNavBtn
+    }
+
+    public func setLeftNavigationItem(name: String, imageName: String, target: Any?, action: Selector) -> UIButton {
+        setNavButton(button: leftNavBtn, name: name, imageName: imageName, target: target, action: action)
+        return leftNavBtn
+    }
+
+    public func setRightNavigationItem(name: String, imageName: String, target: Any?, action: Selector) -> UIButton {
+        setNavButton(button: rightNavBtn, name: name, imageName: imageName, target: target, action: action)
+        return rightNavBtn
+    }
+
+    public func setNavButton(button: UIButton, name: String, imageName: String, target: Any?, action: Selector) {
+        button.setTitle(name, for: .normal)
+        
+        if imageName.count > 0 {
+            let image = UIImage(bundleNamed: imageName)?.withRenderingMode(.alwaysOriginal)
+            if let image = image {
+                button.setImage(image.mirror, for: .normal)
+            }
+        }
+
+        button.addTarget(target, action: action, for: .touchUpInside)
+    }
+
+    public func createNavButton() -> UIButton {
+        let button = UIButton()
+        button.setTitleColor(navTitleColor, for: .normal)
+        button.titleLabel?.font = navRightFont
+        button.titleLabel?.adjustsFontSizeToFitWidth = true
+        return button
+    }
+}

+ 110 - 0
AIPlayRingtones/Classes/BaseClass/TSBaseNavigationC.swift

@@ -0,0 +1,110 @@
+//
+//  TSBaseNavigationC.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+import UIKit
+
+open class TSBaseNavigationC: UINavigationController, UIGestureRecognizerDelegate, UINavigationControllerDelegate, UINavigationBarDelegate {
+
+//    /// Whether to use system pop gesture. If false, full-screen pop gesture will be set.
+//    static let useSystemGesture: Bool = false
+//    /// Whether to enable global pop gestures. The default is true.
+//    static let popGestureEnabled: Bool = true
+//
+//    private var vcsDic: [String: Bool] = [:]
+
+    open override func viewDidLoad() {
+        super.viewDidLoad()
+        configureNavigationBar()
+//        configureNavigationGestures()
+        view.backgroundColor = .clear
+        interactivePopGestureRecognizer?.isEnabled = true;
+    }
+
+    private func configureNavigationBar() {
+        self.navigationBar.isHidden = true
+    }
+//
+//    private func configureNavigationGestures() {
+//        vcsDic = [:]
+//        self.interactivePopGestureRecognizer?.delegate = self
+//    }
+//
+//    // Override pushViewController to initialize interactive pop gesture here.
+//    override func pushViewController(_ viewController: UIViewController, animated: Bool) {
+//        // Hide bottom bar only for the first pushed view controller
+//        viewController.hidesBottomBarWhenPushed = self.viewControllers.count == 1
+//        super.pushViewController(viewController, animated: animated)
+//        self.interactivePopGestureRecognizer?.isEnabled = TSBaseNavigationC.popGestureEnabled
+//    }
+//
+//    // MARK: - UIGestureRecognizerDelegate
+//
+//    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
+//        // Disable pop gesture if it's the root view controller
+//        if self.viewControllers.count == 1 {
+//            return false
+//        }
+//        let vcKey = vcKeyFromVC(self.topViewController)
+//        if let vcKey = vcKey, let isEnabled = vcsDic[vcKey] {
+//            return isEnabled
+//        }
+//        return self.interactivePopGestureRecognizer?.isEnabled ?? true
+//    }
+//
+//    // MARK: - UINavigationControllerDelegate
+//
+//    func navigationController(_ navigationController: UINavigationController,
+//                              willShow viewController: UIViewController,
+//                              animated: Bool) {
+//        if #available(iOS 10.0, *) {
+//            viewController.transitionCoordinator?.notifyWhenInteractionChanges { context in
+//                if context.isCancelled { return }
+//                debugPrint("notifyWhenInteractionChanges context.isCancelled=\(context.isCancelled)")
+//            }
+//        }
+//    }
+//
+//    func navigationController(_ navigationController: UINavigationController,
+//                              didShow viewController: UIViewController,
+//                              animated: Bool) {
+//        let vcKey = vcKeyFromVC(viewController)
+//        if let vcKey = vcKey, vcsDic[vcKey] == nil {
+//            // Save pop gesture enabled value for each child view controller
+//            vcsDic[vcKey] = self.interactivePopGestureRecognizer?.isEnabled ?? true
+//        }
+//    }
+//
+//    // MARK: - UINavigationBarDelegate
+//
+//    func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
+//        return true
+//    }
+//
+//    // MARK: - Convenience Methods
+//
+//    private func vcKeyFromVC(_ viewController: UIViewController?) -> String? {
+//        guard let viewController = viewController else { return nil }
+//        return String(describing: type(of: viewController))
+//    }
+//
+//    func setSpecifiedViewControllerInteractivePopGestureEnabled(_ enabled: Bool) {
+//        let vcKey = vcKeyFromVC(self.topViewController)
+//        if let vcKey = vcKey {
+//            vcsDic[vcKey] = enabled
+//        }
+//    }
+//
+//    // MARK: - Status Bar
+//
+//    override var childForStatusBarStyle: UIViewController? {
+//        return self.topViewController
+//    }
+//
+//    deinit {
+////        debugPrint("TGRootNavigationController deinitialized")
+//    }
+}

+ 40 - 0
AIPlayRingtones/Classes/BaseClass/TSBaseTabViewCell.swift

@@ -0,0 +1,40 @@
+//
+//  CWBaseTabViewCell.swift
+//  ClockWidget
+//
+//  Created by fff on 2024/11/12.
+//
+
+
+open class TSBaseTabViewCell: UITableViewCell {
+    
+    open lazy var bgContentView:UIView = {
+        let view = UIView()
+        view.backgroundColor = .clear
+        return view
+    }()
+    
+    public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+        super.init(style: style, reuseIdentifier: reuseIdentifier)
+        
+        self.selectionStyle = .none
+        self.backgroundColor = .clear
+        self.addSubview(bgContentView)
+        bgContentView.snp.makeConstraints { make in
+            make.top.leading.trailing.bottom.equalTo(0)
+        }
+        creatUI()
+    }
+    
+    required public init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    open func creatUI(){
+        
+    }
+    
+    deinit {
+//        debugPrint("<---deinit \(String(describing: self))")
+    }
+}

+ 224 - 0
AIPlayRingtones/Classes/BaseClass/TSBaseVC.swift

@@ -0,0 +1,224 @@
+//
+//  TSBaseVC.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+import UIKit
+import Combine
+
+open class TSBaseVC: UIViewController {
+  
+    public var closePageComplete:(()->Void)?
+    
+    public var cancellable: [AnyCancellable] = []
+    
+    public var blurEffect: UIBlurEffect?
+    public lazy var navBlurView: UIVisualEffectView = {
+        let view = UIVisualEffectView()
+        return view
+    }()
+    
+    public lazy var contentView: UIView = {
+        let view = UIView()
+        return view
+    }()
+    
+    
+    public lazy var navBarContentView: UIView = {
+        let view = UIView()
+        view.backgroundColor = .clear
+        return view
+    }()
+    
+    public lazy var normalNavBarView: TSNormalNavigationBarView = {
+        let view = TSNormalNavigationBarView()
+        return view
+    }()
+    
+    
+    public lazy var viewBgImageView: UIImageView = {
+        let view = UIImageView()
+        view.backgroundColor = .clear
+        return view
+    }()
+
+    public lazy var netWorkView: UIView = {
+        let view = UIView()
+        view.backgroundColor = .clear
+        return view
+    }()
+    
+    
+    public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
+        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
+    }
+
+    required public init?(coder: NSCoder) {
+        super.init(coder: coder)
+    }
+
+    /// 提供一个无参数的默认初始化方法
+    public init() {
+        super.init(nibName: nil, bundle: nil)
+    }
+    
+    open override func viewDidLoad() {
+        super.viewDidLoad()
+        setupSuperSomeView()
+        createData()
+        createView()
+        dealThings()
+    }
+    
+    private func setupSuperSomeView() {
+        edgesForExtendedLayout = [.all]
+        
+        view.addSubview(contentView)
+        view.addSubview(navBarContentView)
+        
+        contentView.snp.makeConstraints { make in
+            make.top.equalTo(k_Nav_Height)
+            make.leading.trailing.bottom.equalToSuperview()
+        }
+        
+        navBarContentView.snp.makeConstraints { make in
+            make.leading.top.trailing.equalToSuperview()
+            make.height.equalTo(k_Nav_Height)
+        }
+
+        view.backgroundColor = .mainBg
+        view.layoutIfNeeded()
+    }
+    
+    
+    
+    
+    public func setViewBgImageNamed(named:String){
+        if viewBgImageView.superview == nil {
+            view.insertSubview(viewBgImageView, at: 0)
+            viewBgImageView.snp.makeConstraints { make in
+                make.edges.equalToSuperview()
+            }
+        }
+        viewBgImageView.image = UIImage(named: named)
+    }
+    
+    
+    //子类重写
+    open func createData(){
+        
+        
+    }
+    open func createView(){
+        
+        
+    }
+    
+    open func dealThings(){
+        
+        
+    }
+    
+
+    public func addNormalNavBarView(){
+        navBarContentView.addSubview(normalNavBarView)
+        normalNavBarView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+    }
+
+
+    open override func viewWillAppear(_ animated: Bool) {
+        debugPrint("进入------>\(String(describing: type(of: self)))")
+    }
+
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+        cancellable.removeAll()
+        closePageComplete?()
+        debugPrint("♻️♻️♻️ TGRootViewController -> \(type(of: self)) deinit ♻️♻️♻️")
+    }
+
+    // MARK: - 导航栏方法
+
+    public func setTitleText(_ title: String) {
+       _ = normalNavBarView.setTitleName(NSLocalizedString(title, comment: ""))
+    }
+
+    public func setPageTitle(_ title: String) {
+        let pageTitle = title
+        let backTitle = " "
+        setTitleText(pageTitle)
+        _ = setNavigationItem(backTitle, imageName: "navi_back_white", direction: .left, action: #selector(navBarClickLeftAction))
+    }
+
+    public func setNavigationItem(_ name: String, imageName: String, direction: NSTextAlignment, action: Selector) -> UIButton {
+        if direction == .left {
+            return normalNavBarView.setLeftNavigationItem(name: name, imageName: imageName, target: self, action: action)
+        } else {
+            return normalNavBarView.setRightNavigationItem(name: name, imageName: imageName, target: self, action: action)
+        }
+    }
+
+    @objc open func navBarClickLeftAction() {
+        debugPrint("navBarClickLeftAction -> \(type(of: self))")
+        pop()
+    }
+
+    public func pop() {
+        if navigationController == nil {
+            dismiss(animated: true, completion: nil)
+        } else if navigationController?.presentingViewController != nil, navigationController?.viewControllers.count == 1 {
+            navigationController?.dismiss(animated: true, completion: nil)
+        } else {
+            navigationController?.popViewController(animated: true)
+        }
+    }
+
+}
+
+
+extension TSBaseVC {
+    
+    public func setNavBarViewHidden(_ isHidden: Bool) {
+        navBarContentView.snp.updateConstraints { make in
+            make.height.equalTo(isHidden ? 0 : k_Nav_Height)
+        }
+        
+        contentView.snp.updateConstraints { make in
+            make.top.equalTo(isHidden ? 0 : k_Nav_Height)
+        }
+    }
+    
+    public var isViewVisible: Bool {
+        return isViewLoaded && view.window != nil && presentedViewController == nil
+    }
+    
+    public func addPullDownClosePage() {
+        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handleDismissPan(_:)))
+        view.addGestureRecognizer(panGesture)
+    }
+
+    // 手势处理方法
+     @objc public func handleDismissPan(_ gesture: UIPanGestureRecognizer) {
+        let translation = gesture.translation(in: view)
+        let progress = translation.y / view.bounds.height
+        
+        switch gesture.state {
+        case .changed:
+            view.transform = CGAffineTransform(translationX: 0, y: max(0, translation.y))
+        case .ended:
+            if progress > 0.5 || gesture.velocity(in: view).y > 1000 {
+                dismiss(animated: true)
+            } else {
+                UIView.animate(withDuration: 0.3) {
+                    self.view.transform = .identity
+                }
+            }
+        default:
+            break
+        }
+    }
+}

+ 41 - 0
AIPlayRingtones/Classes/BaseClass/TSBaseView.swift

@@ -0,0 +1,41 @@
+//
+//  TSBaseView.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/24.
+//
+
+open class TSBaseView : UIView {
+
+    public lazy var contentView:UIView = {
+        let view = UIView()
+        view.backgroundColor = .clear
+        return view
+    }()
+    
+    public override init(frame: CGRect) {
+        super.init(frame: frame)
+        self.backgroundColor = .clear
+        self.addSubview(contentView)
+        contentView.snp.makeConstraints { make in
+            make.top.leading.trailing.bottom.equalTo(0)
+        }
+        creatUI()
+        dealThings()
+    }
+    
+    required public init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    open func creatUI(){
+        
+    }
+    
+    
+    open func dealThings(){
+        
+        
+    }
+    
+}

+ 24 - 0
AIPlayRingtones/Classes/BaseClass/TSBaseViewModel.swift

@@ -0,0 +1,24 @@
+//
+//  TSBaseViewModel.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/1/20.
+//
+
+open class TSBaseViewModel {
+    
+    public init() {
+        creatData()
+        dealThings()
+    }
+    
+    public func creatData () {
+        
+        
+    }
+    
+    public func dealThings () {
+        
+        
+    }
+}

+ 152 - 0
AIPlayRingtones/Classes/BaseClass/TSBasicItemModel.swift

@@ -0,0 +1,152 @@
+//
+//  TSBasicItemModel.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+open class TSBasicItemModel {
+    // MARK: - Properties
+    public var index: Int = 0
+//    var clickBlock: ((Any?) -> Void)?
+    public var tapBlock: ((TSBasicItemModel, Int, Any?) -> Void)?
+    public var updateDataBlock: ((TSBasicItemModel) -> Void)?
+    
+    public var leftImageName: String?
+    public var leftImage: UIImage?
+    public var leftImagePath: String?
+    public var leftTitle: String?
+    public var leftSubTitle: String?
+    public var rightString: String?
+    public  var rightSubString: String?
+    public var rightIntValue: Int = 0
+    public var itemData: Any?
+    public var identifier: String?
+    public var parametView: UIView?
+    public var rightIsHave: Bool = false
+    public var bottomIsHave: Bool = false
+    public var isSelected: Bool = false
+    public var isWhether: Bool = false
+    public var rightViewStyle: Int = 0
+    public var rectCorner: UIRectCorner = []
+    public var topSpace: Bool = false
+    public var bottomSpace: Bool = false
+    public var height: CGFloat = 0.0
+    public var width: CGFloat = 0.0
+    public var type: Int = 0
+    public weak var attributionSection: TSBasicSectionModel?
+    
+    public init() {
+        
+    }
+    
+    // MARK: - Methods
+    public func setCornerView(_ view: UIView, corner: CGFloat) {
+        if rectCorner.rawValue == 0 || rectCorner.isEmpty {
+            view.layer.cornerRadius = 0
+        } else {
+            let path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: rectCorner, cornerRadii: CGSize(width: corner, height: corner))
+            let mask = CAShapeLayer()
+            mask.path = path.cgPath
+            view.layer.mask = mask
+        }
+    }
+    
+    public func updateData() {
+        updateDataBlock?(self)
+    }
+    
+    
+    public static func createItemModel(leftTitle: String,
+                                rightViewStyle: Int,
+                                rightString: String,
+                                rightIsHave: Bool,
+                                height: CGFloat,
+                                rectCorner: UIRectCorner,
+                                tapBlock: @escaping ((TSBasicItemModel, Int, Any?) -> Void)) -> TSBasicItemModel {
+        let model = TSBasicItemModel()
+        model.leftTitle = leftTitle
+        model.rightViewStyle = rightViewStyle
+        model.rightString = rightString
+        model.rightIsHave = rightIsHave
+        model.height = height
+        model.rectCorner = rectCorner
+        model.tapBlock = tapBlock
+        return model
+    }
+    
+    
+    public static func setEdge(with dataArray: [TSBasicItemModel]) {
+        guard !dataArray.isEmpty else { return }
+        if dataArray.count == 1 {
+            dataArray.first?.rectCorner = .allCorners
+        } else {
+            dataArray.first?.rectCorner = [.topLeft, .topRight]
+            dataArray.last?.rectCorner = [.bottomLeft, .bottomRight]
+        }
+    }
+}
+
+
+
+open class TSBasicSectionModel {
+    // MARK: - Properties
+    open var index: Int = 0
+    open var sectionData: Any?
+    open var leftTitle: String?
+    open var leftSubTitle: String?
+    open var rightString: String?
+    open var rightSubString: String?
+    open var rightIsHave: Bool = false
+    open var bottomIsHave: Bool = false
+    open var topIsHave: Bool = false
+    open var isOpen: Bool = false
+    open var type: Int = 0
+    open var rectCorner: UIRectCorner = []
+    open var height: CGFloat = 0.0
+    open var isFooter: Bool = false
+    open var itemsArray: [TSBasicItemModel] = []
+    open var selectedItemsIndex: Int = 0
+    
+    public init() {
+        
+    }
+    
+    // MARK: - Methods
+    public func setCornerView(_ view: UIView, corner: CGFloat) {
+        if rectCorner.rawValue == 0 || rectCorner.isEmpty {
+            view.layer.cornerRadius = 0
+        } else {
+            let path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: rectCorner, cornerRadii: CGSize(width: corner, height: corner))
+            let mask = CAShapeLayer()
+            mask.path = path.cgPath
+            view.layer.mask = mask
+        }
+    }
+    
+    public func addSubItemModel(_ itemModel: TSBasicItemModel) {
+        itemModel.attributionSection = self
+        itemsArray.append(itemModel)
+    }
+    
+    public static func createSectionModel(leftTitle: String, height: CGFloat, index: Int = 0) -> TSBasicSectionModel {
+        let model = TSBasicSectionModel()
+        model.leftTitle = leftTitle
+        model.height = height
+        model.index = index
+        return model
+    }
+    
+    public static func setEdge(with dataArray: [TSBasicSectionModel]) {
+        for section in dataArray {
+            let itemsArray = section.itemsArray
+            guard !itemsArray.isEmpty else { continue }
+            if itemsArray.count == 1 {
+                itemsArray.first?.rectCorner = .allCorners
+            } else {
+                itemsArray.first?.rectCorner = [.topLeft, .topRight]
+                itemsArray.last?.rectCorner = [.bottomLeft, .bottomRight]
+            }
+        }
+    }
+}

+ 78 - 0
AIPlayRingtones/Classes/Ex/AVAsset+Ex.swift

@@ -0,0 +1,78 @@
+//
+//  AVAsset+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/23.
+//
+
+import UIKit
+import AVFoundation
+
+public extension AVAsset {
+    func getImage(at time: TimeInterval, videoComposition: AVVideoComposition? = nil) -> UIImage? {
+        let assetImageGenerator = AVAssetImageGenerator(asset: self)
+        assetImageGenerator.appliesPreferredTrackTransform = true
+        assetImageGenerator.videoComposition = videoComposition
+        assetImageGenerator.apertureMode = .encodedPixels
+        do {
+            let thumbnailImageRef = try assetImageGenerator.copyCGImage(
+                at: CMTime(
+                    value: CMTimeValue(time),
+                    timescale: duration.timescale
+                ),
+                actualTime: nil
+            )
+            let image = UIImage.init(cgImage: thumbnailImageRef)
+            return image
+        } catch {
+            return nil
+        }
+    }
+    func getImage(
+        at time: TimeInterval,
+        videoComposition: AVVideoComposition? = nil,
+        imageGenerator: ((AVAssetImageGenerator) -> Void)? = nil,
+        completion: @escaping (AVAsset, UIImage?, AVAssetImageGenerator.Result) -> Void
+    ) {
+        loadValuesAsynchronously(forKeys: ["duration"]) { [weak self] in
+            guard let self = self else {
+                DispatchQueue.main.async {
+                    completion(.init(), nil, .failed)
+                }
+                return
+            }
+            if self.statusOfValue(forKey: "duration", error: nil) != .loaded {
+                DispatchQueue.main.async {
+                    completion(self, nil, .failed)
+                }
+                return
+            }
+            let generator = AVAssetImageGenerator(asset: self)
+            generator.appliesPreferredTrackTransform = true
+            generator.videoComposition = videoComposition
+            let time = CMTime(value: CMTimeValue(time), timescale: self.duration.timescale)
+            let array = [NSValue(time: time)]
+            generator.generateCGImagesAsynchronously(
+                forTimes: array
+            ) { (_, cgImage, _, result, _) in
+                if let image = cgImage, result == .succeeded {
+                    var image = UIImage(cgImage: image)
+                    if image.imageOrientation != .up,
+                       let img = image.normalizedImage() {
+                        image = img
+                    }
+                    DispatchQueue.main.async {
+                        completion(self, image, result)
+                    }
+                }else {
+                    DispatchQueue.main.async {
+                        completion(self, nil, result)
+                    }
+                }
+            }
+            DispatchQueue.main.async {
+                imageGenerator?(generator)
+            }
+        }
+    }
+}

+ 47 - 0
AIPlayRingtones/Classes/Ex/Array+Ex.swift

@@ -0,0 +1,47 @@
+//
+//  Array+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+public extension Array {
+    
+    func safeString(At index: Int) -> String {
+        return safeObject(At: index, defaultValue: "")
+    }
+    
+    func safeStringInt(At index: Int) -> String {
+        return safeObject(At: index, defaultValue: "0")
+    }
+    
+    func safeArray(At index: Int) -> [Any] {
+        return safeObject(At: index, defaultValue: [])
+    }
+    
+    func safeDictionary(At index: Int) -> [String: Any] {
+        return safeObject(At: index, defaultValue: [:])
+    }
+    
+    func safeNumber(At index: Int) -> NSNumber {
+        return safeObject(At: index, defaultValue: NSNumber(value: 0))
+    }
+    
+    func safeObject(At index: Int) -> Any {
+        return safeObject(At: index, defaultValue: NSObject())
+    }
+    
+    private func safeObject<T>(At index: Int, defaultValue: T) -> T {
+        guard index >= 0 && index < count else { return defaultValue }
+        let value = self[index]
+        if value is NSNull { return defaultValue }
+        return value as? T ?? defaultValue
+    }
+    
+    func safeObj(At index: Int) -> Element? {
+        if index >= 0,  index < count {
+            return self[index]
+        }
+        return nil
+    }
+}

+ 32 - 0
AIPlayRingtones/Classes/Ex/Bundle+Ex.swift

@@ -0,0 +1,32 @@
+//
+//  Bundle+Ex.swift
+//  Pods
+//
+//  Created by fff on 2025/2/7.
+//
+
+
+open class TSMyBundleClass {}
+
+public extension Bundle {
+    
+    static var environmentName: String {
+        let bundle = Bundle(for: TSMyBundleClass.self)  // 你的类名
+        return bundle.infoDictionary?["CFBundleName"] as? String ?? "Unknown"
+    }
+    
+    static var environmentBundle:Bundle{
+        if let bundlePath = Bundle(for: TSMyBundleClass.self).path(forResource: environmentName, ofType: "bundle"),
+           let bundle = Bundle(path: bundlePath) {
+            return bundle
+        } else {
+            return Bundle.main
+        }
+    }
+}
+
+public extension UIImage {
+    public convenience init?(bundleNamed name: String) {
+        self.init(named: name, in: Bundle.environmentBundle, compatibleWith: nil)
+    }
+}

+ 55 - 0
AIPlayRingtones/Classes/Ex/CGFloat+Ex.swift

@@ -0,0 +1,55 @@
+//
+//  CGFloat+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/23.
+//
+
+public extension CGFloat {
+    
+    static var max: CGFloat {
+        CGFloat(MAXFLOAT)
+    }
+    
+    var compressionQuality: CGFloat {
+        if self > 30000000 {
+            return 30000000 / self
+        }else if self > 15000000 {
+            return 10000000 / self
+        }else if self > 10000000 {
+            return 6000000 / self
+        }else {
+            return 3000000 / self
+        }
+    }
+    
+    var transitionCompressionQuality: CGFloat {
+        if self > 6000000 {
+            return 3000000 / self
+        }else if self > 3000000 {
+            return 1000000 / self
+        }else if self > 1000000 {
+            return 600000 / self
+        }else {
+            return 1000000 / self
+        }
+    }
+}
+
+public extension Float {
+    //float 转 分秒
+    func floatToMinuteSecond() -> String {
+        // 将 Float 类型的时间转换为整数秒
+        let totalSeconds = Int(self)
+        // 计算分钟数
+        let minutes = totalSeconds / 60
+        // 计算剩余的秒数
+        let seconds = totalSeconds % 60
+        // 将分钟数和秒数格式化为两位数的字符串
+        let minuteString = String(format: "%02d", minutes)
+        let secondString = String(format: "%02d", seconds)
+        // 拼接成分秒格式的字符串
+        return "\(minuteString):\(secondString)"
+    }
+}
+

+ 49 - 0
AIPlayRingtones/Classes/Ex/Date+Ex.swift

@@ -0,0 +1,49 @@
+//
+//  Date+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+public extension Date {
+    private static var HmsFormatter: DateFormatter {
+        let formatter = DateFormatter()
+        formatter.dateFormat = "HH:mm:ss:SSS"
+        formatter.locale = Locale(identifier: "en")
+        return formatter
+    }
+    
+    static var hmsString: String {
+        return HmsFormatter.string(from: Date())
+    }
+    
+    static var timestampString: String {
+        return String(timestampInt)
+    }
+    
+    
+    static var timestampInt: Int {
+        return Int(Date().timeIntervalSince1970)
+    }
+    
+    var dateTimeString:String {
+        // 创建 DateFormatter 实例
+        let dateFormatter = DateFormatter()
+        // 设置日期格式
+        dateFormatter.dateFormat = "MMMM d, yyyy 'At' h:mm a"
+        // 设置本地化信息,确保月份名称和 AM/PM 显示正确
+        dateFormatter.locale = Locale(identifier: "en_US")
+        // 将 Date 对象转换为字符串
+        let dateString = dateFormatter.string(from: self)
+        
+        return dateString
+    }
+    
+    
+    var dateDayString:String {
+        let dateFormatter = DateFormatter()
+        dateFormatter.dateFormat = "yyyyMMdd"
+        let dateString = dateFormatter.string(from: self)
+        return dateString
+    }
+}

+ 61 - 0
AIPlayRingtones/Classes/Ex/Dictionary+Ex.swift

@@ -0,0 +1,61 @@
+//
+//  Dictionary+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+import Foundation
+
+public extension Dictionary where Key == String {
+    
+    func safeString(forKey key: String) -> String {
+        return safeObject(forKey: key, defaultValue: "")
+    }
+    
+    func safeStringInt(forKey key: String) -> String {
+        return safeObject(forKey: key, defaultValue: "0")
+    }
+    
+    func safeArray(forKey key: String) -> [Any] {
+        return safeObject(forKey: key, defaultValue: [])
+    }
+    
+    func safeDictionary(forKey key: String) -> [String: Any] {
+        return safeObject(forKey: key, defaultValue: [:])
+    }
+    
+    func safeNumber(forKey key: String) -> NSNumber {
+        return safeObject(forKey: key, defaultValue: NSNumber(value: 0))
+    }
+    
+    func safeInt(forKey key: String) -> Int {
+        guard let value = self[key] , let valueInt = value as? Int else { return 0 }
+        return valueInt
+    }
+    
+    func safeObject(forKey key: String) -> Any {
+        return safeObject(forKey: key, defaultValue: NSObject())
+    }
+    
+    private func safeObject<T>(forKey key: String, defaultValue: T) -> T {
+        guard let value = self[key]  , let valueT = value as? T else { return defaultValue }
+        return valueT
+    }
+}
+
+public extension Dictionary where Key == String {
+    /// 将字典转换为 JSON 字符串
+    func toJSONString() -> String? {
+        do {
+            // 将字典转换为 JSON 数据
+            let jsonData = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
+            
+            // 将 JSON 数据转换为字符串
+            return String(data: jsonData, encoding: .utf8)
+        } catch {
+            print("Error converting dictionary to JSON: \(error)")
+            return nil
+        }
+    }
+}

+ 98 - 0
AIPlayRingtones/Classes/Ex/Int+Ex.swift

@@ -0,0 +1,98 @@
+//
+//  Int+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+
+public extension Int {
+    /// 将秒数转换为 "HH:mm" 格式的字符串
+    /// - Returns: 转换后的字符串
+    public func toTimeString() -> String {
+        // 计算小时和分钟
+        let hours = self / 3600
+        let minutes = (self % 3600) / 60
+        
+        // 格式化为 "HH:mm"
+        return String(format: "%02d:%02d", hours, minutes)
+    }
+    
+    /// 将秒数转换为 "HH:mm:ss" 格式的字符串
+    /// - Returns: 转换后的字符串
+    public func toHourMinuteSecondString() -> String {
+        // 计算小时、分钟和秒
+        let hours = self / 3600
+        let minutes = (self % 3600) / 60
+        let seconds = self % 60
+        
+        // 格式化为 "HH:mm:ss"
+        return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
+    }
+    
+    public static func timestampInt() -> Int {
+         let currentTimestamp = Int(Date().timeIntervalSince1970)
+         return currentTimestamp
+     }
+}
+
+public extension Int {
+    private static var uniqueCounter: Int64 = 0
+    
+    static var uuid: Int {
+        return Int(OSAtomicIncrement64(&uniqueCounter))
+    }
+}
+
+public extension TimeInterval {
+    public var mmss: String {
+        let minute = Int(self) / 60
+        let second = Int(self) % 60
+        return String(format: "%02d", minute) + ":" + String(format: "%02d", second)
+    }
+
+    public var mmsshh: String {
+        let hour = Int(self) / 3600
+        let minute = (Int(self) % 3600) / 60
+        let second = Int(self) % 60
+
+        if hour > 0 {
+            return String(format: "%02d:%02d:%02d", hour, minute, second)
+        } else {
+            return mmss
+        }
+    }
+
+    public var hhmmss: String {
+        let hour = Int(self) / 3600
+        let minute = (Int(self) % 3600) / 60
+        let second = Int(self) % 60
+        return String(format: "%02d:%02d:%02d", hour, minute, second)
+    }
+}
+public extension Int {
+    public var mmss: String {
+        let minute = Int(self) / 60
+        let second = Int(self) % 60
+        return String(format: "%02d", minute) + ":" + String(format: "%02d", second)
+    }
+
+    public var mmsshh: String {
+        let hour = Int(self) / 3600
+        let minute = (Int(self) % 3600) / 60
+        let second = Int(self) % 60
+
+        if hour > 0 {
+            return String(format: "%02d:%02d:%02d", hour, minute, second)
+        } else {
+            return mmss
+        }
+    }
+
+    public var hhmmss: String {
+        let hour = Int(self) / 3600
+        let minute = (Int(self) % 3600) / 60
+        let second = Int(self) % 60
+        return String(format: "%02d:%02d:%02d", hour, minute, second)
+    }
+}

+ 370 - 0
AIPlayRingtones/Classes/Ex/NSString+Ex.swift

@@ -0,0 +1,370 @@
+//
+//  NSString+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+import CommonCrypto
+public extension String {
+    
+    var uiColor: UIColor {
+        if isEmpty {
+            return .clear
+        }
+        return UIColor.fromHex(self)
+    }
+    
+    var uiCGColor: CGColor {
+        uiColor.cgColor
+    }
+    
+    var localFilePath: String {
+        var path = self
+        if path.lowercased().hasPrefix("file://") {
+            path.removeFirst("file://".count)
+        }
+        return path
+    }
+    
+    func toCgRect() -> CGRect {
+        NSCoder.cgRect(for: self)
+    }
+    
+    func toCgPoint() -> CGPoint {
+        NSCoder.cgPoint(for: self)
+    }
+
+
+    func toRgba() -> (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat)? {
+        var r:CGFloat = 0;
+        var g:CGFloat = 0;
+        var b:CGFloat = 0;
+        var a:CGFloat = 0;
+        var prefix: Int = 0;
+
+        let color: String = trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
+        if (color.hasPrefix("#")) {
+            prefix = 1
+        }
+        else if (color.hasPrefix("0X")) {
+            prefix = 2
+        }
+        let length: Int = color.count - prefix
+        //         RGB            RGBA          RRGGBB        RRGGBBAA
+        
+        func componentFrom(string: String, start: Int, length: Int) -> CGFloat {
+            guard start >= 0, start + length <= string.count else {
+                return 0
+            }
+            
+            let begin = string.index(startIndex, offsetBy: start)
+            let end = string.index(startIndex, offsetBy: start + length)
+            
+            let sub = string[begin..<end]
+            var full = String(sub)
+            if length == 1 {
+                full += full
+            }
+            
+            var hex: UInt64 = 0
+            Scanner(string: full).scanHexInt64(&hex)
+            
+            let float = CGFloat(hex) / 255.0
+            return float
+        }
+        
+        switch length {
+        case 3: // RGB
+            r = componentFrom(string: color, start: prefix, length: 1)
+            g = componentFrom(string: color, start: prefix + 1, length: 1)
+            b = componentFrom(string: color, start: prefix + 2, length: 1)
+            a = 1
+        case 4: // RGBA
+            r = componentFrom(string: color, start: prefix, length: 1)
+            g = componentFrom(string: color, start: prefix + 1, length: 1)
+            b = componentFrom(string: color, start: prefix + 2, length: 1)
+            a = componentFrom(string: color, start: prefix + 3, length: 1)
+        case 6: // RRGGBB
+            r = componentFrom(string: color, start: prefix, length: 2)
+            g = componentFrom(string: color, start: prefix + 2, length: 2)
+            b = componentFrom(string: color, start: prefix + 4, length: 2)
+            a = 1
+        case 8: // RRGGBBAA
+            r = componentFrom(string: color, start: prefix, length: 2)
+            g = componentFrom(string: color, start: prefix + 2, length: 2)
+            b = componentFrom(string: color, start: prefix + 4, length: 2)
+            a = componentFrom(string: color, start: prefix + 6, length: 2)
+        default:
+            return nil
+        }
+        
+        return (r, g, b, a)
+
+    }
+    
+    func toColor() -> UIColor? {
+        if let rgba = toRgba() {
+            return UIColor(red: rgba.r, green: rgba.g, blue: rgba.b, alpha: rgba.a)
+        }
+        
+        return nil
+    }
+}
+
+public extension String {
+
+    var localized:String {
+        return NSLocalizedString(self, comment: self)
+//        return self
+    }
+        
+}
+
+
+public extension String {
+    var doubleValue: Double? {
+        Double(self)
+    }
+    
+    var md5:String {
+        let data = Data(self.utf8)
+        var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
+        data.withUnsafeBytes {
+            _ = CC_MD5($0.baseAddress, CC_LONG(data.count), &digest)
+        }
+        return digest.map { String(format: "%02x", $0) }.joined()
+    }
+}
+
+
+public extension String {
+    // 移除逗号
+    func deleteDotSymbol() -> String {
+        // 超过1000的数字,格式化成文本后会带 ','(en)/'٬'(ar)
+        return replacingOccurrences(of: ",", with: "").replacingOccurrences(of: "٬", with: "")
+    }
+    
+}
+
+
+
+
+public extension String {
+    /// 根据字符串动态创建类实例
+    /// - Parameters:
+    ///   - type: 要创建的类类型(泛型)
+    ///   - bundle: 模块的命名空间(默认为 `nil`,即当前模块)
+    /// - Returns: 创建的实例或 `nil`
+    func toInstance<T:NSObject>(of type: T.Type, bundle: String? = nil) -> T? {
+        // 获取完整的类名
+        let namespace = bundle ?? Bundle.main.infoDictionary?["CFBundleName"] as? String ?? ""
+        let className = "\(namespace).\(self)"
+        
+        // 通过类名获取类型
+        guard let targetClass = NSClassFromString(className) as? T.Type else {
+            return nil
+        }
+        
+        // 创建实例
+        return targetClass.init()
+        
+    }
+    
+    
+    func toClass(bundle: String? = nil) -> AnyClass? {
+        // 获取完整的类名
+        let namespace = bundle ?? Bundle.main.infoDictionary?["CFBundleName"] as? String ?? ""
+        let className = "\(namespace).\(self)"
+        
+        // 通过类名获取类型
+        guard let targetClass = NSClassFromString(className) else {
+            return nil
+        }
+        
+        // 创建实例
+        return targetClass
+    }
+}
+
+
+
+
+public extension String {
+    
+
+    func boundingRect(ofAttributes attributes: [NSAttributedString.Key: Any], size: CGSize) -> CGRect {
+        let boundingBox = boundingRect(
+            with: size,
+            options: [.usesLineFragmentOrigin, .usesFontLeading],
+            attributes: attributes,
+            context: nil
+        )
+        return boundingBox
+    }
+    
+    func size(ofAttributes attributes: [NSAttributedString.Key: Any], maxWidth: CGFloat, maxHeight: CGFloat) -> CGSize {
+        boundingRect(ofAttributes: attributes, size: .init(width: maxWidth, height: maxHeight)).size
+    }
+    
+    func size(ofFont font: UIFont, maxWidth: CGFloat, maxHeight: CGFloat) -> CGSize {
+        let constraintRect = CGSize(width: maxWidth, height: maxHeight)
+        let boundingBox = boundingRect(
+            with: constraintRect,
+            options: [.usesLineFragmentOrigin, .usesFontLeading],
+            attributes: [.font: font],
+            context: nil
+        )
+        return boundingBox.size
+    }
+    
+    func width(ofSize size: CGFloat, maxHeight: CGFloat) -> CGFloat {
+        width(
+            ofFont: UIFont.systemFont(ofSize: size),
+            maxHeight: maxHeight
+        )
+    }
+    
+    func width(ofFont font: UIFont, maxHeight: CGFloat) -> CGFloat {
+        size(
+            ofAttributes: [NSAttributedString.Key.font: font],
+            maxWidth: CGFloat(MAXFLOAT),
+            maxHeight: maxHeight
+        ).width
+    }
+    
+    func height(ofSize size: CGFloat, maxWidth: CGFloat) -> CGFloat {
+        height(
+            ofFont: UIFont.systemFont(ofSize: size),
+            maxWidth: maxWidth
+        )
+    }
+    
+    func height(ofFont font: UIFont, maxWidth: CGFloat) -> CGFloat {
+        size(
+            ofAttributes: [NSAttributedString.Key.font: font],
+            maxWidth: maxWidth,
+            maxHeight: CGFloat(MAXFLOAT)
+        ).height
+    }
+    
+    subscript(_ indexs: ClosedRange<Int>) -> String {
+            let beginIndex = index(startIndex, offsetBy: indexs.lowerBound)
+            let endIndex = index(startIndex, offsetBy: indexs.upperBound)
+            return String(self[beginIndex...endIndex])
+        }
+        
+    subscript(_ indexs: Range<Int>) -> String {
+        let beginIndex = index(startIndex, offsetBy: indexs.lowerBound)
+        let endIndex = index(startIndex, offsetBy: indexs.upperBound)
+        return String(self[beginIndex..<endIndex])
+    }
+    
+    subscript(_ indexs: PartialRangeThrough<Int>) -> String {
+        let endIndex = index(startIndex, offsetBy: indexs.upperBound)
+        return String(self[startIndex...endIndex])
+    }
+    
+    subscript(_ indexs: PartialRangeFrom<Int>) -> String {
+        let beginIndex = index(startIndex, offsetBy: indexs.lowerBound)
+        return String(self[beginIndex..<endIndex])
+    }
+    
+    subscript(_ indexs: PartialRangeUpTo<Int>) -> String {
+        let endIndex = index(startIndex, offsetBy: indexs.upperBound)
+        return String(self[startIndex..<endIndex])
+    }
+    
+    var assetFormat: String? {
+        let lowercased = lowercased()
+        if lowercased.hasSuffix("heic") {
+            return "heic"
+        }
+        if lowercased.hasSuffix("jpg") || lowercased.hasSuffix("jpeg") {
+            return "jpeg"
+        }
+        if lowercased.hasSuffix("png") {
+            return "png"
+        }
+        if lowercased.hasSuffix("gif") {
+            return "gif"
+        }
+        return nil
+    }
+}
+
+public extension String {
+    
+    func colors(separator:String) -> [UIColor] {
+        let array = self.components(separatedBy: separator)
+        var colors = [UIColor]()
+        for string in array {
+            colors.append(string.uiColor)
+        }
+        return colors
+    }
+    
+    func cgColors(separator:String) -> [CGColor] {
+        let array = self.components(separatedBy: separator)
+        var colors = [CGColor]()
+        for string in array {
+            colors.append(string.uiColor.cgColor)
+        }
+        return colors
+    }
+}
+
+
+
+
+public extension String {
+    
+    func jsonDict() -> [String:Any]? {
+        // 将字符串转换为 Data
+        if let jsonData = self.data(using: .utf8) {
+            do {
+                // 将 Data 解析为 JSON 对象
+                let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: [])
+                // 尝试将 JSON 对象转换为字典
+                if let jsonDict = jsonObject as? [String: Any] {
+//                    dePrint("转换后的字典: \(jsonDict)")
+                    return jsonDict
+                    
+                } else {
+//                    dePrint("无法将 JSON 对象转换为字典。")
+                }
+            } catch {
+//                dePrint("JSON 解析错误: \(error)")
+            }
+        } else {
+//            dePrint("无法将字符串转换为 Data。")
+        }
+        
+        return nil
+    }
+    
+   
+    
+    
+}
+
+public extension String {
+    func removeTimestampFromEnd() -> String {
+        let string = self
+//        // 正则表达式:匹配末尾的10位或13位数字
+//        let pattern = "\\d{10}$|\\d{13}$"
+//        guard let regex = try? NSRegularExpression(pattern: pattern) else {
+//            return string
+//        }
+//        let range = NSRange(location: 0, length: string.utf16.count)
+//        return regex.stringByReplacingMatches(in: string, options: [], range: range, withTemplate: "")
+        
+        // 正则表达式:匹配(可选下划线)后跟10位或13位数字结尾
+        let pattern = "_?\\d{10}$|_?\\d{13}$"
+        guard let regex = try? NSRegularExpression(pattern: pattern) else {
+            return string
+        }
+        let range = NSRange(location: 0, length: string.utf16.count)
+        return regex.stringByReplacingMatches(in: string, options: [], range: range, withTemplate: "")
+        
+    }
+}

+ 47 - 0
AIPlayRingtones/Classes/Ex/SwiftUI/Color+Ex.swift

@@ -0,0 +1,47 @@
+//
+//  Color+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2025/1/14.
+//
+
+import SwiftUI
+
+public extension Color {
+    public static func hex(_ hex: String) -> Color {
+        let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
+        var int: UInt64 = 0
+        Scanner(string: hex).scanHexInt64(&int)
+
+        let a, r, g, b: UInt64
+        switch hex.count {
+        case 3: // RGB (12-bit)
+            (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
+        case 6: // RGB (24-bit)
+            (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
+        case 8: // ARGB (32-bit)
+            (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
+        default:
+            (a, r, g, b) = (255, 0, 0, 0)
+        }
+
+        return Self(
+            .sRGB,
+            red: Double(r) / 255,
+            green: Double(g) / 255,
+            blue: Double(b) / 255,
+            opacity: Double(a) / 255
+        )
+    }
+
+    // 随机生成一个颜色
+    public static var randomColor: Color {
+        // 随机生成 R, G, B 和 alpha(透明度)值
+        let red = Double.random(in: 0 ... 1)
+        let green = Double.random(in: 0 ... 1)
+        let blue = Double.random(in: 0 ... 1)
+        let alpha = Double.random(in: 0.5 ... 1) // 可选,透明度的范围
+
+        return Color(red: red, green: green, blue: blue, opacity: alpha)
+    }
+}

+ 16 - 0
AIPlayRingtones/Classes/Ex/SwiftUI/Font+Ex.swift

@@ -0,0 +1,16 @@
+//
+//  Font+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2025/1/14.
+//
+
+
+import SwiftUI
+public extension Font {
+    static func font(name: FontName = .PingFangSC, size: CGFloat, weight: UIFont.Weight = .regular) -> Font {
+        let uiFont =  UIFont.font(name: name,size: size,weight: weight)
+        return Font(uiFont as CTFont)
+    }
+}
+

+ 34 - 0
AIPlayRingtones/Classes/Ex/SwiftUI/Text+Ex.swift

@@ -0,0 +1,34 @@
+//
+//  Text+Ex.swift
+//  Pods
+//
+//  Created by 100Years on 2025/3/13.
+//
+
+import SwiftUI
+public extension Text {
+    func gradientForeground(colors: [Color], startPoint: UnitPoint, endPoint: UnitPoint) -> some View {
+        self
+            .overlay(
+                LinearGradient(
+                    gradient: Gradient(colors: colors),
+                    startPoint: startPoint,
+                    endPoint: endPoint
+                )
+                .mask(self)
+            )
+    }
+}
+public extension View {
+    func gradientForeground(colors: [Color], startPoint: UnitPoint, endPoint: UnitPoint) -> some View {
+        self
+            .overlay(
+                LinearGradient(
+                    gradient: Gradient(colors: colors),
+                    startPoint: startPoint,
+                    endPoint: endPoint
+                )
+                .mask(self)
+            )
+    }
+}

+ 49 - 0
AIPlayRingtones/Classes/Ex/UIApplication+Ex.swift

@@ -0,0 +1,49 @@
+//
+//  UIApplication+Ex.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/1/22.
+//
+
+import UIKit
+import AVFoundation
+public extension UIApplication {
+    // 获取系统音量
+    public static func getSystemVolume() -> Float {
+        let audioSession = AVAudioSession.sharedInstance()
+        do {
+            try audioSession.setCategory(.playback) // 设置音频会话为播放模式
+            return audioSession.outputVolume
+        } catch {
+            print("Failed to set audio session category.")
+            return -1.0
+        }
+    }
+    
+
+    public enum TSVersionCompareResult {
+        case newer
+        case older
+        case same
+    }
+
+    public static func compareAppVersions(newVersion1: String, oldVersion: String) -> TSVersionCompareResult {
+        let components1 = newVersion1.components(separatedBy: ".")
+        let components2 = oldVersion.components(separatedBy: ".")
+        
+        let maxLength = max(components1.count, components2.count)
+        
+        for i in 0..<maxLength {
+            let num1 = i < components1.count ? Int(components1[i]) ?? 0 : 0
+            let num2 = i < components2.count ? Int(components2[i]) ?? 0 : 0
+            
+            if num1 > num2 {
+                return .newer
+            } else if num1 < num2 {
+                return .older
+            }
+        }
+        
+        return .same
+    }
+}

+ 164 - 0
AIPlayRingtones/Classes/Ex/UIButton+Ex.swift

@@ -0,0 +1,164 @@
+//
+//  UIButton+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+public extension UIButton {
+    
+    private struct AssociatedKeys {
+        static var actionKey = "UIButtonActionKey"
+    }
+
+    // 存储回调闭包
+    private var actionClosure: (() -> Void)? {
+        get {
+            return objc_getAssociatedObject(self, &AssociatedKeys.actionKey) as? (() -> Void)
+        }
+        set {
+            objc_setAssociatedObject(self, &AssociatedKeys.actionKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    
+    
+    /// 快速创建 UIButton
+    /// - Parameters:
+    ///   - title: 按钮的标题
+    ///   - image: 按钮的图标
+    ///   - backgroundImage: 按钮的背景图
+    ///   - backgroundColor: 按钮的背景色
+    ///   - font: 按钮的字体
+    ///   - titleColor: 按钮的字体颜色
+    ///   - action: 按钮点击事件的回调
+    static func createButton(title: String? = nil,
+                             image: UIImage? = nil,
+                             backgroundImage: UIImage? = nil,
+                             backgroundColor: UIColor? = nil,
+                             font: UIFont? = nil,
+                             titleColor: UIColor? = nil,
+                             corner:CGFloat = 0,
+                             action: (() -> Void)? = nil) -> UIButton {
+        let button = UIButton(type: .custom)
+        button.setUpButton(title: title,
+                           image: image,
+                           backgroundImage: backgroundImage,
+                           backgroundColor: backgroundColor,
+                           font: font,
+                           titleColor: titleColor,
+                           corner:corner,
+                           action: action)
+        return button
+    }
+
+    func setUpButton(title: String? = nil,
+                             image: UIImage? = nil,
+                             backgroundImage: UIImage? = nil,
+                             backgroundColor: UIColor? = nil,
+                             font: UIFont? = nil,
+                             titleColor: UIColor? = nil,
+                             corner:CGFloat = 0,
+                             action: (() -> Void)? = nil){
+        let button = self
+        button.showsTouchWhenHighlighted = false
+        button.adjustsImageWhenHighlighted = false
+        // 设置标题
+        if let title = title {
+            button.setTitle(title, for: .normal)
+        }
+        
+        // 设置图片
+        if let image = image {
+            button.setImage(image, for: .normal)
+        }
+        
+        // 设置背景图片
+        if let backgroundImage = backgroundImage {
+            button.setBackgroundImage(backgroundImage, for: .normal)
+        }
+        
+        // 设置背景色
+        if let backgroundColor = backgroundColor {
+            button.backgroundColor = backgroundColor
+        }
+        
+        // 设置字体
+        if let font = font {
+            button.titleLabel?.font = font
+        }
+        
+        // 设置字体颜色
+        if let titleColor = titleColor {
+            button.setTitleColor(titleColor, for: .normal)
+        }
+        
+        if corner > 0 {
+            button.cornerRadius = corner
+        }
+
+        if let action = action {
+            button.actionClosure = action
+            button.addTarget(button, action: #selector(buttonTapped), for: .touchUpInside)
+        }
+
+    }
+    
+    // 按钮点击事件
+    @objc private func buttonTapped() {
+        actionClosure?()
+    }
+    
+    
+
+    public func preventMultipleTaps(delay: Double = 0.75) {
+        self.isEnabled = false
+        DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
+            self.isEnabled = true
+        }
+    }
+  
+
+
+}
+
+public extension UIButton {
+    
+    public func setTitleImageSpace(spacing:CGFloat = 4){
+//        titleEdgeInsets = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: 0) // 只调整 title 的 left
+        contentEdgeInsets = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: 0) // 只调整 title 的 left
+        imageEdgeInsets = UIEdgeInsets(top: 0, left: -spacing, bottom: 0, right: 0) // 只调整 image 的 right
+    }
+    
+}
+public class TSUIExpandedTouchButton: UIButton {
+    public var indexPath: IndexPath = IndexPath(item: 0, section: 0)
+    
+    override public func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
+        let buttonBounds = self.bounds
+        let widthDelta = self.width * 0.5 // 增加点击区域的比例,这里增加了50%
+        let heightDelta = self.height * 0.5
+        let expandedBounds = buttonBounds.insetBy(dx: -widthDelta, dy: -heightDelta)
+        return expandedBounds.contains(point)
+    }
+}
+
+
+public class TSVerticalButton: UIButton {
+    var spacing: CGFloat = 4 {
+        didSet { setNeedsLayout() }
+    }
+
+    public override func layoutSubviews() {
+        super.layoutSubviews()
+        guard let imageView = imageView, let titleLabel = titleLabel else { return }
+
+        // 图片居中靠上
+        imageView.frame.origin.y = (bounds.height - imageView.frame.height - titleLabel.frame.height - spacing) / 2
+        imageView.center.x = bounds.width / 2
+
+        // 文字居中靠下
+        titleLabel.frame.origin.y = imageView.frame.maxY + spacing
+        titleLabel.center.x = bounds.width / 2
+    }
+}

+ 89 - 0
AIPlayRingtones/Classes/Ex/UIColor+Ex.swift

@@ -0,0 +1,89 @@
+//
+//  UIColor+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+import UIKit
+import SwiftUI
+public extension UIColor {
+    /// 返回一个随机颜色
+    static var random: UIColor {
+        return UIColor(
+            red: CGFloat.random(in: 0...1),
+            green: CGFloat.random(in: 0...1),
+            blue: CGFloat.random(in: 0...1),
+            alpha: 1.0
+        )
+    }
+    
+    static func fromHex(_ hex: String, alpha: CGFloat = 1.0) -> UIColor {
+        var cleanedHex = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
+        
+        // Remove leading "#" if present
+        if cleanedHex.hasPrefix("#") {
+            cleanedHex.removeFirst()
+        }
+        
+        // Ensure valid hex length
+        guard cleanedHex.count == 6 else {
+            return UIColor.clear // Return clear color for invalid input
+        }
+        
+        // Extract RGB components
+        var rgbValue: UInt64 = 0
+        Scanner(string: cleanedHex).scanHexInt64(&rgbValue)
+        
+        let red = CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0
+        let green = CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0
+        let blue = CGFloat(rgbValue & 0x0000FF) / 255.0
+        
+        return UIColor(red: red, green: green, blue: blue, alpha: alpha)
+    }
+    
+    var color: Color {
+        return Color(self)
+    }
+
+}
+
+public extension UIColor {
+    /// 调整颜色的亮度和饱和度
+    /// - Parameters:
+    ///   - brightnessFactor: 亮度因子(范围 0.0 - 1.0, 0 表示完全黑暗, 1 表示原始亮度)
+    ///   - saturationFactor: 饱和度因子(范围 0.0 - 1.0, 0 表示无饱和度, 1 表示原始饱和度)
+    /// - Returns: 调整后的颜色
+    public func adjusted(brightness brightnessFactor: CGFloat = 1.0, saturation saturationFactor: CGFloat = 1.0) -> UIColor {
+        var hue: CGFloat = 0
+        var saturation: CGFloat = 0
+        var brightness: CGFloat = 0
+        var alpha: CGFloat = 0
+        
+        if self.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
+            // 根据 factor 计算调整后的亮度和饱和度
+            let adjustedBrightness = brightness * brightnessFactor
+            let adjustedSaturation = saturation * saturationFactor
+            return UIColor(hue: hue, saturation: adjustedSaturation, brightness: adjustedBrightness, alpha: alpha)
+        }
+        return self
+    }
+    
+    /// 获取颜色的亮度
+    var brightness: CGFloat? {
+        var brightness: CGFloat = 0
+        if self.getHue(nil, saturation: nil, brightness: &brightness, alpha: nil) {
+            return brightness
+        }
+        return nil
+    }
+    
+    /// 获取颜色的饱和度
+    var saturation: CGFloat? {
+        var saturation: CGFloat = 0
+        if self.getHue(nil, saturation: &saturation, brightness: nil, alpha: nil) {
+            return saturation
+        }
+        return nil
+    }
+}

+ 128 - 0
AIPlayRingtones/Classes/Ex/UIDevice+Extension.swift

@@ -0,0 +1,128 @@
+//
+//  UIDevice+Extension.swift
+//  Example
+//
+//  Created by Slience on 2021/1/13.
+//
+
+import UIKit
+
+public extension UIDevice {
+    public class var isPortrait: Bool {
+        if isPad {
+            return true
+        }
+        if  statusBarOrientation == .landscapeLeft ||
+                statusBarOrientation == .landscapeRight {
+            return false
+        }
+        return true
+    }
+    public class var statusBarOrientation: UIInterfaceOrientation {
+        UIApplication.shared.statusBarOrientation
+    }
+    public class var navigationBarHeight: CGFloat {
+        if isPad {
+            if #available(iOS 12, *) {
+                return statusBarHeight + 50
+            }
+        }
+        return statusBarHeight + 44
+    }
+    class var generalStatusBarHeight: CGFloat {
+        isAllIPhoneX ? 44 : 20
+    }
+    public class var statusBarHeight: CGFloat {
+        let statusBarHeight: CGFloat
+        let window = UIApplication.shared.windows.first
+        if #available(iOS 13.0, *),
+           let height = window?.windowScene?.statusBarManager?.statusBarFrame.size.height {
+            statusBarHeight = height
+        } else {
+            statusBarHeight = UIApplication.shared.statusBarFrame.size.height
+        }
+        return statusBarHeight
+    }
+    public class var topMargin: CGFloat {
+        if isAllIPhoneX {
+            return statusBarHeight
+        }
+        return safeAreaInsets.top
+    }
+    public class var leftMargin: CGFloat {
+        safeAreaInsets.left
+    }
+    public class var rightMargin: CGFloat {
+        safeAreaInsets.right
+    }
+    public class var bottomMargin: CGFloat {
+        safeAreaInsets.bottom
+    }
+    public class var isPad: Bool {
+        current.userInterfaceIdiom == .pad
+    }
+    public class var isAllIPhoneX: Bool {
+        let safeArea = safeAreaInsets
+        let margin: CGFloat
+        if isPortrait {
+            margin = safeArea.bottom
+        }else {
+            margin = safeArea.left
+        }
+        return margin != 0
+    }
+    
+    public class var safeAreaInsets: UIEdgeInsets {
+        if #available(iOS 11.0, *) {
+            return UIApplication._keyWindow?.safeAreaInsets ?? .zero
+        }
+        return .zero
+    }
+    
+    public class var screenSize: CGSize {
+        if #available(iOS 14.0, *), ProcessInfo.processInfo.isiOSAppOnMac {
+            return UIApplication._keyWindow?.frame.size ?? UIScreen.main.bounds.size
+        }
+        return UIScreen.main.bounds.size
+    }
+}
+
+public extension UIApplication {
+    public class var _keyWindow: UIWindow? {
+        var window: UIWindow?
+        if #available(iOS 13.0, *) {
+            window = shared.windows.filter({ $0.isKeyWindow }).last
+        } else {
+            window = shared.delegate?.window ?? shared.keyWindow
+        }
+        return window
+    }
+}
+
+
+public extension UIDevice {
+    
+    
+    class func copyImage(image:UIImage) {
+        UIPasteboard.general.image = image
+    }
+    
+}
+
+public extension UIDevice {
+    /// 开启屏幕常亮
+    static func keepScreenAwake() {
+        UIApplication.shared.isIdleTimerDisabled = true
+    }
+    
+    /// 恢复默认屏幕休眠行为
+    static func allowScreenSleep() {
+        UIApplication.shared.isIdleTimerDisabled = false
+    }
+    
+    /// 0最小,1 最亮
+    static func brightness(brightness:CGFloat) {
+        UIScreen.main.brightness = brightness
+    }
+    
+}

+ 66 - 0
AIPlayRingtones/Classes/Ex/UIFont+Ex.swift

@@ -0,0 +1,66 @@
+//
+//  UIFont+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+public typealias FontName = String
+
+public extension FontName {
+    static public let PingFangSC   = "PingFangSC"
+}
+
+
+public extension UIFont {
+    class public func font(name: FontName = .PingFangSC, size: CGFloat, weight: UIFont.Weight = .regular) -> UIFont {
+        guard !name.isEmpty,
+                let _ = UIFont(name: name, size: size) else {
+            return UIFont.systemFont(ofSize: size, weight: weight)
+        }
+        var finalFontName = name
+        
+        let fontNames = UIFont.fontNames(forFamilyName: name)
+        switch weight {
+        case .light://细体 300
+            if let aName = fontNames.first(where: { $0.lowercased().hasSuffix("-light") || $0.lowercased().hasSuffix("_light") }) {
+                finalFontName = aName
+            }
+        case .medium://中黑体 500
+            if let aName = fontNames.first(where: { $0.lowercased().hasSuffix("-medium") || $0.lowercased().hasSuffix("_medium") }) {
+                finalFontName = aName
+            }
+        case .bold:// 粗体 700
+            if let aName = fontNames.first(where: { $0.lowercased().hasSuffix("-bold") || $0.lowercased().hasSuffix("_bold") }) {
+                finalFontName = aName
+            }
+        case .semibold://半粗体 600
+            if let aName = fontNames.first(where: { $0.lowercased().hasSuffix("-semibold") || $0.lowercased().hasSuffix("_semibold") }) {
+                finalFontName = aName
+            }
+        case .heavy: //粗黑体 800
+            if let aName = fontNames.first(where: { $0.lowercased().hasSuffix("-heavy") || $0.lowercased().hasSuffix("_heavy") }) {
+                finalFontName = aName
+            }
+        case .black: // 黑体 900
+            if let aName = fontNames.first(where: { $0.lowercased().hasSuffix("-black") || $0.lowercased().hasSuffix("_black") }) {
+                finalFontName = aName
+            }
+        default:
+            break
+        }
+        
+        return UIFont(name: finalFontName, size: size)!
+    }
+}
+extension UIFont {
+    
+    /// 返回该字体下,对应字符的宽度
+    /// - Parameter word: 字符
+    /// - Returns: 宽度
+    public func widthForWord(_ word: String) -> CGFloat {
+        
+        let sizeWidth = (word as NSString).size(withAttributes: [.font: self]).width
+        return sizeWidth//.rounded(.up)
+    }
+}

+ 319 - 0
AIPlayRingtones/Classes/Ex/UIImage+Ex.swift

@@ -0,0 +1,319 @@
+//
+//  UIImage+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/23.
+//
+
+
+public extension UIImage {
+    
+    var width: CGFloat { size.width }
+    var height: CGFloat { size.height }
+    
+    public func normalizedImage() -> UIImage? {
+        if imageOrientation == .up {
+            return self
+        }
+        return repaintImage()
+    }
+    public func repaintImage() -> UIImage? {
+        let format = UIGraphicsImageRendererFormat()
+        format.opaque = false
+        format.scale = scale
+        let renderer = UIGraphicsImageRenderer(size: size, format: format)
+        let image = renderer.image { context in
+            draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
+        }
+        return image
+    }
+
+    public func scaleToFillSize(size: CGSize, mode: UIView.ContentMode = .scaleToFill, scale: CGFloat = 0) -> UIImage? {
+        if __CGSizeEqualToSize(self.size, size) {
+            return self
+        }
+        let rect: CGRect
+        let rendererSize: CGSize
+        if mode == .scaleToFill {
+            let isEqualRatio = size.width / size.height == width / height
+            if isEqualRatio {
+                rendererSize = size
+                rect = CGRect(origin: .zero, size: size)
+            }else {
+                let scale = size.width / width
+                var scaleHeight = scale * height
+                var scaleWidth = size.width
+                if scaleHeight < size.height {
+                    scaleWidth = size.height / scaleHeight * size.width
+                    scaleHeight = size.height
+                }
+                rendererSize = .init(width: scaleWidth, height: scaleHeight)
+                rect = .init(origin: .zero, size: rendererSize)
+            }
+        }else {
+            rendererSize = size
+            if mode == .scaleAspectFit {
+                rect = CGRect(origin: .zero, size: size)
+            }else {
+                var x: CGFloat = 0
+                var y: CGFloat = 0
+                let scale = size.width / width
+                var scaleWidth = size.width
+                var scaleHeight = scale * height
+                if scaleHeight < size.height {
+                    scaleWidth = size.height / scaleHeight * scaleWidth
+                    scaleHeight = size.height
+                }
+                if scaleWidth < size.width {
+                    scaleHeight = size.width / scaleWidth * scaleHeight
+                    scaleWidth = size.width
+                }
+                x = -(scaleWidth - size.width) / 2
+                y = -(scaleHeight - size.height) / 2
+                rect = CGRect(
+                    x: x,
+                    y: y,
+                    width: scaleWidth,
+                    height: scaleHeight
+                )
+            }
+        }
+        let format = UIGraphicsImageRendererFormat()
+        format.opaque = false
+        format.scale = scale == 0 ? self.scale : scale
+        let renderer = UIGraphicsImageRenderer(size: rendererSize, format: format)
+        let image = renderer.image { context in
+            draw(in: rect)
+        }
+        return image
+    }
+    
+    
+    public var pngImage:UIImage?{
+        // 将 UIImage 转为 PNG Data
+        guard let pngData = self.pngData() else {
+            print("Failed to convert WebP image to PNG data.")
+            return nil
+        }
+        
+        // 使用 PNG Data 创建新的 UIImage
+        return UIImage(data: pngData)
+    }
+}
+
+
+public extension UIImage {
+    /// 将颜色转换为 UIImage
+    /// - Parameters:
+    ///   - color: 目标颜色
+    ///   - size: 图片尺寸(默认 1x1)
+    /// - Returns: 转换后的 UIImage
+    public static func colorFrom(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) -> UIImage? {
+        // 创建一个矩形
+        let rect = CGRect(origin: .zero, size: size)
+        
+        // 开始图形上下文
+        UIGraphicsBeginImageContextWithOptions(size, false, 0)
+        
+        // 设置颜色填充
+        color.setFill()
+        UIRectFill(rect)
+        
+        // 获取生成的图片
+        let image = UIGraphicsGetImageFromCurrentImageContext()
+        
+        // 结束图形上下文
+        UIGraphicsEndImageContext()
+        
+        return image
+    }
+    
+    public func resizableImage(capInsets:UIEdgeInsets) -> UIImage{
+        return resizableImage(withCapInsets: capInsets, resizingMode: .stretch)
+    }
+    
+    
+  
+        // 创建纯色图片
+    convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
+        let rect = CGRect(origin: .zero, size: size)
+        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
+        color.setFill()
+        UIRectFill(rect)
+        let image = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+        
+        guard let cgImage = image?.cgImage else { return nil }
+        self.init(cgImage: cgImage)
+    }
+        
+        // 创建圆形图片
+    static func circle(diameter: CGFloat, color: UIColor) -> UIImage {
+        let size = CGSize(width: diameter, height: diameter)
+        let renderer = UIGraphicsImageRenderer(size: size)
+        return renderer.image { context in
+            let rect = CGRect(origin: .zero, size: size)
+            color.setFill()
+            UIBezierPath(ovalIn: rect).fill()
+        }
+    }
+
+}
+
+public extension UIImage {
+    var mirror: UIImage {
+//        guard TSConfig.appLanguage.hasPrefix("ar"), let cgImage = self.cgImage else {
+        guard let cgImage = self.cgImage else {
+            return self
+        }
+        let image = UIImage(cgImage: cgImage, scale: self.scale, orientation: Orientation.upMirrored)
+        return image.withRenderingMode(self.renderingMode)
+    }
+    
+    func resize(to size: CGSize) -> UIImage? {
+        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
+        self.draw(in: CGRect(origin: .zero, size: size))
+        let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+        return resizedImage
+    }
+    
+    func cropped(to aspectRatio: CGFloat) -> UIImage? {
+        let originalSize = self.size
+        let targetSize: CGSize
+
+        if originalSize.width > originalSize.height {
+            // 宽度大于高度,裁剪宽度
+            targetSize = CGSize(width: originalSize.height, height: originalSize.height)
+        } else {
+            // 高度大于宽度,裁剪高度
+            targetSize = CGSize(width: originalSize.width, height: originalSize.width)
+        }
+
+        let cropRect = CGRect(
+            x: (originalSize.width - targetSize.width) / 2,
+            y: (originalSize.height - targetSize.height) / 2,
+            width: targetSize.width,
+            height: targetSize.height
+        )
+
+        if let cgImage = self.cgImage?.cropping(to: cropRect) {
+            return UIImage(cgImage: cgImage, scale: self.scale, orientation: self.imageOrientation)
+        }
+        return nil
+    }
+    
+    /// 判断图片是否大于指定大小(默认10MB) 
+   func isLargerThan(byteSize: Int) -> Bool {
+
+//       // 优先尝试JPEG压缩(更接近实际文件大小)
+//       if let jpegData = self.jpegData(compressionQuality: 1.0) {
+//           print("JPEG格式大小: \(Double(jpegData.count) / (1024 * 1024)) MB")
+//           return jpegData.count > byteSize
+//       }
+       
+       // 透明图片回退到PNG
+       if let pngData = self.pngData() {
+           print("PNG格式大小: \(Double(pngData.count) / (1024 * 1024)) MB")
+           return pngData.count > byteSize
+       }
+       
+       return false // 无法获取数据时默认不限制
+   }
+}
+
+//旋转图片
+public extension UIImage {
+
+    /// 按角度旋转图片
+    /// - Parameter degrees: 旋转正数为顺时针,负数为逆时针)
+    /// - Returns: 旋转后的新图片
+    func rotated(byDegrees degrees: CGFloat) -> UIImage? {
+        let radians = degrees * .pi / 180
+        return rotated(byRadians: radians)
+    }
+    
+    /// 按弧度旋转图片
+    /// - Parameter radians: 旋转弧度
+    /// - Returns: 旋转后的新图片
+    func rotated(byRadians radians: CGFloat) -> UIImage? {
+        // 计算旋转后的新边界大小
+        let rotatedSize = CGRect(origin: .zero, size: size)
+            .applying(CGAffineTransform(rotationAngle: radians))
+            .integral.size
+        
+        // 开始图形上下文
+        UIGraphicsBeginImageContextWithOptions(rotatedSize, false, scale)
+        guard let context = UIGraphicsGetCurrentContext() else { return nil }
+        
+        // 移动和旋转坐标系
+        context.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
+        context.rotate(by: radians)
+        
+        // 绘制图像
+        draw(in: CGRect(
+            x: -size.width / 2,
+            y: -size.height / 2,
+            width: size.width,
+            height: size.height
+        ))
+        
+        // 获取旋转后的图像
+        let rotatedImage = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+        
+        return rotatedImage
+    }
+    
+    /// 固定角度旋转(90度、180度、270度)
+    /// - Parameter rotation: 旋转角度
+    /// - Returns: 旋转后的新图片
+    func rotated(by rotation: Rotation) -> UIImage? {
+        switch rotation {
+        case .degrees90:
+            return rotated(byDegrees: 90)
+        case .degrees180:
+            return rotated(byDegrees: 180)
+        case .degrees270:
+            return rotated(byDegrees: 270)
+        }
+    }
+    
+    // MARK: - 辅助类型
+    
+    enum Rotation {
+        case degrees90
+        case degrees180
+        case degrees270
+    }
+    
+    /// 保存图片到文件系统
+    /// - Parameters:
+    ///   - url: 目标URL
+    ///   - compressionQuality: JPEG压缩质量(0.0-1.0)
+    /// - Throws: 可能抛出错误
+    func saveToFile(at url: URL, compressionQuality: CGFloat = 1.0) {
+        guard let data = self.jpegData(compressionQuality: compressionQuality) else {
+            debugPrint("无法转换为JPEG数据")
+            return
+        }
+        
+        do {
+            try data.write(to: url)
+        } catch {
+            debugPrint(error)
+        }
+    }
+}
+
+public extension UIImage {
+    // 压缩图片到指定尺寸
+    func compressImageSize(to size: CGSize) -> UIImage {
+        let targetSize = size
+        if self.size.width > targetSize.width || self.size.height > targetSize.height {
+            return kf.resize(to: targetSize)
+        }
+        return self
+    }
+    
+}

+ 231 - 0
AIPlayRingtones/Classes/Ex/UIImageView+Ex.swift

@@ -0,0 +1,231 @@
+//
+//  UIImageView+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+import Kingfisher
+
+
+class TSCustomActivityIndicator: Indicator {
+    let activityIndicator = UIActivityIndicatorView(style: .medium)
+    
+    init(color: UIColor) {
+        activityIndicator.color = color
+    }
+    
+    func startAnimatingView() {
+        activityIndicator.startAnimating()
+    }
+    
+    func stopAnimatingView() {
+        activityIndicator.stopAnimating()
+    }
+    
+    var view: IndicatorView {
+        return activityIndicator
+    }
+}
+
+public extension UIImageView {
+
+    /// 创建并配置 UIImageView
+    /// - Parameters:
+    ///   - imageName: 图片名称
+    ///   - contentMode: 内容模式,默认为 `.scaleAspectFit`
+    ///   - backgroundColor: 背景颜色,默认为透明
+    /// - Returns: 配置完成的 UIImageView 实例
+    static public func createImageView(
+                                image:UIImage? = nil,
+                                imageName: String? = nil,
+                                contentMode: UIView.ContentMode = .scaleAspectFit,
+                                backgroundColor: UIColor = .clear,
+                                corner: CGFloat = 0.0) -> UIImageView {
+        let imageView = UIImageView()
+                                    
+        if let image = image{
+            imageView.image = image
+        }
+
+        if let imageName = imageName ,imageName.count > 0 {
+            imageView.image = UIImage(named: imageName)
+        }
+        
+        imageView.contentMode = contentMode
+        imageView.backgroundColor = backgroundColor
+        imageView.cornerRadius = corner
+        return imageView
+    }
+    
+    /// 异步创建并加载图片的 UIImageView
+    /// - Parameters:
+    ///   - imageName: 本地占位图片名称
+    ///   - urlString: 图片的 URL 字符串
+    ///   - contentMode: 内容模式,默认为 `.scaleAspectFit`
+    ///   - backgroundColor: 背景颜色,默认为透明
+    ///   - showLoading: 是否显示加载动画,默认为 `true`
+    ///   - completion: 图片加载成功后的回调
+    static public func createAsyncImageView(urlString: String?,
+                                     placeholder:UIImage?,
+                                     contentMode: UIView.ContentMode = .scaleAspectFit,
+                                        adaptiveMode:Bool = false,
+                                     backgroundColor: UIColor = .clear,
+                                     showLoading: Bool = false,
+                                    progressBlock: ((Float)->Void)? = nil,
+                                     completion: ((UIImage?) -> Void)? = nil) -> UIImageView {
+        let imageView = UIImageView()
+        imageView.setAsyncImage(urlString: urlString,
+                                placeholder:placeholder,
+                                contentMode:contentMode,
+                                adaptiveMode:adaptiveMode,
+                                backgroundColor:backgroundColor,
+                                showLoading:showLoading,
+                                progressBlock:progressBlock,
+                                completion:completion)
+        return imageView
+    }
+
+    
+    
+    public func setAsyncImage(urlString: String?,
+                     placeholder: UIImage? = nil,
+                     contentMode: UIView.ContentMode = .scaleAspectFit,
+                 adaptiveMode:Bool = false,
+                 backgroundColor: UIColor = .clear,
+                      showLoading: Bool = false,
+                        progressBlock: ((Float)->Void)? = nil,
+                      completion: ((UIImage?) -> Void)? = nil){
+        let imageView = self
+        imageView.contentMode = contentMode
+        imageView.backgroundColor = backgroundColor
+        imageView.image = placeholder
+        
+        guard let urlString = urlString else {
+            completion?(nil)
+            return
+        }
+      
+        if urlString.count == 0 {
+            completion?(nil)
+            return
+        }
+        
+        if urlString.contains("http") {
+            guard let url = URL(string: urlString) else {
+                completion?(nil)
+                return
+            }
+            kf.indicatorType = showLoading ? .custom(indicator: TSCustomActivityIndicator(color: .white)) : .none
+            imageView.kf.setImage(with: url,
+                 placeholder: placeholder,
+                     options: nil,
+                progressBlock: { receivedSize, totalSize in
+                let progress = receivedSize/totalSize
+                progressBlock?(Float(progress))
+            }){ result in
+                
+                if let image = try? result.get().image {
+                    kDelayMainShort {
+                        completion?(image)
+                    }
+                }else{
+                    completion?(nil)
+                }
+            }
+            
+        }else if urlString.contains("/") {
+            imageView.image = UIImage(contentsOfFile: urlString.fillCachePath)
+            completion?(imageView.image)
+        }else {
+            if let image = UIImage(named: urlString) {
+                imageView.image = image
+                completion?(image)
+            }
+        }
+    }
+
+}
+
+public extension UIImageView {
+    
+    static public func createRightArrow() -> UIImageView {
+        let imageView = UIImageView()
+        imageView.image = UIImage(named: "white_right_arrow")
+        return imageView
+    }
+    
+    /// 根据图片比例自动选择最佳缩放模式
+    public func adaptiveScale() {
+//        guard let image = image else { return }
+//        
+//        let viewSize = bounds.size
+//        let imageRatio = image.size.width / image.size.height
+//        let viewRatio = viewSize.width / viewSize.height
+//        
+//        let contentNewMode:UIView.ContentMode = imageRatio > viewRatio ? .scaleAspectFill : .scaleAspectFit
+//        self.contentMode = contentNewMode
+//        
+//        dePrint("UIImageView.adaptiveScale contentMode =\(contentNewMode)")
+    }
+}
+
+public extension UIImageView {
+    public func setImage(_ image: UIImage?, duration: CFTimeInterval = 0.2, animated: Bool = true) {
+        if let image = image {
+            if animated {
+                UIView.transition(
+                    with: self,
+                    duration: duration,
+                    options: [.transitionCrossDissolve, .curveEaseInOut, .allowUserInteraction]
+                ) {
+                    self.image = image
+                }
+            }else {
+                self.image = image
+            }
+        }
+    }
+}
+
+
+public extension UIImageView {
+
+    static func retrieveImageInMemoryCache(urlString: String) -> UIImage? {
+        return ImageCache.default.retrieveImageInMemoryCache(forKey: urlString)
+    }
+    
+    static func downloadImageWithProgress(
+        urlString: String,
+        progressHandler: ((Float) -> Void)? = nil,
+        completion: @escaping (UIImage?) -> Void
+    ) {
+
+        guard let url = URL(string: urlString) else {
+            completion(nil)
+            return
+        }
+        
+        KingfisherManager.shared.retrieveImage(
+            with: url,
+            options: [],
+            progressBlock: { receivedSize, totalSize in
+                let progress = Float(receivedSize) / Float(totalSize)
+                DispatchQueue.main.async {
+                    progressHandler?(progress) // 回传进度(主线程)
+                }
+            },
+            completionHandler: { result in
+                DispatchQueue.main.async {
+                    switch result {
+                    case .success(let value):
+                        completion(value.image)
+                    case .failure:
+                        completion(nil)
+                    }
+                }
+            }
+        )
+    }
+    
+    
+}

+ 154 - 0
AIPlayRingtones/Classes/Ex/UILabel+Ex.swift

@@ -0,0 +1,154 @@
+//
+//  UILabel+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+public extension UILabel {
+    /// 快速创建 UILabel
+    /// - Parameters:
+    ///   - text: 文本内容
+    ///   - font: 字体
+    ///   - textColor: 文本颜色
+    ///   - textAlignment: 文本对齐方式
+    ///   - numberOfLines: 行数,默认 1
+    ///   - backgroundColor: 背景颜色
+    ///   - adjustsFontSizeToFitWidth: 是否自适应宽度,默认 false
+    ///   - cornerRadius: 圆角,默认 0
+    /// - Returns: 配置好的 UILabel 实例
+    static public func createLabel(text: String? = nil,
+                            font: UIFont? = nil,
+                            textColor: UIColor? = nil,
+                            textAlignment: NSTextAlignment = .left,
+                            numberOfLines: Int = 0,
+                            backgroundColor: UIColor? = nil,
+                            adjustsFontSizeToFitWidth: Bool = false,
+                            cornerRadius: CGFloat = 0) -> UILabel {
+        let label = UILabel()
+        
+        // 设置文本内容
+        label.text = text
+        
+        // 设置字体
+        if let font = font {
+            label.font = font
+        }
+        
+        // 设置文本颜色
+        if let textColor = textColor {
+            label.textColor = textColor
+        }
+        
+        // 设置文本对齐方式
+        label.textAlignment = textAlignment
+        
+        // 设置行数
+        label.numberOfLines = numberOfLines
+        
+        // 设置背景颜色
+        if let backgroundColor = backgroundColor {
+            label.backgroundColor = backgroundColor
+        }
+        
+        // 设置是否自适应宽度
+        label.adjustsFontSizeToFitWidth = adjustsFontSizeToFitWidth
+        
+        // 设置圆角
+        if cornerRadius > 0 {
+            label.layer.cornerRadius = cornerRadius
+            label.layer.masksToBounds = true
+        }
+        
+        return label
+    }
+    
+    /// 设置行间距
+    /// - Parameters:
+    ///   - lineSpacing: 行间距
+    ///   - alignment: 对齐方式(默认为左对齐)
+    func setLineSpacing(_ lineSpacing: CGFloat, alignment: NSTextAlignment = .left) {
+        guard let text = self.text else { return }
+        
+        // 创建 NSMutableAttributedString
+        let attributedString = NSMutableAttributedString(string: text)
+        
+        // 创建段落样式
+        let paragraphStyle = NSMutableParagraphStyle()
+        paragraphStyle.lineSpacing = lineSpacing
+        paragraphStyle.alignment = alignment
+        
+        // 应用段落样式到整个文本
+        attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length))
+        
+        // 设置属性文本
+        self.attributedText = attributedString
+    }
+}
+
+
+
+extension UILabel {
+    var textHeight: CGFloat {
+        if let textHeight = text?.height(ofFont: font, maxWidth: width > 0 ? width : .max) {
+            return textHeight
+        }
+        return 0
+    }
+    var textWidth: CGFloat {
+        if let textWidth = text?.width(ofFont: font, maxHeight: height > 0 ? height : .max) {
+            return textWidth
+        }
+        return 0
+    }
+    var textSize: CGSize {
+        if let textSize = text?.size(ofFont: font, maxWidth: .max, maxHeight: .max) {
+            return textSize
+        }
+        return .zero
+    }
+}
+
+public extension UILabel {
+    
+    //用 label 计算更加精准
+     static func getAttributedTextSize(attributedText:NSAttributedString,maxWidth:CGFloat) -> CGSize{
+        let label = UILabel()
+        label.attributedText = attributedText
+        label.numberOfLines = 0
+        var size = label.sizeThatFits(CGSize(width: maxWidth, height: .greatestFiniteMagnitude))
+        return size
+    }
+}
+
+
+public extension UILabel {
+    func applyGradient(colors: [UIColor], locations: [NSNumber]? = nil, startPoint: CGPoint = CGPoint(x: 0.0, y: 0.5), endPoint: CGPoint = CGPoint(x: 1.0, y: 0.5)) {
+        // 创建一个 CAGradientLayer 实例,用于实现渐变色
+        let gradientLayer = CAGradientLayer()
+        // 设置渐变层的大小与 UILabel 的大小一致
+        gradientLayer.frame = bounds
+        // 将 UIColor 数组转换为 CGColor 数组
+        gradientLayer.colors = colors.map { $0.cgColor }
+        // 设置渐变颜色的位置,如果没有提供,则默认均匀分布
+        gradientLayer.locations = locations
+        // 设置渐变的起始点
+        gradientLayer.startPoint = startPoint
+        // 设置渐变的结束点
+        gradientLayer.endPoint = endPoint
+
+        // 创建一个新的图形上下文
+        UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
+        if let context = UIGraphicsGetCurrentContext() {
+            // 将渐变层渲染到图形上下文中
+            gradientLayer.render(in: context)
+            // 从图形上下文中获取渐变图像
+            if let gradientImage = UIGraphicsGetImageFromCurrentImageContext() {
+                // 结束图形上下文
+                UIGraphicsEndImageContext()
+                // 设置 UILabel 的文本颜色为渐变图像
+                textColor = UIColor(patternImage: gradientImage)
+            }
+        }
+    }
+}

+ 43 - 0
AIPlayRingtones/Classes/Ex/UIScreen.swift

@@ -0,0 +1,43 @@
+//
+//  UIScreen.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/23.
+//
+
+public extension UIScreen {
+    
+    public static var _scale: CGFloat {
+        if #available(iOS 13.0, *), Thread.isMainThread,
+           let scale = UIApplication._keyWindow?.windowScene?.screen.scale {
+            return scale
+        }
+        return main.scale
+    }
+    
+    public static var _width: CGFloat {
+        if #available(iOS 13.0, *), Thread.isMainThread,
+           let width = UIApplication._keyWindow?.windowScene?.screen.bounds.width {
+            return width
+        }
+        return main.bounds.width
+    }
+    
+    public static var _height: CGFloat {
+        if #available(iOS 13.0, *), Thread.isMainThread,
+           let height = UIApplication._keyWindow?.windowScene?.screen.bounds.height {
+            return height
+        }
+        return main.bounds.height
+    }
+    
+    public static var _size: CGSize {
+        if #available(iOS 13.0, *), Thread.isMainThread,
+           let size = UIApplication._keyWindow?.windowScene?.screen.bounds.size {
+            return size
+        }
+        return main.bounds.size
+    }
+    
+}
+

+ 196 - 0
AIPlayRingtones/Classes/Ex/UITableView+Ex.swift

@@ -0,0 +1,196 @@
+//
+//  UITableView+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+import UIKit
+//import MJRefresh
+
+private var isMJRefreshKey = "isMJRefreshKey"
+private var pageNumKey = "pageNumKey"
+private var pageSizeKey = "pageSizeKey"
+private var nullDataImageViewKey = "nullDataImageViewKey"
+private var dataArrayKey = "dataArrayKey"
+
+private let PAGESIZE = 10
+private let PAGENUM = 1
+
+public extension UITableView {
+    // MARK: - Properties
+    
+    var isUseMJRefresh: Bool {
+        get { objc_getAssociatedObject(self, &isMJRefreshKey) as? Bool ?? false }
+        set { objc_setAssociatedObject(self, &isMJRefreshKey, newValue, .OBJC_ASSOCIATION_ASSIGN) }
+    }
+
+    var pageNum: Int {
+        get { objc_getAssociatedObject(self, &pageNumKey) as? Int ?? PAGENUM }
+        set { objc_setAssociatedObject(self, &pageNumKey, newValue, .OBJC_ASSOCIATION_ASSIGN) }
+    }
+
+    var pageSize: Int {
+        get { objc_getAssociatedObject(self, &pageSizeKey) as? Int ?? PAGESIZE }
+        set { objc_setAssociatedObject(self, &pageSizeKey, newValue, .OBJC_ASSOCIATION_ASSIGN) }
+    }
+
+    var nullDataImageView: UIImageView? {
+        get { objc_getAssociatedObject(self, &nullDataImageViewKey) as? UIImageView }
+        set { objc_setAssociatedObject(self, &nullDataImageViewKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
+    }
+
+    var dataArray: [Any] {
+        get { objc_getAssociatedObject(self, &dataArrayKey) as? [Any] ?? [] }
+        set { objc_setAssociatedObject(self, &dataArrayKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
+    }
+
+    // MARK: - Methods
+    
+    public func myReloadData() {
+        reloadData()
+        guard let dataSource = dataSource else { return }
+        var isEmpty = true
+        let sections = (dataSource.responds(to: #selector(UITableViewDataSource.numberOfSections(in:))) ? dataSource.numberOfSections?(in: self) : 1) ?? 1
+
+        for section in 0..<sections {
+            if dataSource.tableView(self, numberOfRowsInSection: section) > 0 {
+                isEmpty = false
+                break
+            }
+        }
+
+        if isEmpty {
+            if nullDataImageView == nil {
+                let nullImage = UIImage(named: "nullDataImageView") ?? UIImage()
+                let w = nullImage.size.width
+                let h = nullImage.size.height
+                nullDataImageView = UIImageView(frame: CGRect(x: (bounds.width - w) / 2, y: (bounds.height - h) / 2, width: w, height: h))
+                nullDataImageView?.image = nullImage
+                if let nullDataImageView = nullDataImageView {
+                    addSubview(nullDataImageView)
+                }
+            }
+        } else {
+            nullDataImageView?.removeFromSuperview()
+            nullDataImageView = nil
+        }
+    }
+
+    public func handleDataArray(_ networkArray: [Any]) {
+        let count = networkArray.count
+
+        if count > 0 {
+            if pageNum <= PAGENUM {
+                dataArray.removeAll()
+            }
+            dataArray.append(contentsOf: networkArray)
+            myReloadData()
+        } else if pageNum <= PAGENUM {
+            dataArray.removeAll()
+            myReloadData()
+        }
+
+        if isUseMJRefresh {
+            handleMJRefresh(count: count)
+        }
+    }
+
+    func addFooter(refreshingBlock: @escaping () -> Void) {
+        isUseMJRefresh = true
+//        mj_footer = MJRefreshAutoNormalFooter(refreshingBlock: { [weak self] in
+//            self?.pageNum += 1
+//            refreshingBlock()
+//        })
+    }
+
+    func addHeader(refreshingBlock: @escaping () -> Void) {
+        isUseMJRefresh = true
+//        mj_header = MJRefreshNormalHeader(refreshingBlock: { [weak self] in
+//            self?.pageNum = PAGENUM
+//            refreshingBlock()
+//        })
+    }
+
+    func handleMJRefresh(count: Int) {
+//        if count >= pageSize {
+//            mj_footer?.resetNoMoreData()
+//        } else {
+//            mj_footer?.endRefreshingWithNoMoreData()
+//        }
+//
+//        if pageNum <= PAGENUM {
+//            mj_header?.endRefreshing()
+//        } else {
+//            mj_footer?.endRefreshing()
+//        }
+    }
+
+    public func handleMJRefreshWithError(_ error: String?) {
+//        if pageNum <= PAGENUM {
+//            mj_header?.endRefreshing()
+//        } else {
+//            mj_footer?.endRefreshing()
+//        }
+    }
+
+    public func initBaseTableView(reuseXib cellIds: [String], isUseMJRefresh: Bool, delegate: UITableViewDelegate & UITableViewDataSource) {
+        setUpTableView()
+        self.isUseMJRefresh = isUseMJRefresh
+        self.delegate = delegate
+        self.dataSource = delegate
+
+        cellIds.forEach { cellId in
+            register(UINib(nibName: cellId, bundle: nil), forCellReuseIdentifier: cellId)
+        }
+    }
+
+    public func initBaseTableView(reuseClass cellIds: [String], isUseMJRefresh: Bool, delegate: UITableViewDelegate & UITableViewDataSource) {
+        setUpTableView()
+        self.isUseMJRefresh = isUseMJRefresh
+        self.delegate = delegate
+        self.dataSource = delegate
+
+        registerCellClass(cellIds: cellIds)
+    }
+    
+    public func registerCellClass(cellIds:[String]) {
+        cellIds.forEach { cellId in
+            if let cellClass = cellId.toClass() {
+                self.register(cellClass, forCellReuseIdentifier: cellId)
+            }
+        }
+    }
+
+    private func setUpTableView() {
+        if #available(iOS 11.0, *) {
+            contentInsetAdjustmentBehavior = .never
+        }
+
+        if #available(iOS 15.0, *) {
+            sectionHeaderTopPadding = 0
+        }
+
+        separatorStyle = .none
+        backgroundColor = .clear
+
+        pageNum = PAGENUM
+        pageSize = PAGESIZE
+        dataArray = []
+
+        estimatedRowHeight = 0
+        estimatedSectionHeaderHeight = 0
+        estimatedSectionFooterHeight = 0
+
+        showsVerticalScrollIndicator = false
+        showsHorizontalScrollIndicator = false
+        
+        
+        backgroundColor = .clear
+
+        if tableFooterView == nil {
+            tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: CGFLOAT_MIN))
+        }
+    }
+}
+

+ 7 - 0
AIPlayRingtones/Classes/Ex/UITextView+Ex.swift

@@ -0,0 +1,7 @@
+//
+//  UITextView+Ex.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/1/16.
+//
+

+ 422 - 0
AIPlayRingtones/Classes/Ex/UIView+Ex.swift

@@ -0,0 +1,422 @@
+//
+//  View+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+
+import UIKit
+
+public extension UIView {
+
+    
+    /// 增加渐变背景,需要先确定当前 view 的 frame
+    /// - Parameters:
+    ///   - colors: 渐变背景颜色数组
+    ///   - startPoint: 渐变启示位置
+    ///   - endPoint: 渐变结束位置
+    ///   - layerSize: 指定 layer 的 size,否则和自己当前一样大
+    public func addGradientBg(colors: [CGColor],
+                       startPoint: CGPoint = .zero,
+                       endPoint: CGPoint = CGPoint(x: 1, y: 1),
+                       layerSize: CGSize? = nil) {
+        let gl = CAGradientLayer()
+        gl.colors = colors
+        gl.startPoint = startPoint
+        gl.endPoint = endPoint
+        gl.frame = CGRect(origin: .zero, size: layerSize ?? self.bounds.size)
+        gl.zPosition =  -1 // 将 gradientLayer 放在底层
+        layer.insertSublayer(gl, at: 0)
+    }
+    
+    /// 慎用
+    /// 给 view 增加圆角阴影,会改变视图结构,慎用,
+    /// 需add 且确定frame后再调用
+    /// > Note: 慎用
+    /// - Parameters:
+    ///   - cornerRadius: 视图圆角
+    ///   - shadowColor: 阴影颜色
+    ///   - shadowOffset: 阴影偏移量
+    ///   - shadowRadius: 阴影扩散距离
+    ///   - shadowOpacity: 阴影透明度
+    @discardableResult
+    public func addCornerRadiusShadow(cornerRadius: CGFloat,
+                               shadowColor: CGColor?,
+                               shadowOffset: CGSize,
+                               shadowRadius: CGFloat,
+                               shadowOpacity: Float) -> UIView? {
+        guard let supView = superview else {
+            debugPrint("必须要先add,且确定了frame")
+            return nil
+        }
+        
+        let frame = self.frame
+        
+        let shadowView = UIView()
+        shadowView.frame = frame
+        shadowView.layer.shadowColor = shadowColor
+        shadowView.layer.shadowOffset = shadowOffset
+        shadowView.layer.shadowOpacity = shadowOpacity
+        shadowView.layer.shadowRadius = shadowRadius
+        
+        supView.insertSubview(shadowView, belowSubview: self)
+        
+        shadowView.addSubview(self)
+        self.frame = CGRect(origin: .zero, size: frame.size)
+        self.clipsToBounds = false
+        self.layer.masksToBounds = true
+        self.layer.cornerRadius = cornerRadius
+        
+        return shadowView
+        
+    }
+    
+    
+
+    /// 给 view 增加阴影
+    /// - Parameters:
+    ///   - cornerRadius: 视图圆角
+    ///   - shadowColor: 阴影颜色
+    ///   - shadowOffset: 阴影偏移量
+    ///   - shadowRadius: 阴影扩散距离
+    ///   - shadowOpacity: 阴影透明度
+    @discardableResult
+    public func addShadow(shadowColor: CGColor?,
+                          shadowOffset: CGSize,
+                          shadowRadius: CGFloat,
+                          shadowOpacity: Float){
+        layer.shadowColor = shadowColor
+        layer.shadowOffset = shadowOffset
+        layer.shadowOpacity = shadowOpacity
+        layer.shadowRadius = shadowRadius
+    }
+}
+
+public extension UIView {
+
+    public enum MaskContentMode {
+            case scaleToFill
+            case scaleAspectFit
+            case scaleAspectFill
+        }
+
+        /// 设置图片蒙版,支持指定 `contentMode` 和缩小比例
+        /// - Parameters:
+        ///   - image: 用作蒙版的图片
+        ///   - contentMode: 蒙版的内容模式
+        ///   - scaleFactor: 缩小比例,默认 1.0 表示不缩放,0.8 表示缩小为 80%
+    public func setImageMask(image: UIImage, contentMode: MaskContentMode = .scaleAspectFit, scaleFactor: CGFloat = 1.0) {
+            guard scaleFactor > 0 && scaleFactor <= 1 else {
+                print("Invalid scaleFactor. It must be in the range (0, 1].")
+                return
+            }
+            
+            // 创建一个 CALayer 来作为蒙版
+            let maskLayer = CALayer()
+            maskLayer.contents = image.cgImage
+            
+            // 计算蒙版的 frame
+            let viewSize = self.bounds.size
+            let imageSize = image.size
+            
+            var maskFrame: CGRect = .zero
+            
+            switch contentMode {
+            case .scaleToFill:
+                // 拉伸填充整个视图
+                maskFrame = CGRect(origin: .zero, size: viewSize)
+                
+            case .scaleAspectFit:
+                // 按比例缩放,适配视图并居中
+                let aspectWidth = viewSize.width / imageSize.width
+                let aspectHeight = viewSize.height / imageSize.height
+                let aspectRatio = min(aspectWidth, aspectHeight) // 按比例适配
+                
+                let scaledWidth = imageSize.width * aspectRatio * scaleFactor
+                let scaledHeight = imageSize.height * aspectRatio * scaleFactor
+                let xOffset = (viewSize.width - scaledWidth) / 2
+                let yOffset = (viewSize.height - scaledHeight) / 2
+                
+                maskFrame = CGRect(x: xOffset, y: yOffset, width: scaledWidth, height: scaledHeight)
+                
+            case .scaleAspectFill:
+                // 按比例缩放,填充视图并裁剪多余部分
+                let aspectWidth = viewSize.width / imageSize.width
+                let aspectHeight = viewSize.height / imageSize.height
+                let aspectRatio = max(aspectWidth, aspectHeight) // 按比例填充
+                
+                let scaledWidth = imageSize.width * aspectRatio * scaleFactor
+                let scaledHeight = imageSize.height * aspectRatio * scaleFactor
+                let xOffset = (viewSize.width - scaledWidth) / 2
+                let yOffset = (viewSize.height - scaledHeight) / 2
+                
+                maskFrame = CGRect(x: xOffset, y: yOffset, width: scaledWidth, height: scaledHeight)
+            }
+            
+            maskLayer.frame = maskFrame
+            
+            // 设置蒙版
+            self.layer.mask = maskLayer
+        }
+}
+
+
+// Frame
+public extension UIView {
+    
+    var viewController: UIViewController? {
+        var next = superview
+        while next != nil {
+            let nextResponder = next?.next
+            if nextResponder is UINavigationController ||
+                nextResponder is UIViewController {
+                return nextResponder as? UIViewController
+            }
+            next = next?.superview
+        }
+        return nil
+    }
+    
+    func convertedToImage(rect: CGRect = .zero) -> UIImage? {
+        var size = bounds.size
+        var origin = bounds.origin
+        if !size.equalTo(rect.size) && !rect.isEmpty {
+            size = rect.size
+            origin = CGPoint(x: -rect.minX, y: -rect.minY)
+        }
+        let format = UIGraphicsImageRendererFormat()
+        format.opaque = false
+        format.scale = UIScreen._scale
+        let renderer = UIGraphicsImageRenderer(size: size, format: format)
+        let image = renderer.image { context in
+            drawHierarchy(in: CGRect(origin: origin, size: bounds.size), afterScreenUpdates: true)
+        }
+        return image
+    }
+
+    func toImage() -> UIImage? {
+        let renderer = UIGraphicsImageRenderer(bounds: self.bounds)
+        let image = renderer.image { context in
+            self.layer.render(in: context.cgContext)
+        }
+        return image
+    }
+    
+    func cornersRound(radius: CGFloat, corner: UIRectCorner) {
+        if #available(iOS 11.0, *) {
+            layer.cornerRadius = radius
+            layer.maskedCorners = corner.mask
+        } else {
+            let path = UIBezierPath(
+                roundedRect: bounds,
+                byRoundingCorners: corner,
+                cornerRadii: CGSize(width: radius, height: radius)
+            )
+            let mask = CAShapeLayer()
+            mask.path = path.cgPath
+            layer.mask = mask
+        }
+    }
+}
+
+extension UIRectCorner {
+    var mask: CACornerMask {
+        switch self {
+        case .allCorners:
+            return [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
+        default:
+            return .init(rawValue: rawValue)
+        }
+    }
+}
+
+public extension UIView {
+    // 部分圆角(必须Frame已知)
+    func makeCorner(_ corners: UIRectCorner, radius: CGFloat) {
+        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
+        let maskLayer = CAShapeLayer()
+        maskLayer.frame = bounds
+        maskLayer.path = path.cgPath
+        layer.mask = maskLayer
+    }
+}
+
+public extension UIView {
+    /// 为视图添加高斯模糊效果
+    /// - Parameters:
+    ///   - blurRadius: 模糊半径(默认 10.0)
+    ///   - fillColor: 填充颜色(可选,默认透明)
+    func applyGaussianBlur(blurRadius: CGFloat = 10.0, fillColor: UIColor? = nil) {
+        // 移除已有的模糊效果
+        self.removeBlurEffect()
+        
+        // 创建模糊效果
+        let blurEffect = UIBlurEffect(style: .light)
+        let blurView = UIVisualEffectView(effect: blurEffect)
+        blurView.frame = self.bounds
+        blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+        
+        // 设置模糊强度
+        if blurRadius != 10.0 {
+            let blurAnimator = CABasicAnimation(keyPath: "inputRadius")
+            blurAnimator.fromValue = blurRadius
+            blurAnimator.toValue = blurRadius
+            blurAnimator.duration = 0
+            blurView.layer.add(blurAnimator, forKey: nil)
+        }
+        
+        // 如果指定了填充颜色,添加一个覆盖视图
+        if let color = fillColor {
+            let colorOverlay = UIView(frame: self.bounds)
+            colorOverlay.backgroundColor = color
+            colorOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+            colorOverlay.isUserInteractionEnabled = false
+            blurView.contentView.addSubview(colorOverlay)
+        }
+        
+        self.addSubview(blurView)
+    }
+    
+    /// 移除模糊效果
+    func removeBlurEffect() {
+        for subview in subviews {
+            if subview is UIVisualEffectView {
+                subview.removeFromSuperview()
+            }
+        }
+    }
+    
+}
+
+public extension UIView {
+    static func creatColor(color:UIColor) -> UIView {
+        let view = UIView()
+        view.backgroundColor = color
+        return view
+    }
+}
+
+
+public extension UIView {
+    func removeAllSubViews() {
+        subviews.forEach { subview in
+            subview.removeFromSuperview()
+        }
+    }
+}
+
+
+public func kGetSubFrame(superSize: CGSize, subViewSize: CGSize, contentMode: UIView.ContentMode = .scaleAspectFit, scaleFactor: CGFloat = 1.0)-> CGRect {
+    guard scaleFactor > 0 && scaleFactor <= 1 else {
+        print("Invalid scaleFactor. It must be in the range (0, 1].")
+        return CGRect(origin: .zero, size: superSize)
+    }
+    
+    var maskFrame: CGRect = .zero
+    switch contentMode {
+    case .scaleToFill:
+        // 拉伸填充整个视图
+        maskFrame = CGRect(origin: .zero, size: superSize)
+        
+    case .scaleAspectFit:
+        // 按比例缩放,适配视图并居中
+        let aspectWidth = superSize.width / subViewSize.width
+        let aspectHeight = superSize.height / subViewSize.height
+        let aspectRatio = min(aspectWidth, aspectHeight) // 按比例适配
+        
+        let scaledWidth = subViewSize.width * aspectRatio * scaleFactor
+        let scaledHeight = subViewSize.height * aspectRatio * scaleFactor
+        let xOffset = (superSize.width - scaledWidth) / 2
+        let yOffset = (superSize.height - scaledHeight) / 2
+        
+        maskFrame = CGRect(x: xOffset, y: yOffset, width: scaledWidth, height: scaledHeight)
+        
+    case .scaleAspectFill:
+        // 按比例缩放,填充视图并裁剪多余部分
+        let aspectWidth = superSize.width / subViewSize.width
+        let aspectHeight = superSize.height / subViewSize.height
+        let aspectRatio = max(aspectWidth, aspectHeight) // 按比例填充
+        
+        let scaledWidth = subViewSize.width * aspectRatio * scaleFactor
+        let scaledHeight = subViewSize.height * aspectRatio * scaleFactor
+        let xOffset = (superSize.width - scaledWidth) / 2
+        let yOffset = (superSize.height - scaledHeight) / 2
+        
+        maskFrame = CGRect(x: xOffset, y: yOffset, width: scaledWidth, height: scaledHeight)
+    default:
+        maskFrame = CGRect(origin: .zero, size: superSize)
+    }
+    
+    return maskFrame
+}
+
+
+public extension UIView {
+    /// 添加渐变色边框
+    /// - Parameters:
+    ///   - colors: 渐变色数组
+    ///   - width: 边框宽度
+    ///   - radius: 圆角半径(nil 时使用视图的现有圆角)
+    ///   - startPoint: 渐变起点 (默认从左到右)
+    ///   - endPoint: 渐变终点 (默认从左到右)
+    func addGradientBorder(
+        colors: [UIColor],
+        width: CGFloat = 3,
+        radius: CGFloat? = nil,
+        startPoint: CGPoint = CGPoint(x: 0, y: 0.5),
+        endPoint: CGPoint = CGPoint(x: 1, y: 0.5)
+    ) {
+        // 移除旧的渐变边框
+        removeGradientBorder()
+        
+        let gradientLayer = CAGradientLayer()
+        gradientLayer.name = "GradientBorder"
+        gradientLayer.frame = bounds
+        gradientLayer.colors = colors.map { $0.cgColor }
+        gradientLayer.startPoint = startPoint
+        gradientLayer.endPoint = endPoint
+        
+        let cornerRadius = radius ?? layer.cornerRadius
+        let path = UIBezierPath(
+            roundedRect: bounds.insetBy(dx: width/2, dy: width/2),
+            cornerRadius: cornerRadius
+        )
+        
+        let shapeLayer = CAShapeLayer()
+        shapeLayer.name = "GradientBorderMask"
+        shapeLayer.path = path.cgPath
+        shapeLayer.lineWidth = width
+        shapeLayer.fillColor = nil
+        shapeLayer.strokeColor = UIColor.black.cgColor
+        
+        gradientLayer.mask = shapeLayer
+        layer.addSublayer(gradientLayer)
+    }
+    
+    /// 更新渐变色边框的frame(在layoutSubviews中调用)
+    func updateGradientBorder() {
+        guard let gradientLayer = layer.sublayers?.first(where: { $0.name == "GradientBorder" }) as? CAGradientLayer,
+              let shapeLayer = gradientLayer.mask as? CAShapeLayer else {
+            return
+        }
+        
+        gradientLayer.frame = bounds
+        
+        let width = shapeLayer.lineWidth
+        let cornerRadius = layer.cornerRadius
+        let path = UIBezierPath(
+            roundedRect: bounds.insetBy(dx: width/2, dy: width/2),
+            cornerRadius: cornerRadius
+        )
+        
+        shapeLayer.path = path.cgPath
+    }
+    
+    /// 移除渐变色边框
+    func removeGradientBorder() {
+        layer.sublayers?.filter { $0.name == "GradientBorder" }.forEach {
+            $0.removeFromSuperlayer()
+        }
+    }
+}

+ 132 - 0
AIPlayRingtones/Classes/Ex/UIView+Frame.swift

@@ -0,0 +1,132 @@
+//
+//  UIView+Basic.swift
+//  Pods
+//
+//  Created by 100Years on 2025/3/6.
+//
+
+// Frame
+public extension UIView {
+    
+    
+    @IBInspectable
+    public var cornerRadius: CGFloat {
+        set {
+            clipsToBounds = true
+            layer.cornerRadius = newValue
+        }
+        get {
+            layer.cornerRadius
+        }
+    }
+    
+    @IBInspectable
+    public var borderWidth: CGFloat {
+        set {
+            layer.borderWidth = newValue
+        }
+        get {
+            layer.borderWidth
+        }
+    }
+    
+    @IBInspectable
+    public var borderColor: UIColor? {
+        set {
+            layer.borderColor = newValue?.cgColor
+        }
+        get {
+            if let cgColor = layer.borderColor {
+                return UIColor(cgColor: cgColor)
+            }
+            return nil
+        }
+    }
+    
+    
+    var x: CGFloat {
+        set {
+            frame.origin.x = newValue
+        }
+        get {
+            return frame.origin.x
+        }
+    }
+    
+    var y: CGFloat {
+        set {
+            frame.origin.y = newValue
+        }
+        get {
+            return frame.origin.y
+        }
+    }
+    
+    var width: CGFloat {
+        set {
+            frame.size.width = newValue
+        }
+        get {
+            return frame.size.width
+        }
+    }
+    
+    var height: CGFloat {
+        set {
+            frame.size.height = newValue
+        }
+        get {
+            return frame.size.height
+        }
+    }
+    
+    var size: CGSize {
+        get { frame.size }
+        set {
+            var rect = frame
+            rect.size = newValue
+            frame = rect
+        }
+    }
+    
+    var centerX: CGFloat {
+        set {
+            let newCenter = CGPoint(x: newValue, y: center.y)
+            center = newCenter
+        }
+        get {
+            return center.x
+        }
+    }
+    
+    var centerY: CGFloat {
+        set {
+            let newCenter = CGPoint(x: center.x, y: newValue)
+            center = newCenter
+        }
+        get {
+            return center.y
+        }
+    }
+    
+    var bottom: CGFloat {
+        get {
+            return frame.origin.y + frame.size.height
+        }
+    }
+    
+}
+
+public extension UIScreen {
+    static let size = {
+        UIScreen.main.bounds.size
+    }()
+    
+    static var width: CGFloat {
+        return size.width
+    }
+    
+    static var height: CGFloat {
+        return size.height
+    }
+}

+ 73 - 0
AIPlayRingtones/Classes/Ex/UIView+Rotating.swift

@@ -0,0 +1,73 @@
+//
+//  UIView+A.swift
+//  Pods
+//
+//  Created by 100Years on 2025/3/6.
+//
+
+import UIKit
+
+public extension UIView {
+    // MARK: - 旋转动画控制
+    /// 开始旋转动画
+    /// - Parameters:
+    ///   - duration: 单圈旋转时间(默认 1 秒)
+    ///   - clockwise: 是否顺时针旋转(默认 true)
+    ///   - repeatCount: 重复次数(默认无限循环 .greatestFiniteMagnitude)
+    func startRotating(duration: CFTimeInterval = 1.0,
+                     clockwise: Bool = true,
+                     repeatCount: Float = .greatestFiniteMagnitude) {
+        // 同步移除已有旋转动画
+        self.stopRotating()
+        
+        DispatchQueue.main.async {
+            // 创建旋转动画
+            let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
+            rotationAnimation.fromValue = 0
+            rotationAnimation.toValue = clockwise ? CGFloat.pi * 2.0 : -CGFloat.pi * 2.0
+            rotationAnimation.duration = duration
+            rotationAnimation.isCumulative = true
+            rotationAnimation.repeatCount = repeatCount
+            rotationAnimation.timingFunction = CAMediaTimingFunction(name: .linear)
+            
+            // 防止动画结束后恢复初始状态
+            rotationAnimation.fillMode = .forwards
+            rotationAnimation.isRemovedOnCompletion = false
+            
+            // 添加动画到视图的 layer
+            self.layer.add(rotationAnimation, forKey: "rotationAnimation")
+        }
+    }
+    
+    /// 停止旋转动画
+    func stopRotating() {
+        // 同步移除旋转动画
+        DispatchQueue.main.async {
+            self.layer.removeAnimation(forKey: "rotationAnimation")
+        }
+    }
+}
+
+public extension UIView {
+    /// 图片透明度循环动画
+    /// - Parameters:
+    ///   - minAlpha: 最小透明度(默认为 0.1)
+    ///   - duration: 动画时长(单次透明度变化的时长,默认为 1 秒)
+    func startAlphaAnimation(minAlpha: CGFloat = 0.1, duration: TimeInterval = 1.0) {
+        // 防止重复添加动画
+        self.layer.removeAllAnimations()
+        
+        // 开始动画
+        UIView.animate(withDuration: duration, delay: 0, options: [.autoreverse, .repeat]) {
+            self.alpha = minAlpha
+        } completion: { _ in
+            self.alpha = 1.0 // 确保最终状态为 1.0
+        }
+    }
+    
+    /// 停止透明度动画
+    func stopAlphaAnimation() {
+        self.layer.removeAllAnimations()
+        self.alpha = 1.0 // 恢复到默认状态
+    }
+}

+ 154 - 0
AIPlayRingtones/Classes/Ex/UIViewController+Ex.swift

@@ -0,0 +1,154 @@
+//
+//  UIViewController+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/30.
+//
+
+public extension UIViewController {
+    func showCustomAlert(
+        title: String? = "",
+        message: String?,
+        rightActionString:String = "Delete".localized,
+        deleteHandler: (() -> Void)? = nil,
+        cancelHandler: (() -> Void)? = nil
+    ) {
+        let alert = UIAlertController(title: nil, message: nil, preferredStyle: .alert)
+        
+        // 自定义标题
+        if let title = title {
+            let titleAttributes: [NSAttributedString.Key: Any] = [
+                .font: UIFont.boldSystemFont(ofSize: 20),
+                .foregroundColor: UIColor.white
+            ]
+            let attributedTitle = NSAttributedString(string: title, attributes: titleAttributes)
+            alert.setValue(attributedTitle, forKey: "attributedTitle")
+        }
+        
+        // 自定义消息
+        if let message = message {
+            let messageAttributes: [NSAttributedString.Key: Any] = [
+                .font: UIFont.boldSystemFont(ofSize: 17),
+                .foregroundColor: UIColor.white
+            ]
+            let attributedMessage = NSAttributedString(string: message, attributes: messageAttributes)
+            alert.setValue(attributedMessage, forKey: "attributedMessage")
+        }
+        
+        // 添加按钮
+        let cancelAction = UIAlertAction(title: "Cancel".localized, style: .cancel) { _ in
+            cancelHandler?()
+        }
+        alert.addAction(cancelAction)
+        
+        let deleteAction = UIAlertAction(title: rightActionString, style: .destructive) { _ in
+            deleteHandler?()
+        }
+        alert.addAction(deleteAction)
+        
+        // 设置黑暗模式
+        alert.overrideUserInterfaceStyle = .dark
+        
+        // 显示弹窗
+        present(alert, animated: true, completion: nil)
+    }
+    
+    
+    
+    
+    
+    func showDeleteAlert(
+        title: String? = "",
+        message: String?,
+        deleteHandler: (() -> Void)? = nil,
+        cancelHandler: (() -> Void)? = nil
+    ) {
+        let alert = UIAlertController(title: nil, message: nil, preferredStyle: .alert)
+        
+        // 自定义标题
+        if let title = title {
+            let titleAttributes: [NSAttributedString.Key: Any] = [
+                .font: UIFont.boldSystemFont(ofSize: 20),
+                .foregroundColor: UIColor.white
+            ]
+            let attributedTitle = NSAttributedString(string: title, attributes: titleAttributes)
+            alert.setValue(attributedTitle, forKey: "attributedTitle")
+        }
+        
+        // 自定义消息
+        if let message = message {
+            let messageAttributes: [NSAttributedString.Key: Any] = [
+                .font: UIFont.boldSystemFont(ofSize: 17),
+                .foregroundColor: UIColor.white
+            ]
+            let attributedMessage = NSAttributedString(string: message, attributes: messageAttributes)
+            alert.setValue(attributedMessage, forKey: "attributedMessage")
+        }
+        
+        // 添加按钮
+        let cancelAction = UIAlertAction(title: "Cancel".localized, style: .cancel) { _ in
+            cancelHandler?()
+        }
+        alert.addAction(cancelAction)
+        
+        let deleteAction = UIAlertAction(title: "Delete".localized, style: .destructive) { _ in
+            deleteHandler?()
+        }
+        alert.addAction(deleteAction)
+        
+        // 设置黑暗模式
+        alert.overrideUserInterfaceStyle = .dark
+        
+        // 显示弹窗
+        present(alert, animated: true, completion: nil)
+    }
+    
+    
+    func showDeleteErrorAlert(
+        title: String? = "",
+        message: String?,
+        deleteHandler: (() -> Void)? = nil,
+        cancelHandler: (() -> Void)? = nil
+    ) {
+        let alert = UIAlertController(title: nil, message: nil, preferredStyle: .alert)
+        
+        // 自定义标题
+        if let title = title {
+            let titleAttributes: [NSAttributedString.Key: Any] = [
+                .font: UIFont.boldSystemFont(ofSize: 20),
+                .foregroundColor: UIColor.white
+            ]
+            let attributedTitle = NSAttributedString(string: title, attributes: titleAttributes)
+            alert.setValue(attributedTitle, forKey: "attributedTitle")
+        }
+        
+        // 自定义消息
+        if let message = message {
+            let messageAttributes: [NSAttributedString.Key: Any] = [
+                .font: UIFont.boldSystemFont(ofSize: 17),
+                .foregroundColor: UIColor.white
+            ]
+            let attributedMessage = NSAttributedString(string: message, attributes: messageAttributes)
+            alert.setValue(attributedMessage, forKey: "attributedMessage")
+        }
+        
+        // 添加按钮
+        let cancelAction = UIAlertAction(title: "Retain".localized, style: .cancel) { _ in
+            cancelHandler?()
+        }
+        alert.addAction(cancelAction)
+        
+        let deleteAction = UIAlertAction(title: "Delete".localized, style: .destructive) { _ in
+            deleteHandler?()
+        }
+        alert.addAction(deleteAction)
+        
+        // 设置黑暗模式
+        alert.overrideUserInterfaceStyle = .dark
+        
+        // 显示弹窗
+        present(alert, animated: true, completion: nil)
+    }
+}
+
+

+ 30 - 0
AIPlayRingtones/Classes/Ex/UserDefault+Ex.swift

@@ -0,0 +1,30 @@
+//
+//  UserDefault+Ex.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/30.
+//
+
+import Foundation
+
+@propertyWrapper
+public struct UserDefault<T> {
+    public let key: String
+    public let defaultValue: T
+    public let storage: UserDefaults
+
+    public init(key: String, defaultValue: T, storage: UserDefaults = .standard) {
+        self.key = key
+        self.defaultValue = defaultValue
+        self.storage = storage
+    }
+
+    public var wrappedValue: T {
+        get {
+            return storage.object(forKey: key) as? T ?? defaultValue
+        }
+        set {
+            storage.set(newValue, forKey: key)
+        }
+    }
+}

+ 167 - 0
AIPlayRingtones/Classes/GlobalImports/GlobalImports.swift

@@ -0,0 +1,167 @@
+//
+//  GlobalImports.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+@_exported import Foundation
+@_exported import UIKit
+@_exported import SnapKit
+
+import AVFoundation
+
+public func k_Height_statusBar() -> CGFloat {
+    var statusBarHeight: CGFloat = 0;
+    if #available(iOS 13.0, *) {
+        let scene = UIApplication.shared.connectedScenes.first;
+        guard let windowScene = scene as? UIWindowScene else {return 0};
+        guard let statusBarManager = windowScene.statusBarManager else {return 0};
+        statusBarHeight = statusBarManager.statusBarFrame.height;
+    } else {
+        statusBarHeight = UIApplication.shared.statusBarFrame.height;
+    }
+    return statusBarHeight;
+}
+/// ②、顶部安全区高度 k_Height_safeAreaInsetsTop
+public func k_Height_safeAreaInsetsTop() -> CGFloat {
+    if #available(iOS 13.0, *) {
+        let scene = UIApplication.shared.connectedScenes.first;
+        guard let windowScene = scene as? UIWindowScene else {return 0}; // guard:如果 expression 值计算为false,则执行代码块内的 guard 语句。(必须包含一个控制语句: return、 break、 continue 或 throw。)。as?:类型转换,(还有这两种:as、as!)
+        guard let window = windowScene.windows.first else {return 0};
+        return window.safeAreaInsets.top;
+    } else if #available(iOS 11.0, *) {
+        guard let window = UIApplication.shared.windows.first else {return 0};
+        return window.safeAreaInsets.top;
+    }
+    return 0;
+}
+
+/// ③、底部安全区高度
+public func k_Height_safeAreaInsetsBottom() -> CGFloat {
+    if #available(iOS 13.0, *) {
+        let scene = UIApplication.shared.connectedScenes.first;
+        guard let windowScene = scene as? UIWindowScene else {return 0};
+        guard let window = windowScene.windows.first else {return 0};
+        return window.safeAreaInsets.bottom;
+    } else if #available(iOS 11.0, *) {
+        guard let window = UIApplication.shared.windows.first else {return 0};
+        return window.safeAreaInsets.bottom;
+    }
+    return 0;
+}
+/** 屏幕宽度 */
+public let k_ScreenWidth = UIScreen.main.bounds.size.width
+/** 屏幕高度 */
+public let k_ScreenHeight = UIScreen.main.bounds.size.height
+/* 导航栏高度 固定高度 = 44.0f */
+//let k_Height_NavContentBar :CGFloat  = UINavigationBar.appearance().frame.size.height
+public let k_Height_NavBar :CGFloat = 44.0
+/** 状态栏高度 */
+public let k_Height_StatusBar :CGFloat = k_Height_statusBar()
+/** 状态栏+导航栏的高度 */
+public let k_Nav_Height: CGFloat = k_Height_NavBar + k_Height_StatusBar
+/** 底部tabBar栏高度(不包含安全区,即:在 iphoneX 之前的手机) */
+public let k_TabBar_Height :CGFloat = 49.0
+/** 底部导航栏高度(包括安全区),一般使用这个值 */
+public let k_Height_TabBar :CGFloat = k_Height_safeAreaInsetsBottom() + k_TabBar_Height
+
+
+
+// MARK: - 检查系统版本 -
+/* 检查系统版本 */
+/// 版本号相同:
+public func systemVersionEqual(version:String) -> Bool {
+    return UIDevice.current.systemVersion == version
+}
+
+/// 系统版本高于等于该version  测试发现只能传入带一位小数点的版本号  不然会报错    具体原因待探究
+public func systemVersionGreaterThan(version:String) -> Bool {
+    return UIDevice.current.systemVersion.compare(version, options: .numeric, range: version.startIndex..<version.endIndex, locale: Locale(identifier:version)) != ComparisonResult.orderedAscending
+}
+
+//判断是否是 x、及x以上 系列
+public func isIphoneX() -> Bool {
+    return k_Height_safeAreaInsetsBottom() > 0.0; // 底部安全区 > 0 时,
+    
+}
+
+//设计稿为 375*812 比例,所以比例系数为 375.0/kScreenWidth
+public let kDesignScale = k_ScreenWidth/375.0
+
+
+public func kGetUIH(designSize:CGSize,currentW:CGFloat) -> CGFloat {
+    let scale = designSize.width/currentW
+    return designSize.height/scale
+}
+    
+
+public func debugPrint<T>(_ messsage: T, file: String = #file, funcName: String = #function, lineNum: Int = #line) {
+//    #if DEBUG
+    let fileName = (file as NSString).lastPathComponent
+    print(Date.hmsString + " \(fileName) (\(funcName)): [\(lineNum)]- \(messsage)")
+//    #endif
+}
+
+public func dePrint<T>(_ messsage: T) {
+//    #if DEBUG
+    print(Date.hmsString + " \(messsage)")
+//    #endif
+}
+
+public func className(from object: Any) -> String {
+    return String(describing: type(of: object))
+}
+
+public func appShortVersion() ->String{
+    let short = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
+    return short
+}
+
+public func appVersion() ->String{
+    let short = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
+    return "V" + short
+}
+
+/// 震动
+public func playVibration() {
+    // 默认震动   kSystemSoundID_Vibrate
+    // 1519     短震 3D Touch中的peek震动反馈
+    // 1520     短震 3D Touch中的pop震动反馈
+    // 1521     连续三次短震动
+    // peek的震动反馈轻于pop
+    
+    let soundID: UInt32 = 1520
+    AudioServicesPlaySystemSound(soundID)
+}
+
+public func kPresentModalVC(target:UIViewController,
+                     modelVC:UIViewController,
+                       style:UIModalPresentationStyle = .overFullScreen,
+             transitionStyle:UIModalTransitionStyle = .coverVertical,
+                  completion: (() -> Void)? = nil){
+    let navi = TSBaseNavigationC(rootViewController: modelVC)
+    navi.modalPresentationStyle = style
+    navi.modalTransitionStyle = transitionStyle
+    target.present(navi, animated: true,completion: completion)
+}
+
+public func kPushVC(target:UIViewController,modelVC:UIViewController){
+    modelVC.hidesBottomBarWhenPushed = true
+    target.navigationController?.pushViewController(modelVC, animated: true)
+}
+
+public func kGetVideoThumbnail(from videoURL: URL) -> UIImage? {
+    let asset = AVAsset(url: videoURL)
+    let assetImgGenerate = AVAssetImageGenerator(asset: asset)
+    assetImgGenerate.appliesPreferredTrackTransform = true
+    
+    let time = CMTimeMakeWithSeconds(0.0, preferredTimescale: 600) // 获取第0秒的帧
+    do {
+        let imgRef = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
+        return UIImage(cgImage: imgRef)
+    } catch {
+        print("Error generating thumbnail: \(error.localizedDescription)")
+        return nil
+    }
+}

+ 147 - 0
AIPlayRingtones/Classes/Tool/PhotoManager.swift

@@ -0,0 +1,147 @@
+//
+//  PhotoManager.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/24.
+//
+
+
+
+
+import Photos
+import UIKit
+
+public let PhotoManagerShared = PhotoManager.shared
+open class PhotoManager {
+    public static let shared = PhotoManager()
+
+    public func saveImagesToUniqueAlbum(images: [UIImage], baseAlbumName: String, completion: @escaping (Bool, Error?) -> Void) {
+        // 检查并请求权限
+        PHPhotoLibrary.requestAuthorization { status in
+            guard status == .authorized else {
+                completion(false, NSError(domain: "PhotoLibrary", code: 1, userInfo: [NSLocalizedDescriptionKey: "未获得相册访问权限"]))
+                return
+            }
+
+            // 获取所有相册名称
+            let allAlbumNames = self.fetchAllAlbumNames()
+            let uniqueAlbumName = self.generateUniqueAlbumName(baseName: baseAlbumName, existingNames: allAlbumNames)
+            
+            // 保存图片到生成的相册
+            self.saveImagesToAlbum(images: images, albumName: uniqueAlbumName, completion: completion)
+        }
+    }
+
+    // 获取所有相册名称
+    private func fetchAllAlbumNames() -> [String] {
+        var albumNames: [String] = []
+        
+        let userCollections = PHCollectionList.fetchTopLevelUserCollections(with: nil)
+        userCollections.enumerateObjects { collection, _, _ in
+            if let album = collection as? PHAssetCollection, let albumName = album.localizedTitle {
+                albumNames.append(albumName)
+            }
+        }
+        
+        return albumNames
+    }
+
+    // 生成唯一的相册名称
+    private func generateUniqueAlbumName(baseName: String, existingNames: [String]) -> String {
+        var uniqueName = baseName
+        var index = 1
+        
+        while existingNames.contains(uniqueName) {
+            uniqueName = "\(baseName)-\(index)"
+            index += 1
+        }
+        
+        return uniqueName
+    }
+
+    // 保存图片到指定相册(之前提供的 saveImagesToAlbum 方法)
+    private func saveImagesToAlbum(images: [UIImage], albumName: String, completion: @escaping (Bool, Error?) -> Void) {
+        var albumPlaceholder: PHObjectPlaceholder?
+        let fetchOptions = PHFetchOptions()
+        fetchOptions.predicate = NSPredicate(format: "title = %@", albumName)
+        let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
+        
+        if let existingAlbum = collection.firstObject {
+            saveImages(images: images, to: existingAlbum, completion: completion)
+        } else {
+            PHPhotoLibrary.shared().performChanges {
+                let creationRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: albumName)
+                albumPlaceholder = creationRequest.placeholderForCreatedAssetCollection
+            } completionHandler: { success, error in
+                guard success, let placeholder = albumPlaceholder else {
+                    completion(false, error)
+                    return
+                }
+                
+                let fetchResult = PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [placeholder.localIdentifier], options: nil)
+                if let newAlbum = fetchResult.firstObject {
+                    self.saveImages(images: images, to: newAlbum, completion: completion)
+                } else {
+                    completion(false, NSError(domain: "PhotoLibrary", code: 2, userInfo: [NSLocalizedDescriptionKey: "创建相册失败"]))
+                }
+            }
+        }
+    }
+
+    // 保存图片到指定相册
+    private func saveImages(images: [UIImage], to album: PHAssetCollection, completion: @escaping (Bool, Error?) -> Void) {
+        PHPhotoLibrary.shared().performChanges {
+            let assetRequest = images.map { PHAssetChangeRequest.creationRequestForAsset(from: $0) }
+            let assetPlaceholders = assetRequest.compactMap { $0.placeholderForCreatedAsset }
+            
+            guard !assetPlaceholders.isEmpty else { return }
+            
+            if let albumChangeRequest = PHAssetCollectionChangeRequest(for: album) {
+                albumChangeRequest.addAssets(assetPlaceholders as NSArray)
+            }
+        } completionHandler: { success, error in
+            completion(success, error)
+        }
+    }
+    
+    
+    
+    /// 保存图片到相册
+    /// - Parameters:
+    ///   - image: 要保存的 UIImage
+    ///   - completion: 保存结果的回调,返回成功与否和错误信息
+    public func saveImageToAlbum(_ image: UIImage, completion: @escaping (Bool, Error?) -> Void) {
+        // 检查相册权限
+        PHPhotoLibrary.requestAuthorization { status in
+            DispatchQueue.main.async {
+                switch status {
+                case .authorized:
+                    // 权限已授权,保存图片
+                    self.save(image: image, completion: completion)
+                case .limited:
+                    // 在受限权限下保存图片
+                    self.save(image: image, completion: completion)
+                case .denied, .restricted:
+                    // 权限被拒绝或受限
+                    completion(false, NSError(domain: "PhotoSaver", code: 1, userInfo: [NSLocalizedDescriptionKey: "Photo Library access is denied."]))
+                case .notDetermined:
+                    // 不会进入这个分支,因为已经请求了权限
+                    completion(false, NSError(domain: "PhotoSaver", code: 2, userInfo: [NSLocalizedDescriptionKey: "Photo Library access not determined."]))
+                @unknown default:
+                    completion(false, NSError(domain: "PhotoSaver", code: 3, userInfo: [NSLocalizedDescriptionKey: "Unknown authorization status."]))
+                }
+            }
+        }
+    }
+
+    /// 保存图片到相册的具体实现
+    private func save(image: UIImage, completion: @escaping (Bool, Error?) -> Void) {
+        PHPhotoLibrary.shared().performChanges({
+            PHAssetChangeRequest.creationRequestForAsset(from: image)
+        }) { success, error in
+            DispatchQueue.main.async {
+                completion(success, error)
+            }
+        }
+    }
+}

+ 63 - 0
AIPlayRingtones/Classes/Tool/SVProgressHUD+Ex.swift

@@ -0,0 +1,63 @@
+//
+//  SVProgressHUD+Ex.swift
+//  TSSmalCoacopods_Example
+//
+//  Created by 100Years on 2025/3/11.
+//  Copyright © 2025 CocoaPods. All rights reserved.
+//
+import Foundation
+import ObjectiveC
+import SVProgressHUD
+extension SVProgressHUD {
+    // 重写 moveToPoint:rotateAngle: 方法
+    @objc dynamic func swizzledMoveToPoint(_ newCenter: CGPoint, rotateAngle angle: CGFloat) {
+        // 调用原方法
+        self.swizzledMoveToPoint(newCenter, rotateAngle: angle)
+//        print("swizzledMoveToPoint swizzledMoveToPoint")
+        // 使用运行时获取 hudView 属性
+        if let hudView = getHudView() {
+            if let containerView = containerView {
+                hudView.center = CGPoint(x: containerView.bounds.size.width/2 + offsetFromCenter.horizontal, y: containerView.bounds.size.height/2 + offsetFromCenter.vertical)
+            } else {
+                hudView.center = CGPoint(x: newCenter.x + offsetFromCenter.horizontal, y: newCenter.y + offsetFromCenter.vertical)
+            }
+        }
+    }
+    
+    // 获取 hudView 属性
+    private func getHudView() -> UIVisualEffectView? {
+        var count: UInt32 = 0
+        guard let classProperties = class_copyPropertyList(SVProgressHUD.self, &count) else {
+            return nil
+        }
+        
+        for i in 0..<Int(count) {
+            let property = classProperties[i]
+            let propertyName = String(cString: property_getName(property))
+            if propertyName == "hudView" {
+                let hudView = value(forKey: propertyName) as? UIVisualEffectView
+                free(classProperties)
+                return hudView
+            }
+        }
+        
+        free(classProperties)
+        return nil
+    }
+}
+
+// 方法交换
+extension SVProgressHUD {
+    static func swizzleMethods() {
+        // 手动创建选择器
+        let originalSelector = NSSelectorFromString("moveToPoint:rotateAngle:")
+        let swizzledSelector = #selector(SVProgressHUD.swizzledMoveToPoint(_:rotateAngle:))
+        
+        let originalMethod = class_getInstanceMethod(SVProgressHUD.self, originalSelector)
+        let swizzledMethod = class_getInstanceMethod(SVProgressHUD.self, swizzledSelector)
+        
+        if let originalMethod = originalMethod, let swizzledMethod = swizzledMethod {
+            method_exchangeImplementations(originalMethod, swizzledMethod)
+        }
+    }
+}

+ 151 - 0
AIPlayRingtones/Classes/Tool/TSBusinessFileManager.swift

@@ -0,0 +1,151 @@
+//
+//  TSBusinessFileManager.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/27.
+//
+
+class TSBusinessFileManager {
+    
+    /// 获取 Video 下载后保存的的文件件路径
+    static var saveVideoPathURL:URL = {
+        let saveRingPathURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("video")
+        return saveRingPathURL
+    }()
+    
+    static var saveCacheAllPathURL:URL = {
+        let saveRingPathURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("cacheAll")
+        return saveRingPathURL
+    }()
+    
+    public static func generateFileName(
+        urlString: String,
+        fileEx:String? = nil,
+    missingEx:String? = nil
+    )->String?{
+        guard let url = URL(string: urlString) else{
+            return nil
+        }
+        
+        var fileName = url.path.md5
+
+        // 使用 URL 的 MD5 哈希值作为缓存文件名,附加 URL 的后缀名
+        var fileExtension = ""
+        if let fileEx = fileEx {
+            fileExtension = fileEx
+        }else{
+            var missingExStr = ""
+            if let missingEx = missingEx {
+                missingExStr = missingEx
+            }
+            fileExtension = url.pathExtension.isEmpty ? missingExStr : url.pathExtension
+        }
+
+        if fileExtension.count > 0 {
+            fileName = url.path.md5 + ".\(fileExtension)"
+        }
+         
+        return fileName
+    }
+    
+    //获取 urlstring 本地的缓存 url path
+    public static func getLocalURL(
+        urlString: String,
+        fileEx:String? = nil,
+    missingEx:String? = nil)->URL?{
+        
+        guard let fileName = generateFileName(urlString: urlString,fileEx:fileEx,missingEx: missingEx) else{
+            return nil
+        }
+        
+        //检查文件是否已存在于缓存中
+        let ringFileURL = saveVideoPathURL.appendingPathComponent(fileName)
+        if FileManager.default.fileExists(atPath: ringFileURL.path) {
+            print("文件已存在于缓存中: \(ringFileURL)")
+            return ringFileURL
+        }
+        
+        let cachedFileURL = saveCacheAllPathURL.appendingPathComponent(fileName)
+        if FileManager.default.fileExists(atPath: cachedFileURL.path) {
+            print("文件已存在于缓存中: \(cachedFileURL)")
+            return cachedFileURL
+        }
+        
+        return nil
+    }
+    
+
+}
+
+//缓存路径
+extension TSBusinessFileManager {
+    
+    //检查 url 对不对
+    public static func generateCachesURL(
+        from urlString: String,
+        fileEx:String? = nil,
+    missingEx:String? = nil,
+        cacheDirectory:String = "cacheAll",
+        completion:((String?, Error?) -> Void)? = nil
+    )->URL?
+    {
+        guard let url = URL(string: urlString) else{
+            completion?(nil, NSError(domain: "url null", code: 0))
+            return nil
+        }
+        
+        if !urlString.contains("http") && urlString.contains("/"){
+            completion?(urlString.fillCachePath, nil)
+            return nil
+        }
+        
+        let fileManager = FileManager.default
+        
+        // 获取缓存目录下的 `cacheAll` 文件夹路径
+        let cachesDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
+        let cacheAllDirectory = cachesDirectory.appendingPathComponent(cacheDirectory)
+        
+        // 创建 `cacheAll` 文件夹(如果不存在)
+        if !fileManager.fileExists(atPath: cacheAllDirectory.path) {
+            do {
+                try fileManager.createDirectory(at: cacheAllDirectory, withIntermediateDirectories: true, attributes: nil)
+            } catch {
+                completion?(nil, error)
+                return nil
+            }
+        }
+        
+        
+        guard let fileName = generateFileName(urlString: urlString,fileEx:fileEx,missingEx: missingEx) else{
+            completion?(nil, NSError(domain: "url error", code: 0))
+            return nil
+        }
+
+        let cachedFileURL = cacheAllDirectory.appendingPathComponent(fileName)
+        return cachedFileURL
+    }
+    
+    //获取 urlstring 本地的缓存 url path
+    public static func getCachesURL(
+        from urlString: String,
+        fileEx:String? = nil,
+    missingEx:String? = nil,
+        cacheDirectory:String = "cacheAll")->URL?{
+        
+        if let cachedFileURL = generateCachesURL(
+            from: urlString,
+            fileEx: fileEx,
+            missingEx: missingEx,
+            cacheDirectory: cacheDirectory
+        ){
+            //检查文件是否已存在于缓存中
+            if FileManager.default.fileExists(atPath: cachedFileURL.path) {
+                print("文件已存在于缓存中: \(cachedFileURL)")
+                return cachedFileURL
+            }
+        }
+        
+        return nil
+    }
+    
+}

+ 78 - 0
AIPlayRingtones/Classes/Tool/TSCollectionViewObserver.swift

@@ -0,0 +1,78 @@
+//
+//  Untitled.swift
+//  Pods
+//
+//  Created by 100Years on 2025/5/16.
+//
+
+
+public class TSCollectionViewObserver {
+    private weak var collectionView: UICollectionView?
+    private var observers: [NSKeyValueObservation] = []
+    
+    public var onContentSizeChange: ((CGSize) -> Void)?
+    public var onContentInsetChange: ((UIEdgeInsets) -> Void)?
+    public var onContentOffsetChange: ((CGPoint) -> Void)?
+    
+    public init(collectionView: UICollectionView) {
+        self.collectionView = collectionView
+        setupObservers()
+    }
+    
+    private func setupObservers() {
+        guard let collectionView = collectionView else { return }
+        
+        observers.append(
+            collectionView.observe(\.contentSize, options: [.new, .old]) { [weak self] (_, change) in
+                guard let newSize = change.newValue, newSize != change.oldValue else { return }
+                self?.onContentSizeChange?(newSize)
+            }
+        )
+        
+        observers.append(
+            collectionView.observe(\.contentInset, options: [.new, .old]) { [weak self] (_, change) in
+                guard let newInset = change.newValue, newInset != change.oldValue else { return }
+                self?.onContentInsetChange?(newInset)
+            }
+        )
+        
+        observers.append(
+            collectionView.observe(\.contentOffset, options: [.new]) { [weak self] (_, change) in
+                guard let newOffset = change.newValue else { return }
+                self?.onContentOffsetChange?(newOffset)
+            }
+        )
+    }
+    
+    deinit {
+        observers.forEach { $0.invalidate() }
+    }
+}
+
+
+
+//        collectionViewObserverHandle()
+//
+//private var collectionViewObserver: CollectionViewObserver!
+//func collectionViewObserverHandle(){
+//    collectionViewObserver = CollectionViewObserver(collectionView: collectionComponent.collectionView)
+//
+//    collectionViewObserver.onContentSizeChange = { size in
+//        print("collectionViewObserver 内容大小变化: \(size)")
+//    }
+//
+//    collectionViewObserver.onContentInsetChange = { inset in
+//        print("collectionViewObserver 内边距变化: \(inset)")
+//    }
+//
+//    collectionViewObserver.onContentOffsetChange = { offset in
+//        print("collectionViewObserver 偏移量变化: \(offset)")
+//    }
+//
+//}
+//
+//
+//func scrollViewDidScroll(_ scrollView: UIScrollView) {
+//    print("contentOffset 变化: \(scrollView.contentOffset)")
+//    // 实时监听偏移量变化
+//}

+ 182 - 0
AIPlayRingtones/Classes/Tool/TSCommonTool/TSCommonTool+Down.swift

@@ -0,0 +1,182 @@
+//
+//  TSCommonTool+Down.swift
+//  Pods
+//
+//  Created by 100Years on 2025/3/17.
+//
+
+public extension TSCommonTool {
+    
+    /// 下载并缓存文件,依据 URL 的后缀名动态设置文件名
+    /// - Parameters:
+    ///   - url: 文件的 URL 地址
+    ///   - completion: 完成回调,返回本地缓存路径或错误
+    public static func downloadAndCacheFile(from urlString: String, fileEx:String? = nil,missingEx:String? = nil, cacheDirectory:String = "cacheAll",completion: @escaping (String?, Error?) -> Void) {
+        
+        guard let url = URL(string: urlString) else{
+            completion(nil, NSError(domain: "url null", code: 0))
+            return
+        }
+        
+        
+        guard let cachedFileURL = checkURLString(
+            from: urlString,
+            fileEx: fileEx,
+            missingEx: missingEx,
+            cacheDirectory: cacheDirectory,
+            completion: completion
+        ) else {
+            return
+        }
+        
+        let fileManager = FileManager.default
+        //检查文件是否已存在于缓存中
+        if fileManager.fileExists(atPath: cachedFileURL.path) {
+            print("文件已存在于缓存中: \(cachedFileURL)")
+            completion(cachedFileURL.path, nil)
+            return
+        }
+        
+        // 下载文件
+        let task = URLSession.shared.downloadTask(with: url) { tempFileURL, response, error in
+            if let error = error {
+                DispatchQueue.main.async {
+                    completion(nil, error)
+                }
+                return
+            }
+            
+            guard let tempFileURL = tempFileURL else {
+                DispatchQueue.main.async {
+                    completion(nil, NSError(domain: "DownloadError", code: -1, userInfo: [NSLocalizedDescriptionKey: "临时文件路径不存在"]))
+                }
+                return
+            }
+            
+            do {
+                if fileManager.fileExists(atPath: cachedFileURL.path) {
+                    try fileManager.removeItem(atPath:cachedFileURL.path)
+                    dePrint("下载成功,移除已有的旧文件: \(cachedFileURL)")
+                }
+                try fileManager.moveItem(at: tempFileURL, to: cachedFileURL)
+                dePrint("文件下载并缓存成功: \(cachedFileURL)")
+                DispatchQueue.main.async {
+                    completion(cachedFileURL.path, nil)
+                }
+            } catch {
+                dePrint("文件下载成功,但失败:\(error)")
+                DispatchQueue.main.async {
+                    completion(nil, error)
+                }
+            }
+        }
+        
+        task.resume()
+    }
+    
+    
+    //检查 url 对不对
+    public static func checkURLString(
+        from urlString: String,
+        fileEx:String? = nil,
+    missingEx:String? = nil,
+        cacheDirectory:String = "cacheAll",
+        completion:((String?, Error?) -> Void)? = nil
+    )->URL?
+    {
+        guard let url = URL(string: urlString) else{
+            completion?(nil, NSError(domain: "url null", code: 0))
+            return nil
+        }
+        
+        
+        if !urlString.contains("http") && urlString.contains("/"){
+            completion?(urlString.fillCachePath, nil)
+            return nil
+        }
+        
+        let fileManager = FileManager.default
+        
+        // 获取缓存目录下的 `cacheAll` 文件夹路径
+        let cachesDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
+        let cacheVideoDirectory = cachesDirectory.appendingPathComponent(cacheDirectory)
+        
+        // 创建 `cacheAll` 文件夹(如果不存在)
+        if !fileManager.fileExists(atPath: cacheVideoDirectory.path) {
+            do {
+                try fileManager.createDirectory(at: cacheVideoDirectory, withIntermediateDirectories: true, attributes: nil)
+            } catch {
+                completion?(nil, error)
+                return nil
+            }
+        }
+        
+        var fileName = url.path.md5
+        
+        // 使用 URL 的 MD5 哈希值作为缓存文件名,附加 URL 的后缀名
+        var fileExtension = ""
+        
+        if let fileEx = fileEx {
+            fileExtension = fileEx
+        }else{
+            var missingExStr = ""
+            if let missingEx = missingEx {
+                missingExStr = missingEx
+            }
+            fileExtension = url.pathExtension.isEmpty ? missingExStr : url.pathExtension
+        }
+
+        if fileExtension.count > 0 {
+            fileName = url.path.md5 + ".\(fileExtension)"
+        }
+        
+        let cachedFileURL = cacheVideoDirectory.appendingPathComponent(fileName)
+        return cachedFileURL
+
+    }
+    
+//    func a(){
+//        var fileName = url.path.md5
+//        
+//        // 使用 URL 的 MD5 哈希值作为缓存文件名,附加 URL 的后缀名
+//        var fileExtension = ""
+//        
+//        if let fileEx = fileEx {
+//            fileExtension = "fileEx"
+//        }else{
+//            var missingExStr = ""
+//            if let missingEx = missingEx {
+//                missingExStr = missingEx
+//            }
+//            fileExtension = url.pathExtension.isEmpty ? missingExStr : url.pathExtension
+//        }
+//
+//        if fileExtension.count > 0 {
+//            fileName = url.path.md5 + ".\(fileExtension)"
+//        }
+//
+//    }
+    
+    //获取 urlstring 本地的缓存 url path
+    public static func getCachedURLString(
+        from urlString: String,
+        fileEx:String? = nil,
+    missingEx:String? = nil,
+        cacheDirectory:String = "cacheAll")->URL?{
+        
+        if let cachedFileURL = checkURLString(
+            from: urlString,
+            fileEx: fileEx,
+            missingEx: missingEx,
+            cacheDirectory: cacheDirectory
+        ){
+            //检查文件是否已存在于缓存中
+            if FileManager.default.fileExists(atPath: cachedFileURL.path) {
+                print("文件已存在于缓存中: \(cachedFileURL)")
+                return cachedFileURL
+            }
+        }
+        
+        return nil
+    }
+}

+ 89 - 0
AIPlayRingtones/Classes/Tool/TSCommonTool/TSCommonTool+MultDown.swift

@@ -0,0 +1,89 @@
+//
+//  MultiTaskDown.swift
+//  Pods
+//
+//  Created by 100Years on 2025/3/17.
+//
+
+//public extension TSCommonTool {
+//    
+//    /// 多任务下载并缓存文件,依据 URL 的后缀名动态设置文件名
+//    /// - Parameters:
+//    ///   - url: 文件的 URL 地址
+//    ///   - completion: 完成回调,返回本地缓存路径或错误
+//    public static func multidownloadAndCacheFile(
+//        from urlString: String,
+//        fileEx:String? = nil,
+//     missingEx:String? = nil,
+//        cacheDirectory:String = "cacheAll",
+//        progressHandler: @escaping (Double) -> Void,
+//        completion: @escaping (String?, Error?) -> Void) -> TSMultiTaskDownloader?
+//    {
+//        
+//        guard let url = URL(string: urlString) else{
+//            completion(nil, NSError(domain: "url null", code: 0))
+//            return nil
+//        }
+//        
+//    
+//        guard let cachedFileURL = checkURLString(
+//            from: urlString,
+//            fileEx: fileEx,
+//            missingEx: missingEx,
+//            cacheDirectory: cacheDirectory,
+//            completion: completion
+//        ) else {
+//            return nil
+//        }
+//        
+//        let fileManager = FileManager.default
+//        //检查文件是否已存在于缓存中
+//        if fileManager.fileExists(atPath: cachedFileURL.path) {
+//            print("文件已存在于缓存中: \(cachedFileURL)")
+//            completion(cachedFileURL.path, nil)
+//            return nil
+//        }
+//        
+//        
+//        let downloader = TSMultiTaskDownloader.shared
+//        downloader.downloadFile(from: url, progressHandler: { progress in
+//            print("Download progress for file1: \(progress * 100)%")
+//            progressHandler(progress)
+//        }, completionHandler: { tempFileURL, error in
+//            if let error = error {
+//                DispatchQueue.main.async {
+//                    completion(nil, error)
+//                }
+//                return
+//            }
+//            
+//            guard let tempFileURL = tempFileURL else {
+//                DispatchQueue.main.async {
+//                    completion(nil, NSError(domain: "DownloadError", code: -1, userInfo: [NSLocalizedDescriptionKey: "临时文件路径不存在"]))
+//                }
+//                return
+//            }
+//            
+//            do {
+//                if fileManager.fileExists(atPath: cachedFileURL.path) {
+//                    try fileManager.removeItem(atPath:cachedFileURL.path)
+//                    dePrint("下载成功,移除已有的旧文件: \(cachedFileURL)")
+//                }
+//                try fileManager.moveItem(at: tempFileURL, to: cachedFileURL)
+//                dePrint("文件下载并缓存成功: \(cachedFileURL)")
+//                DispatchQueue.main.async {
+//                    completion(cachedFileURL.path, nil)
+//                }
+//            } catch {
+//                dePrint("文件下载成功,但失败:\(error)")
+//                DispatchQueue.main.async {
+//                    completion(nil, error)
+//                }
+//            }
+//        })
+//        
+//        return downloader
+//    }
+//    
+//
+//}

+ 191 - 0
AIPlayRingtones/Classes/Tool/TSCommonTool/TSCommonTool.swift

@@ -0,0 +1,191 @@
+//
+//  TSCommonTool.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+import Kingfisher
+import Foundation
+import AVFoundation
+open class TSCommonTool {
+    /// 下载图片,优先从缓存中取,支持设置缓存策略
+    /// - Parameters:
+    ///   - url: 图片的 URL 地址
+    ///   - completion: 完成回调,返回图片或错误
+    public static func fetchImageWithCache(
+        from urlString: String,
+        completion: @escaping (UIImage?, Error?) -> Void
+    ) {
+        if urlString.contains("http") == false,
+           let image = UIImage(named: urlString){
+            completion(image, nil)
+            return
+        }
+        guard let url = URL(string: urlString) else{
+            completion(nil, NSError(domain: "url null", code: 0))
+            return
+        }
+
+        // 配置 Kingfisher 的缓存策略
+        let options: KingfisherOptionsInfo = [
+            .cacheOriginalImage,
+            .memoryCacheExpiration(.days(7)),  // 内存缓存时间
+            .diskCacheExpiration(.days(30)),  // 磁盘缓存时间
+            .cacheSerializer(DefaultCacheSerializer.default)
+        ]
+        
+        // 使用 Kingfisher 检查缓存是否存在
+        let cache = ImageCache.default
+        let cacheKey = url.absoluteString
+        
+        cache.retrieveImage(forKey: cacheKey, options: nil) { result in
+            switch result {
+            case .success(let value):
+                if let image = value.image {
+                    // 从缓存中取到图片
+                    completion(image, nil)
+                } else {
+                    // 缓存中不存在图片,进行下载
+                    downloadImage(from: url, options: options, completion: completion)
+                }
+            case .failure(let error):
+                // 缓存检查失败,直接下载
+                print("缓存检查失败: \(error.localizedDescription)")
+                downloadImage(from: url, options: options, completion: completion)
+            }
+        }
+    }
+    
+    
+    /// 获取已缓存的磁盘图片文件路径
+    /// - Parameters:
+    ///   - urlString: 图片的 URL 地址
+    ///   - completion: 完成回调,返回图片的磁盘路径或错误
+    public static func fetchImagePathWithCache(
+        from urlString: String,
+        completion: @escaping (String?, Error?) -> Void
+    ) {
+        guard let url = URL(string: urlString) else {
+            completion(nil, NSError(domain: "InvalidURL", code: 0, userInfo: [NSLocalizedDescriptionKey: "无效的 URL"]))
+            return
+        }
+        
+        let cache = ImageCache.default
+        let cacheKey = url.absoluteString
+        if cache.isCached(forKey: cacheKey) {
+            completion(cache.cachePath(forKey: cacheKey), nil)
+        }else {
+          completion(nil, NSError(domain: "CachePathError", code: 1, userInfo: [NSLocalizedDescriptionKey: "无法获取缓存路径"]))
+        }
+
+    }
+    
+
+    /// 下载图片并缓存
+    /// - Parameters:
+    ///   - url: 图片的 URL 地址
+    ///   - options: Kingfisher 下载和缓存的选项
+    ///   - completion: 完成回调,返回图片或错误
+    public static func downloadImage(
+        from url: URL,
+        options: KingfisherOptionsInfo,
+        completion: @escaping (UIImage?, Error?) -> Void
+    ) {
+        KingfisherManager.shared.retrieveImage(with: url, options: options,progressBlock: { receivedSize, totalSize in
+            debugPrint(receivedSize)
+        }) { result in
+            switch result {
+            case .success(let value):
+                completion(value.image, nil)
+            case .failure(let error):
+                completion(nil, error)
+            }
+        }
+    }
+
+}
+
+
+
+
+
+
+public let kMainQueue = DispatchQueue.main
+/// 主线程延迟执行回调
+/// - Parameters:
+///   - delay: 延迟时间(秒)
+///   - completion: 延迟后的回调
+public func kDelayOnMainThread(_ delay: TimeInterval, completion: @escaping () -> Void) {
+    DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
+        completion()
+    }
+}
+
+public func kDelayMainShort(completion: @escaping () -> Void) {
+    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
+        completion()
+    }
+}
+
+/// 在主线程上执行代码
+/// - Parameter block: 要执行的代码块
+public func kExecuteOnMainThread(_ block: @escaping () -> Void) {
+    if Thread.isMainThread {
+        block()
+    } else {
+        DispatchQueue.main.async {
+            block()
+        }
+    }
+}
+
+
+/// 子 size 根据容器 size,返回自己的最大 size
+/// - Parameters:
+///   - containerSize: 容器 Size
+///   - subviewSize: 子 Size
+///   - contentInset: contentInset
+///   - contentMode: contentMode
+/// - Returns: 最大 size
+public func kCalculateScaledSize(containerSize: CGSize, subviewSize: CGSize, contentInset: UIEdgeInsets = .zero ,contentMode:UIView.ContentMode) -> CGSize {
+    // 计算去除边距后的容器可用大小
+    let availableWidth = containerSize.width - contentInset.left - contentInset.right
+    let availableHeight = containerSize.height - contentInset.top - contentInset.bottom
+    
+    switch contentMode {
+    case .scaleToFill:
+        // 直接填充容器,忽略子视图比例
+        return CGSize(width: availableWidth, height: availableHeight)
+    case .scaleAspectFit:
+        // 按比例适配容器,确保子视图完全显示在容器内
+        let widthRatio = availableWidth / subviewSize.width
+        let heightRatio = availableHeight / subviewSize.height
+        let scale = min(widthRatio, heightRatio)
+        return CGSize(width: subviewSize.width * scale, height: subviewSize.height * scale)
+    case .scaleAspectFill:
+        // 按比例填充容器,可能会裁剪子视图
+        let widthRatio = availableWidth / subviewSize.width
+        let heightRatio = availableHeight / subviewSize.height
+        let scale = max(widthRatio, heightRatio)
+        return CGSize(width: subviewSize.width * scale, height: subviewSize.height * scale)
+    default:
+        // 默认返回子视图原始大小
+        return subviewSize
+    }
+}
+
+
+public func kGetCollectionViewCellSize(sectionInset:UIEdgeInsets,
+               itemSpacing:CGFloat,
+               originalSize:CGSize,
+               cellRowNum:Int)->CGSize{
+    let widthReduction:CGFloat = 1.0
+    let originalScale = originalSize.width/originalSize.height
+    var w = k_ScreenWidth-sectionInset.left-sectionInset.right-widthReduction
+    w = w - itemSpacing * CGFloat((cellRowNum-1))
+    w = w/CGFloat(cellRowNum)
+    let h = w/originalScale
+    let cellSize = CGSizeMake(w, h)
+    return cellSize
+}

+ 83 - 0
AIPlayRingtones/Classes/Tool/TSCommonTool/TSMultiTaskDownloader.swift

@@ -0,0 +1,83 @@
+//
+//  TSMultiTaskDownloader.swift
+//  Pods
+//
+//  Created by 100Years on 2025/3/17.
+//
+
+import Foundation
+
+//public class TSMultiTaskDownloader: NSObject, URLSessionDownloadDelegate {
+//    
+//    public static let shared = TSMultiTaskDownloader()
+//    
+//    private var session: URLSession!
+//    private var downloadTasks: [URLSessionDownloadTask] = []
+//    private var progressHandlers: [URL: (Double) -> Void] = [:]
+//    private var completionHandlers: [URL: (URL?, Error?) -> Void] = [:]
+//    
+//    override init() {
+//        super.init()
+//        let config = URLSessionConfiguration.background(withIdentifier: "com.yourapp.multitaskdownloader")
+//        session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
+//    }
+//    
+//    public func downloadFile(from url: URL, progressHandler: @escaping (Double) -> Void, completionHandler: @escaping (URL?, Error?) -> Void) {
+//        let downloadTask = session.downloadTask(with: url)
+//        downloadTasks.append(downloadTask)
+//        progressHandlers[url] = progressHandler
+//        completionHandlers[url] = completionHandler
+//        downloadTask.resume()
+//    }
+//    
+//    public func cancelDownload(for url: URL) {
+//        if let task = downloadTasks.first(where: { $0.originalRequest?.url == url }) {
+//            task.cancel()
+//            downloadTasks.removeAll { $0.originalRequest?.url == url }
+//            progressHandlers.removeValue(forKey: url)
+//            completionHandlers.removeValue(forKey: url)
+//        }
+//    }
+//    
+//    public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
+//        guard let originalURL = downloadTask.originalRequest?.url else { return }
+//        
+//        let fileManager = FileManager.default
+//        let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
+//        let destinationURL = documentsDirectory.appendingPathComponent(originalURL.lastPathComponent)
+//        
+//        do {
+//            try fileManager.moveItem(at: location, to: destinationURL)
+//            completionHandlers[originalURL]?(destinationURL, nil)
+//        } catch {
+//            completionHandlers[originalURL]?(nil, error)
+//        }
+//        
+//        downloadTasks.removeAll { $0.originalRequest?.url == originalURL }
+//        progressHandlers.removeValue(forKey: originalURL)
+//        completionHandlers.removeValue(forKey: originalURL)
+//    }
+//    
+//    public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
+//        guard let originalURL = downloadTask.originalRequest?.url else { return }
+//        
+//        let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
+//        progressHandlers[originalURL]?(progress)
+//    }
+//    
+//    public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
+//        guard let originalURL = task.originalRequest?.url else { return }
+//        
+//        if let error = error {
+//            completionHandlers[originalURL]?(nil, error)
+//        }
+//        
+//        downloadTasks.removeAll { $0.originalRequest?.url == originalURL }
+//        progressHandlers.removeValue(forKey: originalURL)
+//        completionHandlers.removeValue(forKey: originalURL)
+//    }
+//    
+//    deinit {
+//        session.invalidateAndCancel()
+//    }
+//}

+ 27 - 0
AIPlayRingtones/Classes/Tool/TSCommonTool/TSRandomTextTool.swift

@@ -0,0 +1,27 @@
+//
+//  TSRandomTextTool.swift
+//  Pods
+//
+//  Created by 100Years on 2025/5/15.
+//
+
+public class TSRandomTextTool {
+    private var texts: [String]
+    private var lastIndex: Int?
+
+    public init(texts: [String]) {
+        self.texts = texts
+    }
+    
+    public func getRandomText() -> String? {
+        guard !texts.isEmpty else { return nil }
+        
+        var randomIndex: Int
+        repeat {
+            randomIndex = Int.random(in: 0..<texts.count)
+        } while randomIndex == lastIndex
+        
+        lastIndex = randomIndex
+        return texts[randomIndex]
+    }
+}

+ 76 - 0
AIPlayRingtones/Classes/Tool/TSCustomAlertController.swift

@@ -0,0 +1,76 @@
+//
+//  TSCustomAlertController.swift
+//  Pods
+//
+//  Created by 100Years on 2025/3/26.
+//
+
+import UIKit
+
+
+public class TSCustomAlertController {
+    
+    // MARK: - 配置模型
+    public struct AlertConfig {
+        public var style: UIUserInterfaceStyle = .dark
+        public var message: String
+        public var messageColor: UIColor = .black
+        public var messageFont: UIFont = .systemFont(ofSize: 14)
+        
+        public var cancelTitle: String = "取消"
+        public var cancelColor: UIColor = .gray
+        
+        public var confirmTitle: String = "确定"
+        public var confirmColor: UIColor = .blue
+        
+        public var cancelAction: (() -> Void)?
+        public var confirmAction: (() -> Void)?
+        
+        public init(style:UIUserInterfaceStyle = .dark, message: String, messageColor: UIColor, messageFont: UIFont, cancelTitle: String, cancelColor: UIColor, confirmTitle: String, confirmColor: UIColor, cancelAction: ( () -> Void)? = nil, confirmAction: ( () -> Void)? = nil) {
+            self.style = style
+            self.message = message
+            self.messageColor = messageColor
+            self.messageFont = messageFont
+            self.cancelTitle = cancelTitle
+            self.cancelColor = cancelColor
+            self.confirmTitle = confirmTitle
+            self.confirmColor = confirmColor
+            self.cancelAction = cancelAction
+            self.confirmAction = confirmAction
+        }
+    }
+    
+    // MARK: - 显示弹窗
+    public static func show(in viewController: UIViewController, config: AlertConfig) {
+        // 1. 创建AlertController (title设为空字符串而不是nil,避免自动上移)
+        let alert = UIAlertController(title: "", message: "", preferredStyle: .alert)
+        
+        // 2. 自定义message
+        let messageAttributed = NSAttributedString(
+            string: config.message,
+            attributes: [
+                .foregroundColor: config.messageColor,
+                .font: config.messageFont
+            ]
+        )
+        alert.setValue(messageAttributed, forKey: "attributedMessage")
+        // 设置黑暗模式
+        alert.overrideUserInterfaceStyle = config.style
+        // 3. 添加取消按钮
+        let cancelAction = UIAlertAction(title: config.cancelTitle, style: .default) { _ in
+            config.cancelAction?()
+        }
+        cancelAction.setValue(config.cancelColor, forKey: "titleTextColor")
+        alert.addAction(cancelAction)
+        
+        // 4. 添加确定按钮
+        let confirmAction = UIAlertAction(title: config.confirmTitle, style: .default) { _ in
+            config.confirmAction?()
+        }
+        confirmAction.setValue(config.confirmColor, forKey: "titleTextColor")
+        alert.addAction(confirmAction)
+        
+        // 5. 显示弹窗
+        viewController.present(alert, animated: true)
+    }
+}

+ 111 - 0
AIPlayRingtones/Classes/Tool/TSDownloadManager.swift

@@ -0,0 +1,111 @@
+//
+//  TSPublicContent.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/20.
+//
+
+
+import AVFoundation
+import Alamofire
+
+class TSDownloadManager {
+    
+
+    static func downloadFile(
+        urlString: String,
+        fileEx:String? = nil,
+    missingEx:String? = nil,
+        progressHandler: ((Double) -> Void)? = nil,
+        completion: @escaping (URL?, Error?) -> Void
+    ) -> DownloadRequest? {
+        
+        if let fileName = TSBusinessFileManager.getLocalURL(urlString: urlString,fileEx:fileEx,missingEx:missingEx){
+            completion(fileName,nil)
+            return nil
+        }
+    
+        guard let cachesAllPath = TSBusinessFileManager.generateCachesURL(from: urlString, fileEx: fileEx, missingEx: missingEx,completion: { string, error in
+            completion(nil,error)
+        })else { return nil }
+        
+
+        return nil
+//        return TSNetworkShared.downloadFile(urlString: urlString,to: cachesAllPath, progressHandler:progressHandler,completion: completion)
+    }
+    
+//
+//    
+//    
+//    static func getDownLoadRing(ringModel:TSRingModel,complete:@escaping (URL?,Bool)->Void){
+//        let urlString = ringModel.audioUrl
+//        if let path = TSDownloadManager.getRingLocalURL(ringModel: ringModel) {
+//            complete(path,false)
+//        }else{
+//            _ = TSDownloadManager.downloadFile(urlString:urlString,missingEx: "mp3") { url, error in
+//                if let path = url {
+//                    complete(path,true)
+//                }else{
+//                    complete(nil,true)
+//                }
+//            }
+//        }
+//    }
+    
+//    static func getDownLoadVideo(urlString:String,complete:@escaping (URL?,Bool)->Void){
+//        if let path = TSDownloadManager.getRingLocalURL(urlString: urlString) {
+//            complete(path,false)
+//        }else{
+//            _ = TSDownloadManager.downloadFile(urlString:urlString,missingEx: "mp3") { url, error in
+//                if let path = url {
+//                    complete(path,true)
+//                }else{
+//                    complete(nil,true)
+//                }
+//            }
+//        }
+//    }
+
+}
+//
+//extension TSDownloadManager {
+//    
+//    
+//    //获取 TSRingModel 本地的缓存
+//    public static func getRingLocalURL(ringModel: TSRingModel)->URL?{
+//        if ringModel.documentPath.count == 0 {
+//            return nil
+//        }
+//        
+//        let documentPath = ringModel.documentPath.fillDocumentURL
+//        if TSFileManagerTool.fileExists(at: documentPath) {
+//            return documentPath
+//        }
+//        
+//        let urlString = ringModel.audioUrl
+//        guard let fileName = TSBusinessFileManager.getLocalURL(urlString: urlString,fileEx:nil,missingEx: "mp3") else{
+//            return nil
+//        }
+//        return fileName
+//    }
+//    
+//    //获取 urlstring 本地的缓存 url path
+//    public static func getRingLocalURL(urlString: String)->URL?{
+//        guard let fileName = TSBusinessFileManager.getLocalURL(urlString: urlString,fileEx:nil,missingEx: "mp3") else{
+//            return nil
+//        }
+//        return fileName
+//    }
+//
+//    //获取 urlstring 本地的缓存 url path
+//    public static func generateRingSaveLocalURL(name:String,timestamp:Bool = true,fileExtension:String = "mp3")->URL{
+//        var fileName = name
+//        if timestamp {
+//            fileName = fileName + String(Date.timestampInt)
+//        }
+//        
+//        fileName = fileName + "." + fileExtension
+//        return TSBusinessFileManager.saveRingPathURL.appendingPathComponent(fileName)
+//    }
+//    
+//}

+ 173 - 0
AIPlayRingtones/Classes/Tool/TSFileManagerTool.swift

@@ -0,0 +1,173 @@
+//
+//  TSFileManagerTool.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/26.
+//
+
+public class TSFileManagerTool {
+
+    /// 获取沙盒 Documents 目录路径
+    public static var documentsDirectory: URL {
+        return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
+    }
+
+    /// 获取沙盒 Cache 目录路径
+    public static var cacheDirectory: URL {
+        return FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
+    }
+
+    /// 获取沙盒 Temporary 目录路径
+    public static var temporaryDirectory: URL {
+        return FileManager.default.temporaryDirectory
+    }
+
+    public  static func copyFileWithOverwrite(from sourceURL: URL, to targetURL: URL) {
+        let fileManager = FileManager.default
+        do {
+            removeItem(from: targetURL)
+            checkFolderAndCreate(from: targetURL)
+            try fileManager.copyItem(at: sourceURL, to: targetURL)
+            debugPrint("文件复制成功!")
+        } catch {
+            debugPrint("文件复制失败: \(error.localizedDescription)")
+        }
+    }
+    
+    public static func removeItem(from sourceURL: URL) {
+        let fileManager = FileManager.default
+        do {
+            // 如果目标路径存在同名文件,先删除旧文件
+            if fileManager.fileExists(atPath: sourceURL.path) {
+                try fileManager.removeItem(at: sourceURL)
+            }
+            debugPrint("文件删除成功!")
+        } catch {
+            debugPrint("文件删除失败: \(error.localizedDescription)")
+        }
+    }
+    
+    /// 移动文件的方法(自动创建目标文件夹)
+    /// - Parameters:
+    ///   - sourceURL: 文件的源 URL
+    ///   - destinationURL: 目标 URL
+    /// - Throws: 如果移动失败,会抛出错误
+    public static func moveFile(from sourceURL: URL, to destinationURL: URL) {
+        let fileManager = FileManager.default
+        
+        // 检查源文件是否存在
+        guard fileManager.fileExists(atPath: sourceURL.path) else {
+            let error = NSError(domain: "FileMoveError", code: 404, userInfo: [NSLocalizedDescriptionKey: "源文件不存在"])
+            debugPrint(error)
+            return
+        }
+        
+        // 获取目标文件夹的路径
+        let destinationDirectory = destinationURL.deletingLastPathComponent()
+        do {
+            // 如果目标文件夹不存在,创建文件夹
+            if !fileManager.fileExists(atPath: destinationDirectory.path) {
+                try fileManager.createDirectory(at: destinationDirectory, withIntermediateDirectories: true, attributes: nil)
+            }
+            
+            // 检查目标路径是否已经存在文件
+            if fileManager.fileExists(atPath: destinationURL.path) {
+                // 如果需要覆盖,可以选择先删除目标文件
+                try fileManager.removeItem(at: destinationURL)
+            }
+            
+            // 尝试移动文件
+            try fileManager.moveItem(at: sourceURL, to: destinationURL)
+        } catch {
+            debugPrint("尝试移动文件失败: \(error.localizedDescription)")
+        }
+    }
+    
+    public static func getFileName(from url: URL, includeExtension: Bool = true) -> String {
+        if includeExtension {
+            return url.lastPathComponent
+        } else {
+            return url.deletingPathExtension().lastPathComponent
+        }
+    }
+    
+    public static func checkFolderAndCreate(from destinationURL: URL){
+        let fileManager = FileManager.default
+        let destinationDirectory = destinationURL.deletingLastPathComponent()
+        // 如果目标文件夹不存在,创建文件夹
+        if !fileManager.fileExists(atPath: destinationDirectory.path) {
+            do {
+                try fileManager.createDirectory(at: destinationDirectory, withIntermediateDirectories: true, attributes: nil)
+            } catch {
+                debugPrint("尝试创建文件夹失败: \(error.localizedDescription)")
+            }
+        }
+    }
+
+    // MARK: - 文件操作方法
+
+    /// 检查文件或文件夹是否存在
+    public static func fileExists(at url: URL) -> Bool {
+        return FileManager.default.fileExists(atPath: url.path)
+    }
+
+    /// 创建文件夹
+    public static func createDirectory(at url: URL) throws {
+        if !fileExists(at: url) {
+            try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
+        }
+    }
+    
+    //获取缓存目录下文件夹路径
+    public static func getCacheSubPath(at url: URL) ->String? {
+        let array = url.path.components(separatedBy:"/Caches/")
+        let cashFilePath = array.last
+        return cashFilePath
+    }
+    
+}
+
+public extension String {
+    
+    public var isCachesPath:Bool{
+        return self.contains("/Caches/")
+    }
+    
+    public var fillCachePath:String{
+        return TSFileManagerTool.cacheDirectory.appendingPathComponent(self).path
+    }
+    
+    public var fillCacheURL:URL{
+        return TSFileManagerTool.cacheDirectory.appendingPathComponent(self)
+    }
+    
+    public var cachesLastURLString:String{
+        let parts = self.components(separatedBy: "/Caches/")
+        if let last = parts.last {
+            return last
+        }
+        return self
+    }
+}
+public extension String {
+    
+    public var isDocumentPath:Bool{
+        return self.contains("/Documents/")
+    }
+    
+    public var fillDocumentPath:String{
+        return TSFileManagerTool.documentsDirectory.appendingPathComponent(self).path
+    }
+    
+    public var fillDocumentURL:URL{
+        return TSFileManagerTool.documentsDirectory.appendingPathComponent(self)
+    }
+    
+    public var documentLastURLString:String{
+        let parts = self.components(separatedBy: "/Documents/")
+        if let last = parts.last {
+            return last
+        }
+        return self
+    }
+}

+ 50 - 0
AIPlayRingtones/Classes/Tool/TSGCDTimer.swift

@@ -0,0 +1,50 @@
+//
+//  GCDTimer.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/21.
+//
+
+import Foundation
+
+public class TSGCDTimer {
+    private var timer: DispatchSourceTimer?
+    private var isRunning: Bool = false
+
+    /// 启动定时器
+    /// - Parameters:
+    ///   - interval: 时间间隔(秒)
+    ///   - repeats: 是否重复
+    ///   - queue: 执行队列(默认主队列)
+    ///   - handler: 定时器触发时的回调
+    public func start(interval: TimeInterval, repeats: Bool = true, queue: DispatchQueue = .main, handler: @escaping () -> Void) {
+        // 如果定时器已经存在,先停止
+        stop()
+
+        // 创建定时器
+        timer = DispatchSource.makeTimerSource(queue: queue)
+        let deadline: DispatchTime = .now() + interval
+        let repeating: DispatchTimeInterval = repeats ? .seconds(Int(interval)) : .never
+
+        timer?.schedule(deadline: deadline, repeating: repeating)
+        timer?.setEventHandler(handler: handler)
+        timer?.resume()
+
+        isRunning = true
+    }
+
+    /// 停止定时器
+    public func stop() {
+        if isRunning {
+            timer?.cancel()
+            timer = nil
+            isRunning = false
+        }
+    }
+
+    /// 释放定时器
+    deinit {
+        stop()
+        dePrint("GCDTimer deinit")
+    }
+}

+ 190 - 0
AIPlayRingtones/Classes/Tool/TSImageCompress.swift

@@ -0,0 +1,190 @@
+//
+//  TSImageCompress.swift
+//  Pods
+//
+//  Created by 100Years on 2025/4/15.
+//
+
+import UIKit
+
+// MARK: - UIImage 扩展(Alpha 通道检测和缩放)
+public extension UIImage {
+    /// 检查图片是否有 Alpha 通道(透明度)
+    func hasAlphaChannel() -> Bool {
+        guard let cgImage = self.cgImage else { return false }
+        let alphaInfo = cgImage.alphaInfo
+        return alphaInfo == .first || alphaInfo == .last || alphaInfo == .premultipliedFirst || alphaInfo == .premultipliedLast
+    }
+    
+    /// 按比例缩放图片
+    func resized(withScaleFactor scaleFactor: CGFloat) -> UIImage {
+        let newSize = CGSize(width: size.width * scaleFactor, height: size.height * scaleFactor)
+        let renderer = UIGraphicsImageRenderer(size: newSize)
+        return renderer.image { _ in
+            self.draw(in: CGRect(origin: .zero, size: newSize))
+        }
+    }
+}
+
+public class TSImageCompress {
+
+    // MARK: - 核心压缩方法
+    public static func compressImageToTargetSize(
+        _ image: UIImage,
+        targetSizeKB: Int,
+        preserveTransparency: Bool
+    ) -> Data? {
+        let targetBytes = targetSizeKB * 1024
+        let hasAlpha = image.hasAlphaChannel()
+        let usePNG = preserveTransparency && hasAlpha
+
+        if usePNG {
+            let compressPNGImage = compressPNGImage(image, targetBytes: targetBytes)
+            if let compressedData = compressPNGImage {
+//                saveData(compressedData: compressedData)
+                print("PNG 压缩后大小: \(compressedData.count / 1024)KB")
+            }
+            return compressPNGImage
+        } else {
+            let compressJPEGImage = compressJPEGImage(image, targetBytes: targetBytes)
+            if let compressJPEGImage = compressJPEGImage {
+//                saveData(compressedData: compressJPEGImage)
+                print("JPEG 压缩后大小: \(compressJPEGImage.count / 1024)KB")
+            }
+            return compressJPEGImage
+        }
+    }
+
+    // MARK: - PNG 压缩逻辑(调整尺寸)
+    static private func compressPNGImage(_ image: UIImage, targetBytes: Int) -> Data? {
+        var currentImage = image
+        var currentData = currentImage.pngData()
+        var currentBytes = currentData?.count ?? 0
+        
+        // 如果已经满足目标大小,直接返回
+        guard currentBytes > targetBytes else { return currentData }
+        
+        var scaleFactor: CGFloat = 0.9
+        let minScaleFactor: CGFloat = 0.1
+        
+        while scaleFactor >= minScaleFactor {
+            let scaledImage = currentImage.resized(withScaleFactor: scaleFactor)
+            guard let newData = scaledImage.pngData() else { break }
+            let newBytes = newData.count
+            
+            if newBytes <= targetBytes {
+                return newData // 达到目标
+            }
+            
+            if newBytes >= currentBytes {
+                break // 无法继续优化
+            }
+            
+            currentImage = scaledImage
+            currentData = newData
+            currentBytes = newBytes
+            scaleFactor -= 0.1
+        }
+        
+        return currentBytes <= targetBytes ? currentData : nil
+    }
+
+    // MARK: - JPEG 压缩逻辑(先调质量,后调尺寸)
+    static private func compressJPEGImage(_ image: UIImage, targetBytes: Int) -> Data? {
+        var compressionQuality: CGFloat = 1.0
+        var currentImage = image
+        var currentData = currentImage.jpegData(compressionQuality: compressionQuality)
+        var currentBytes = currentData?.count ?? 0
+        
+        // 第一步:降低 JPEG 质量
+        while currentBytes > targetBytes && compressionQuality > 0.1 {
+            compressionQuality -= 0.1
+            currentData = currentImage.jpegData(compressionQuality: compressionQuality)
+            currentBytes = currentData?.count ?? 0
+        }
+        
+        guard currentBytes > targetBytes else { return currentData }
+        
+        // 第二步:缩小尺寸
+        var scaleFactor: CGFloat = 0.9
+        let minScaleFactor: CGFloat = 0.1
+        
+        while scaleFactor >= minScaleFactor {
+            let scaledImage = currentImage.resized(withScaleFactor: scaleFactor)
+            guard let newData = scaledImage.jpegData(compressionQuality: compressionQuality) else { break }
+            let newBytes = newData.count
+            
+            if newBytes <= targetBytes {
+                return newData // 达到目标
+            }
+            
+            if newBytes >= currentBytes {
+                break // 无法继续优化
+            }
+            
+            currentImage = scaledImage
+            currentData = newData
+            currentBytes = newBytes
+            scaleFactor -= 0.1
+        }
+        
+        return currentBytes <= targetBytes ? currentData : nil
+    }
+}
+
+
+extension TSImageCompress {
+    
+    static func saveData(compressedData:Data){
+        // 写入到沙盒的 "Images" 子目录
+          let fileName = "compressed_\(Date().timeIntervalSince1970).jpg"
+          if let fileURL = writeDataToSandbox(data: compressedData, fileName: fileName, subDirectory: "Images") {
+              print("文件已保存至: \(fileURL.path)")
+              
+              // 可选:读取文件验证
+              if let savedData = try? Data(contentsOf: fileURL) {
+                  print("读取文件大小: \(savedData.count / 1024)KB")
+              }
+          }
+    }
+    
+    static func getDocumentsDirectory() -> URL {
+        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
+        return paths[0] // 返回第一个路径(通常唯一)
+    }
+    
+    static func writeDataToSandbox(data: Data, fileName: String, subDirectory: String? = nil) -> URL? {
+        let baseDirectory: URL
+        
+        // 1. 确定基础目录(默认 Documents)
+        baseDirectory = getDocumentsDirectory()
+        
+        // 2. 拼接子目录(如果存在)
+        var targetDirectory = baseDirectory
+        if let subDir = subDirectory {
+            targetDirectory = baseDirectory.appendingPathComponent(subDir)
+        }
+        
+        // 3. 创建子目录(如果不存在)
+        do {
+            try FileManager.default.createDirectory(at: targetDirectory, withIntermediateDirectories: true, attributes: nil)
+        } catch {
+            print("创建目录失败: \(error.localizedDescription)")
+            return nil
+        }
+        
+        // 4. 拼接最终文件路径
+        let fileURL = targetDirectory.appendingPathComponent(fileName)
+        
+        // 5. 写入数据
+        do {
+            try data.write(to: fileURL)
+//            print("文件写入成功: \(fileURL.path)")
+            return fileURL
+        } catch {
+            print("写入文件失败: \(error.localizedDescription)")
+            return nil
+        }
+    }
+
+}

+ 40 - 0
AIPlayRingtones/Classes/Tool/TSNetworkTool.swift

@@ -0,0 +1,40 @@
+//
+//  TSNetworkTool.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2025/1/2.
+//
+
+import Alamofire
+import Network
+
+class TSNetworkTool {
+    static let shared = TSNetworkTool()
+    
+    func monitorNetworkPermission(escapable result: @escaping (Bool) -> Void) {
+        let monitor = NWPathMonitor()
+        let queue = DispatchQueue.global(qos: .background)
+        monitor.start(queue: queue)
+        monitor.pathUpdateHandler = { path in
+            DispatchQueue.main.async {
+                if path.status == .satisfied {
+                    debugPrint("网络可用,用户同意了权限")
+                    result(true)
+                } else {
+                    debugPrint("网络不可用,可能用户拒绝了权限")
+                    result(false)
+                }
+            }
+        }
+    }
+
+    func startListenNetStatus(handler: @escaping (NetworkReachabilityManager.NetworkReachabilityStatus, NetworkReachabilityManager?) -> Void) {
+        // 创建 NetworkReachabilityManager 实例
+        let networkManager = NetworkReachabilityManager.default
+
+        // 开始监听网络状态
+        networkManager?.startListening { status in
+            handler(status, networkManager)
+        }
+    }
+}

+ 107 - 0
AIPlayRingtones/Classes/Tool/TSToastTool.swift

@@ -0,0 +1,107 @@
+//
+//  TSToastTool.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+import SVProgressHUD
+
+public let TSToastShared = TSToastTool.shared
+public let TSToastWindow = TSToastTool.getCurrentWindow()
+
+open class TSToastTool {
+    public static let shared = TSToastTool()
+    
+    private init() {
+        SVProgressHUD.swizzleMethods()
+//        如果您想要堆叠 HUD,您可以使用以下方法平衡每个节目通话:
+//        [SVProgressHUD popActivity];
+        SVProgressHUD.setDefaultStyle(.dark)
+        SVProgressHUD.setDefaultAnimationType(.native)
+        SVProgressHUD.setDefaultMaskType(.clear)
+        SVProgressHUD.setMinimumSize(CGSize(width: 100, height: 100))
+        SVProgressHUD.setCornerRadius(16.0)
+        SVProgressHUD.setBackgroundColor("#000000".uiColor.withAlphaComponent(0.8))
+        SVProgressHUD.setForegroundColor(.white)
+        SVProgressHUD.setMinimumDismissTimeInterval(3.0)
+//        SVProgressHUD.setInfoImage(UIImage())
+//        SVProgressHUD.setImageViewSize(CGSize.zero)
+
+    }
+    
+    
+    public func setUIContent(font:UIFont? = nil,infoImage:UIImage? = nil) {
+        if let font = font {
+            SVProgressHUD.setFont(font)
+        }
+        
+        if let infoImage = infoImage {
+            SVProgressHUD.setInfoImage(infoImage)
+        }
+        
+    }
+
+    /// 显示文字提示
+    public func showToast(text: String?, duration: TimeInterval = 3.0,containerView:UIView? = nil) {
+        if let text = text {
+            if text.count == 0 {
+                return
+            }
+            kExecuteOnMainThread {
+                if let containerView = containerView {
+                    SVProgressHUD.setContainerView(containerView)
+                }else if let window = Self.getCurrentWindow() {
+                    SVProgressHUD.setContainerView(window)
+                }
+                SVProgressHUD.setDefaultMaskType(.none)
+                SVProgressHUD.showInfo(withStatus: text)
+            }
+        }
+    }
+    
+
+    /// 显示加载动画
+    public func showLoading(text:String? = nil,containerView:UIView?) {
+        kExecuteOnMainThread {
+            SVProgressHUD.setDefaultMaskType(.clear)
+            SVProgressHUD.setContainerView(containerView)
+            SVProgressHUD.show(withStatus: text)
+        }
+    }
+
+    /// 隐藏加载动画
+    public func hideLoading() {
+        kExecuteOnMainThread {
+            SVProgressHUD.dismiss()
+        }
+    }
+    
+    /// 显示进度提示
+    public func showProgress(progress:Float, status: String?,containerView:UIView?) {
+        kExecuteOnMainThread {
+            SVProgressHUD.setDefaultMaskType(.clear)
+            SVProgressHUD.setContainerView(containerView)
+            SVProgressHUD.showProgress(progress, status: status)
+        }
+    }
+    
+    /// 获取当前窗口,兼容 iOS 13 及以上版本
+    static func getCurrentWindow() -> UIWindow? {
+        if #available(iOS 13.0, *) {
+            // iOS 13 及以上使用 `scene` 获取当前 window
+            guard let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene else {
+                return UIApplication.shared.keyWindow
+            }
+            return scene.windows.first { $0.isKeyWindow }
+        } else {
+            // iOS 12 及以下直接从 keyWindow 获取
+            return UIApplication.shared.keyWindow
+        }
+    }
+}
+
+
+public func kShowToastDataMissing(){
+    TSToastShared.showToast(text: "Data missing")
+}

+ 78 - 0
AIPlayRingtones/Classes/Tool/WindowHelper.swift

@@ -0,0 +1,78 @@
+//
+//  WindowHelper.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2024/12/20.
+//
+
+import UIKit
+
+open class WindowHelper {
+    
+    /// 获取当前的 keyWindow
+    public static func getKeyWindow() -> UIWindow? {
+        // 在 iOS 13 及以上,SceneDelegate 管理窗口
+        if #available(iOS 13.0, *) {
+            return UIApplication.shared.connectedScenes
+                .compactMap { $0 as? UIWindowScene }
+                .flatMap { $0.windows }
+                .first { $0.isKeyWindow }
+        } else {
+            // iOS 13 以下直接获取 keyWindow
+            return UIApplication.shared.keyWindow
+        }
+    }
+    
+    /// 获取当前窗口,兼容 iOS 13 及以上版本
+    public static func getCurrentWindow() -> UIWindow? {
+        if #available(iOS 13.0, *) {
+            // iOS 13 及以上使用 `scene` 获取当前 window
+            guard let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene else {
+                return nil
+            }
+            return scene.windows.first { $0.isKeyWindow }
+        } else {
+            // iOS 12 及以下直接从 keyWindow 获取
+            return UIApplication.shared.keyWindow
+        }
+    }
+    
+    /// 获取当前根视图控制器
+    public static func getRootViewController() -> UIViewController? {
+        guard let window = getCurrentWindow() else {
+            return nil
+        }
+        return window.rootViewController
+    }
+    
+    /// 获取当前的视图控制器
+    public static func getCurrentViewController() -> UIViewController? {
+        var currentViewController = getRootViewController()
+        
+        while let presentedViewController = currentViewController?.presentedViewController {
+            currentViewController = presentedViewController
+        }
+        
+        return currentViewController
+    }
+    
+    
+    public static func topViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
+        if let nav = base as? UINavigationController {
+            return topViewController(base: nav.visibleViewController)
+        } else if let tab = base as? UITabBarController {
+            return topViewController(base: tab.selectedViewController)
+        } else if let presented = base?.presentedViewController {
+            return topViewController(base: presented)
+        }
+        debugPrint("当前顶层 VC 是: \(base)")
+        return base
+    }
+}
+
+
+public func kGetScaleHeight(originalSize:CGSize,width:CGFloat) -> CGFloat {
+    let originalScale = originalSize.width/originalSize.height
+    let height = width/originalScale
+    return height
+}

+ 120 - 0
AIPlayRingtones/Classes/View/TSCircularProgressView.swift

@@ -0,0 +1,120 @@
+//
+//  TSCircularProgressView.swift
+//  Pods
+//
+//  Created by 100Years on 2025/3/21.
+//
+
+import UIKit
+
+open class TSCircularProgressView: UIView {
+    // MARK: - Properties
+    public var trackLayer: CAShapeLayer!
+    public var progressLayer: CAShapeLayer!
+    
+    /// 进度条颜色
+    public var progressColor: UIColor = .blue {
+        didSet {
+            progressLayer.strokeColor = progressColor.cgColor
+            //print("Progress Color Updated: \(progressColor)")
+        }
+    }
+    
+    /// 背景轨道颜色
+    public var trackColor: UIColor = .lightGray {
+        didSet {
+            trackLayer.strokeColor = trackColor.cgColor
+        }
+    }
+    
+    /// 线条粗细
+    public var lineWidth: CGFloat = 10.0 {
+        didSet {
+            trackLayer.lineWidth = lineWidth
+            progressLayer.lineWidth = lineWidth
+            setNeedsLayout()
+        }
+    }
+    
+    /// 进度(0.0 到 1.0)
+    public var progress: CGFloat = 0.0 {
+        didSet {
+            progress = min(max(progress, 0.0), 1.0) // 限制范围
+//            print("Progress Updated: \(progress)")
+            progressLayer.strokeEnd = progress
+        }
+    }
+    
+    /// 绘制方向(默认顺时针)
+    public var isClockwise: Bool = true {
+        didSet {
+            setNeedsLayout()
+        }
+    }
+    
+    // MARK: - Initialization
+    override public init(frame: CGRect) {
+        super.init(frame: frame)
+        setupLayers()
+    }
+    
+    required public init?(coder: NSCoder) {
+        super.init(coder: coder)
+        setupLayers()
+    }
+    
+    // MARK: - Setup Layers
+    public func setupLayers() {
+        // 背景轨道
+        trackLayer = CAShapeLayer()
+        trackLayer.fillColor = UIColor.clear.cgColor
+        trackLayer.strokeColor = trackColor.cgColor
+        trackLayer.lineWidth = lineWidth
+        trackLayer.lineCap = .round
+        layer.addSublayer(trackLayer)
+        
+        // 进度条
+        progressLayer = CAShapeLayer()
+        progressLayer.fillColor = UIColor.clear.cgColor
+        progressLayer.strokeColor = progressColor.cgColor
+        progressLayer.lineWidth = lineWidth
+        progressLayer.lineCap = .round
+        progressLayer.strokeEnd = progress
+        layer.addSublayer(progressLayer)
+        
+        //print("Progress Layer Added: \(progressLayer)")
+    }
+    
+    // MARK: - Layout
+    override public func layoutSubviews() {
+        super.layoutSubviews()
+        //print("layoutSubviews Called")
+        updatePaths()
+    }
+    
+    public func updatePaths() {
+        let center = CGPoint(x: bounds.midX, y: bounds.midY)
+        let radius = min(bounds.width, bounds.height) / 2 - lineWidth / 2
+        
+        // 背景轨道路径
+        let trackPath = UIBezierPath(
+            arcCenter: center,
+            radius: radius,
+            startAngle: -.pi / 2, // 从顶部开始
+            endAngle: .pi * 1.5,   // 到顶部结束
+            clockwise: true // 背景轨道始终为顺时针
+        )
+        trackLayer.path = trackPath.cgPath
+        
+        // 进度条路径
+        let progressPath = UIBezierPath(
+            arcCenter: center,
+            radius: radius,
+            startAngle: -.pi / 2, // 从顶部开始
+            endAngle: -.pi / 2 + .pi * 2 * (isClockwise ? 1 : -1), // 根据 isClockwise 控制方向
+            clockwise: isClockwise
+        )
+        progressLayer.path = progressPath.cgPath
+        //print("Progress Layer Path: \(progressLayer.path)")
+    }
+}

+ 194 - 0
AIPlayRingtones/Classes/View/TSPlaceholderTextView/TSPlaceholderTextView.swift

@@ -0,0 +1,194 @@
+//
+//  TSPlaceholderTextView.swift
+//  Pods
+//
+//  Created by 100Years on 2025/3/4.
+//
+
+open class TSPlaceholderTextView: UITextView {
+    
+//    // MARK: - 垂直对齐模式
+//    public enum VerticalAlignment {
+//        case top
+//        case center
+//        case bottom
+//    }
+//    
+//    public var verticalAlignment: VerticalAlignment = .top {
+//        didSet { setNeedsLayout() }
+//    }
+    
+    // Placeholder Label
+    public lazy var placeholderLabel: TopLeftLabel = {
+        let placeholderLabel = TopLeftLabel()
+        placeholderLabel.font = font
+        placeholderLabel.textColor = placeholderColor
+        placeholderLabel.text = placeholder
+        placeholderLabel.numberOfLines = 0
+        placeholderLabel.isUserInteractionEnabled = false
+        placeholderLabel.textAlignment = .left
+        return placeholderLabel
+    }()
+    
+    public var maxLength:Int = 1000 {
+        didSet {
+            handleTextViewTextCount(textView: self)
+        }
+    }
+    // Placeholder Text
+    public  var placeholder: String? {
+        didSet {
+            placeholderLabel.text = placeholder
+        }
+    }
+    
+    // Placeholder Color
+    public var placeholderColor: UIColor = .lightGray {
+        didSet {
+            placeholderLabel.textColor = placeholderColor
+        }
+    }
+    
+    // Placeholder Insets
+    public var textInsets: UIEdgeInsets = .zero {
+        didSet {
+            updateTextContainerInset()
+        }
+    }
+    
+    // Override text property to manage placeholder visibility
+    open override var text: String! {
+        didSet {
+            updatePlaceholderVisibility()
+        }
+    }
+    
+    public var textViewTextChanged:(()->Void)?
+    
+    // Initializer
+    public init(placeholder: String? = nil,
+         text: String? = nil,
+         font: UIFont? = nil,
+         textColor: UIColor = .black,
+         backgroundColor: UIColor = .white,
+         textInsets: UIEdgeInsets = .zero) {
+        super.init(frame: .zero, textContainer: nil)
+        
+        self.placeholder = placeholder
+        self.font = font
+        self.textColor = textColor
+        self.backgroundColor = backgroundColor
+        self.textInsets = textInsets
+        
+        setupPlaceholder()
+        self.text = text
+    }
+    
+    required public init?(coder: NSCoder) {
+        super.init(coder: coder)
+        setupPlaceholder()
+    }
+    
+    // Setup placeholder label
+    private func setupPlaceholder() {
+ 
+        addSubview(placeholderLabel)
+        updatePlaceholderVisibility()
+        
+        // Observe text changes to update placeholder
+        NotificationCenter.default.addObserver(self,
+                                               selector: #selector(textDidChange(_:)),
+                                               name: UITextView.textDidChangeNotification,
+                                               object: self)
+        
+        // Update text container insets
+        updateTextContainerInset()
+    }
+    
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+    }
+    
+    // Update placeholder visibility
+    private func updatePlaceholderVisibility() {
+        placeholderLabel.isHidden = !text.isEmpty
+    }
+    
+    
+    @objc private func textDidChange(_ notification: Notification) {
+        if let textView = notification.object as? UITextView {
+            handleTextViewTextCount(textView: textView)
+            textViewTextChanged?()
+        }
+        updatePlaceholderVisibility()
+    }
+    
+    open func handleTextViewTextCount(textView:UITextView) {
+        if textView.text.count > maxLength {
+            textView.text = String(textView.text.prefix(maxLength))
+        }
+    }
+    
+    // Update textContainerInset and placeholder frame
+    private func updateTextContainerInset() {
+        textContainerInset = textInsets
+        setNeedsLayout()
+    }
+
+    
+    // Layout placeholder
+    open override func layoutSubviews() {
+        super.layoutSubviews()
+
+  
+//        let textInsets = textCntainerInset
+        let placeholderX = textInsets.left + 5 // Add padding for placeholder
+        let placeholderY = textInsets.top
+        let placeholderWidth = bounds.width - textInsets.left - textInsets.right - 10 // Adjust for padding
+        let placeholderHeight = bounds.height - textInsets.top - textInsets.bottom
+        
+        placeholderLabel.frame = CGRect(x: placeholderX,
+                                        y: placeholderY,
+                                        width: placeholderWidth,
+                                        height: placeholderHeight)
+        
+//        handleVerticalAlignment()
+    }
+    
+    func handleVerticalAlignment() {
+      
+//          guard !bounds.isEmpty else { return }
+//          
+//        
+//        let label = UILabel()
+//        label.text = text
+//        label.font = font
+//        label.numberOfLines = 0
+//        var height = label.sizeThatFits(CGSize(width: bounds.width, height: .greatestFiniteMagnitude)).height
+//
+//        
+////          let size = sizeThatFits(CGSize(
+////              width: bounds.width,
+////              height: CGFloat.greatestFiniteMagnitude
+////          ))
+//          
+//          let topInset: CGFloat
+//          switch verticalAlignment {
+//          case .top:
+//              topInset = 0
+//          case .center:
+//              topInset = max(0, (bounds.height - height) / 2)
+//          case .bottom:
+//              topInset = max(0, bounds.height - height)
+//          }
+//          
+//          textContainerInset = UIEdgeInsets(
+//              top: topInset,
+//              left: textContainerInset.left,
+//              bottom: textContainerInset.bottom,
+//              right: textContainerInset.right
+//          )
+      }
+}
+
+

+ 68 - 0
AIPlayRingtones/Classes/View/TSProgressSlider/TSProgressSlider.swift

@@ -0,0 +1,68 @@
+//
+//  TSProgressSlider.swift
+//  Pods
+//
+//  Created by 100Years on 2025/4/22.
+//
+
+import UIKit
+
+open class TSProgressSlider: UISlider {
+    // 透明覆盖视图
+    private let overlayView = UIView()
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        setupOverlay()
+    }
+    
+    required public init?(coder: NSCoder) {
+        super.init(coder: coder)
+        setupOverlay()
+    }
+    
+    private func setupOverlay() {
+        // 配置透明覆盖视图
+        overlayView.backgroundColor = .clear
+        overlayView.isUserInteractionEnabled = true
+        addSubview(overlayView)
+        
+        // 添加拖动手势
+        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
+        overlayView.addGestureRecognizer(panGesture)
+        
+        // 添加点击手势
+        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
+        overlayView.addGestureRecognizer(tapGesture)
+    }
+    
+    open override func layoutSubviews() {
+        super.layoutSubviews()
+        // 确保覆盖视图与slider同大小
+        overlayView.frame = bounds
+    }
+    
+    // 处理拖动手势
+    @objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
+        let location = gesture.location(in: overlayView)
+        updateSliderValue(with: location)
+        
+        if gesture.state == .ended {
+            sendActions(for: .touchUpInside)
+        }
+    }
+    
+    // 处理点击手势
+    @objc private func handleTap(_ gesture: UITapGestureRecognizer) {
+        let location = gesture.location(in: overlayView)
+        updateSliderValue(with: location)
+        sendActions(for: .touchUpInside)
+    }
+    
+    // 更新滑块值
+    private func updateSliderValue(with location: CGPoint) {
+        let percentage = max(0, min(1, location.x / bounds.width))
+        setValue(Float(percentage), animated: false)
+        sendActions(for: .valueChanged)
+    }
+}

+ 201 - 0
AIPlayRingtones/Classes/View/TSReusableCollectionView/TSSimpleCollectionView.swift

@@ -0,0 +1,201 @@
+//
+//  TSReusableCollectionView.swift
+//  Pods
+//
+//  Created by 100Years on 2025/4/25.
+//
+import UIKit
+
+// 单元格配置协议
+public protocol TSSimpleConfigurableView {
+    var data:Any? { get set }
+    var delegate:TSSimpleCollectionViewDelegate? { get set }
+    var indexPath:IndexPath { get set }
+}
+
+public extension TSSimpleConfigurableView {
+    func actionCustom(custom:String){
+        delegate?.collectionView(didTrigger: TSSimpleCellEvent(action: .custom(custom), indexPath: indexPath, data: data))
+    }
+}
+
+// 单元格事件类型
+public enum TSSimpleCellAction {
+    case tap
+    case buttonTapped(String) // 支持自定义按钮标识
+    case custom(String)
+}
+
+// 回调参数结构体
+public struct TSSimpleCellEvent {
+    public let action: TSSimpleCellAction
+    public let indexPath: IndexPath
+    public let data: Any?
+    
+    public init(action: TSSimpleCellAction, indexPath: IndexPath, data: Any) {
+        self.action = action
+        self.indexPath = indexPath
+        self.data = data
+    }
+}
+
+// 集合视图回调协议
+public protocol TSSimpleCollectionViewDelegate: AnyObject {
+    func collectionView(didTrigger event: TSSimpleCellEvent)
+}
+
+
+public class TSSimpleSectionData {
+    public var title: String = ""
+    public var items: [Any] = []
+    
+    public init(
+        title:String = "",
+        items:[Any] = []
+    ) {
+        self.title = title
+        self.items = items
+    }
+}
+
+open class TSSimpleCollectionView: UIView {
+    
+    public lazy var layout: UICollectionViewFlowLayout = {
+        let layout = UICollectionViewFlowLayout()
+        layout.scrollDirection = .vertical
+        return layout
+    }()
+    
+    // MARK: - Properties
+    public  lazy var collectionView: UICollectionView = {
+        let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
+        // 禁用自动 contentInset 调整
+        if #available(iOS 11.0, *) {
+            cv.contentInsetAdjustmentBehavior = .never
+        }
+        cv.backgroundColor = .clear
+        cv.delegate = self
+        cv.dataSource = self
+        cv.showsHorizontalScrollIndicator = false
+        cv.showsVerticalScrollIndicator = false
+        return cv
+    }()
+    
+    public var sections: [TSSimpleSectionData] = []
+    public weak var delegate: TSSimpleCollectionViewDelegate?
+    
+    public var cellTypes: [String: (UIView & TSSimpleConfigurableView).Type] = [:]
+    public var cellIdentifierForItem: ((Any) -> String)?
+    
+    public var headerTypes: [String: (UIView & TSSimpleConfigurableView).Type] = [:]
+    public var headerIdentifierForItem: ((Any) -> String)?
+    
+    public var bottomTypes: [String: (UIView & TSSimpleConfigurableView).Type] = [:]
+    public var bottomIdentifierForItem: ((Any) -> String)?
+    
+    // MARK: - Initialization
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        setupViews()
+    }
+    
+    required public init?(coder: NSCoder) {
+        super.init(coder: coder)
+        setupViews()
+    }
+    
+    private func setupViews() {
+        addSubview(collectionView)
+        collectionView.translatesAutoresizingMaskIntoConstraints = false
+        NSLayoutConstraint.activate([
+            collectionView.topAnchor.constraint(equalTo: topAnchor),
+            collectionView.leadingAnchor.constraint(equalTo: leadingAnchor),
+            collectionView.trailingAnchor.constraint(equalTo: trailingAnchor),
+            collectionView.bottomAnchor.constraint(equalTo: bottomAnchor)
+        ])
+    }
+    
+    // MARK: - Public Methods
+    public func registerCell<Cell: UIView & TSSimpleConfigurableView>(_ cellType: Cell.Type, identifier: String) {
+        cellTypes[identifier] = cellType
+        collectionView.register(cellType, forCellWithReuseIdentifier: identifier)
+    }
+    
+    public func registerSectionHeader<Cell: UIView & TSSimpleConfigurableView>(_ cellType: Cell.Type, identifier: String) {
+        headerTypes[identifier] = cellType
+        collectionView.register(cellType, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: identifier)
+    }
+    
+    public func registerSectionFooter<Cell: UIView & TSSimpleConfigurableView>(_ cellType: Cell.Type, identifier: String) {
+        bottomTypes[identifier] = cellType
+        collectionView.register(cellType, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: identifier)
+    }
+    
+    public func reload(with sections: [TSSimpleSectionData]) {
+        self.sections = sections
+        collectionView.reloadData()
+    }
+    
+    public func reloadItem(at indexPath: IndexPath) {
+        collectionView.reloadItems(at: [indexPath])
+    }
+    
+    public func reloadSection(_ section: Int) {
+        collectionView.reloadSections(IndexSet(integer: section))
+    }
+}
+
+// MARK: - UICollectionViewDataSource & Delegate
+extension TSSimpleCollectionView: UICollectionViewDataSource, UICollectionViewDelegate {
+    public func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return sections.count
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return sections[section].items.count
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        let data = sections[indexPath.section].items[indexPath.item]
+        let identifier = cellIdentifierForItem?(data) ?? String(describing: type(of: data))
+        
+        guard let cellType = cellTypes[identifier] else {
+            fatalError("未注册对应数据类型的单元格: \(identifier)")
+        }
+        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
+       
+        if var configurableCell = cell as? (UICollectionViewCell & TSSimpleConfigurableView) {
+            
+            configurableCell.data = data
+            weak var delegate: TSSimpleCollectionViewDelegate? = delegate
+            configurableCell.delegate = delegate
+            configurableCell.indexPath = indexPath
+        }
+        
+        return cell
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        let data = sections[indexPath.section].items[indexPath.item]
+        let event = TSSimpleCellEvent(action: .tap, indexPath: indexPath, data: data)
+        delegate?.collectionView(didTrigger: event)
+    }
+    
+    
+    public func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
+        let data = sections[indexPath.section]
+        let identifier = headerIdentifierForItem?(data) ?? String(describing: type(of: data))
+        
+        guard let headerType = headerTypes[identifier] else {
+            fatalError("未注册对应数据类型的区间头尾: \(identifier)")
+        }
+        
+        let reusableView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: identifier, for: indexPath)
+        if var configurableCell = reusableView as? TSSimpleConfigurableView {
+            configurableCell.data = data
+            configurableCell.delegate = delegate
+            configurableCell.indexPath = indexPath
+        }
+        return reusableView
+    }
+}

+ 127 - 0
AIPlayRingtones/Classes/View/TSSaveSuccessTool/TSSaveSuccessTool.swift

@@ -0,0 +1,127 @@
+//
+//  TSSaveSuccessTool.swift
+//  Pods
+//
+//  Created by 100Years on 2025/4/24.
+//
+
+public let kSaveSuccesswShared = TSSaveSuccessTool.shared
+open class TSSaveSuccessTool {
+    
+    static let shared = TSSaveSuccessTool()
+    
+    public var clickViewHandle:(()->Void)?
+    
+    private lazy var textLabel:UILabel = {
+        let textLabel = UILabel()
+        textLabel.textColor = UIColor.white
+        textLabel.text = "Save Successfully".localized
+        textLabel.font = UIFont.font(size: 14)
+        return textLabel
+    }()
+    
+    private lazy var saveSuccessBg: UIView = {
+        return creatSaveSuccessBg()
+    }()
+    
+    
+    private lazy var viewButton:UIView = {
+        let color = "4FEA9D".uiColor
+        let viewButton = UIButton.createButton(title: "View".localized ,backgroundColor: color.withAlphaComponent(0.1),font: UIFont.font(size: 12),titleColor: color,corner: 14) { [weak self]  in
+            guard let self = self else { return }
+            if let clickViewHandle = clickViewHandle {
+                clickViewHandle()
+            }else {
+                if let url = URL(string: "photos-redirect://") {
+                    if UIApplication.shared.canOpenURL(url) {
+                        UIApplication.shared.open(url, options: [:], completionHandler: nil)
+                    }
+                }
+            }
+            
+            DispatchQueue.main.async{
+                self.saveSuccessBg.removeFromSuperview()
+            }
+        }
+        viewButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
+        return viewButton
+    }()
+    
+    func creatSaveSuccessBg() -> UIView {
+        let view = UIView()
+        view.frame = CGRect(x: 0, y: 0, width: 288, height: 48)
+        // 阴影
+        view.backgroundColor = .clear
+        view.layer.shadowColor = UIColor.black.cgColor
+        view.layer.shadowOffset = CGSize(width: 0, height: 2)
+        view.layer.shadowOpacity = 0.1
+        
+        // 圆角
+        let colorBg = UIView()
+        colorBg.backgroundColor = "#333333".uiColor
+        colorBg.layer.cornerRadius = 8
+        colorBg.layer.masksToBounds = true
+        colorBg.clipsToBounds = true
+        
+        view.addSubview(colorBg)
+        colorBg.snp.makeConstraints { make in
+            make.leading.trailing.top.bottom.equalTo(0)
+        }
+        
+        let image = UIImage(named: "success_icon")
+        let iconView = UIImageView(image: image)
+        view.addSubview(iconView)
+        iconView.snp.makeConstraints { make in
+            make.width.height.equalTo(24)
+            make.centerY.equalToSuperview()
+            make.leading.equalTo(12)
+        }
+    
+        view.addSubview(viewButton)
+        view.addSubview(textLabel)
+        viewButton.snp.makeConstraints { make in
+            make.width.equalTo(viewButton.intrinsicContentSize.width)
+            make.height.equalTo(28)
+            make.trailing.equalTo(-8)
+            make.centerY.equalToSuperview()
+        }
+    
+        textLabel.snp.makeConstraints { make in
+            make.leading.equalTo(iconView.snp.trailing).offset(8)
+            make.trailing.equalTo(viewButton.snp.leading).offset(-8)
+            make.centerY.equalToSuperview()
+        }
+
+        return view
+    }
+
+    
+    
+    public func getBottom(topY:CGFloat)->CGFloat{
+        let bottom = -(k_ScreenHeight - 48 - topY)
+        debugPrint("bottom=\(bottom)")
+        return bottom
+    }
+    
+    public func show(atView:UIView,text:String = "Save Successfully".localized,deadline:Double = 2.0,bottom:CGFloat = -112,showViewBtn:Bool = true,clickViewHandle:(()->Void)? = nil) {
+        self.clickViewHandle = clickViewHandle
+        kExecuteOnMainThread {
+            self.textLabel.text = text
+            self.viewButton.isHidden = !showViewBtn
+            atView.addSubview(self.saveSuccessBg)
+            self.saveSuccessBg.snp.remakeConstraints { make in
+                make.width.greaterThanOrEqualTo(288)
+                make.width.lessThanOrEqualTo(k_ScreenWidth-32)
+                make.height.equalTo(48)
+                make.centerX.equalToSuperview()
+                make.bottom.equalTo(bottom)
+            }
+        }
+
+        DispatchQueue.main.asyncAfter(deadline: .now() + deadline) {
+            self.saveSuccessBg.removeFromSuperview()
+        }
+    }
+    
+    
+}

+ 310 - 0
AIPlayRingtones/Classes/View/UICollectionView+Component/CollectionViewComponent.swift

@@ -0,0 +1,310 @@
+//
+//  TSCollectionViewComponent.swift
+//  ClockWidget
+//
+//  Created by TSYH on 2023/7/18.
+//
+
+import UIKit
+
+
+
+public let kIndexPath = "kIndexPath"
+
+open class TSCollectionView: UICollectionView {
+    lazy var reuseSubviews: [String: UIView] = [:]
+    
+    func cacheReuseView(_ view: UIView, reuseId: String?) {
+        guard let reuseId = reuseId, !reuseId.isEmpty else {
+            return
+        }
+        reuseSubviews[reuseId] = view
+    }
+    
+    func dequeueReuseSubviews(for reuseId: String?) -> UIView? {
+        guard let reuseId = reuseId, !reuseId.isEmpty else {
+            return nil
+        }
+        return reuseSubviews[reuseId]
+    }
+    
+    func clearReuseSubviews() {
+        reuseSubviews.removeAll()
+    }
+    
+    public var isCanGestureRecognizer:Bool = true
+    open override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
+        return isCanGestureRecognizer
+    }
+}
+
+open class TSCollectionViewComponent: NSObject {
+    
+    open var sectionActionHandler: ((Any?, IndexPath) -> Void)?
+
+    open var itemDidSelectedHandler: ((Any?, IndexPath) -> Void)?
+    open var itemActionHandler: ((Any?, IndexPath) -> Void)?
+    
+    open var itemWillDisplayHandler: ((UICollectionViewCell, Any?, IndexPath) -> Void)?
+    open var itemDidEndDisplayingHandler: ((UICollectionViewCell, Any?, IndexPath) -> Void)?
+    
+    open var didScrollHandler: ((UIScrollView) -> Void)?
+    open var willBeginDraggingHandler: ((UIScrollView) -> Void)?
+    open var didEndDraggingHandler: ((UIScrollView, Bool) -> Void)?
+    open var didScrollToTopHandler: ((UIScrollView) -> Void)?
+    open var willEndDraggingWithVelocityHandler: ((UIScrollView, CGPoint) -> Void)?
+    open var didEndDeceleratingHandler: ((UIScrollView) -> Void)?
+    
+    open var collectionView: TSCollectionView!
+    open var attributes: [String: Any]?
+    
+    
+    
+    
+    
+    public static let defaultCellID: String = "Component_Default_Cell_ID"
+    public init(frame: CGRect, layout: UICollectionViewFlowLayout? = nil, attributes: [String: Any]? = nil) {
+        super.init()
+        
+        var clLayout = UICollectionViewFlowLayout()
+        if let direction = attributes?["direction"] as? UICollectionView.ScrollDirection {
+            clLayout.scrollDirection = direction
+        }
+        
+        if let layout = layout {
+            clLayout = layout
+        }
+        collectionView = TSCollectionView(frame: frame, collectionViewLayout: clLayout)
+        collectionView.delegate = self
+        collectionView.dataSource = self
+        collectionView.contentInsetAdjustmentBehavior = .never
+        collectionView.showsVerticalScrollIndicator = false
+        collectionView.showsHorizontalScrollIndicator = false
+        collectionView.backgroundColor = .clear
+        
+        if #available(iOS 11.0, *) {
+            collectionView.contentInsetAdjustmentBehavior = .never// 关闭自动调整
+        }
+        
+        self.attributes = attributes
+        if let showScrollIndicator = attributes?["showScrollIndicator"] as? Bool {
+            collectionView.showsHorizontalScrollIndicator = showScrollIndicator
+        }
+        
+        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: Self.defaultCellID)
+    }
+    
+    open lazy var dataSource = [TSComponent]()
+    public func clear() {
+        dataSource = []
+    }
+    
+    public func reloadView(with components: [TSComponent]) {
+        registerClass(components: components)
+        dataSource.append(contentsOf: components)
+        collectionView.clearReuseSubviews()
+        collectionView.reloadData()
+    }
+    
+    public func reloadData() {
+        collectionView.clearReuseSubviews()
+        collectionView.reloadData()
+    }
+    
+    // 注册cell
+    private func registerClass(components: [TSComponent]) {
+        for cp in components {
+            if let sectionCp = cp as? TSCollectionViewSectionComponent {
+                if let header = sectionCp.headerComponent {
+                    collectionView.register(header.viewClass, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: header.reuseIdentifier)
+                }
+                if let footer = sectionCp.footerComponent {
+                    collectionView.register(footer.viewClass, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: footer.reuseIdentifier)
+                }
+                for cellCp in sectionCp.cells {
+                    collectionView.register(cellCp.cellClass, forCellWithReuseIdentifier: cellCp.cellClass.description())
+                }
+            } else if let cellCp = cp as? TSCollectionViewCellComponent {
+                collectionView.register(cellCp.cellClass, forCellWithReuseIdentifier: cellCp.cellClass.description())
+            }
+        }
+    }
+}
+
+extension TSCollectionViewComponent {
+    private var isSectionFramework: Bool {
+        if dataSource.first is TSCollectionViewSectionComponent {
+            return true
+        }
+        return false
+    }
+    
+    public func cellComponent(at indexPath: IndexPath) -> TSCollectionViewCellComponent? {
+        var cellCp: TSComponent?
+        if isSectionFramework {
+            if indexPath.section < dataSource.count,
+                let sectionCp = dataSource[indexPath.section] as? TSCollectionViewSectionComponent,
+                indexPath.item < sectionCp.cells.count {
+                cellCp = sectionCp.cells[indexPath.item]
+            }
+        } else if indexPath.item < dataSource.count {
+            cellCp = dataSource[indexPath.item]
+        }
+        if let cellCp = cellCp as? TSCollectionViewCellComponent {
+            return cellCp
+        }
+        return nil
+    }
+}
+
+extension TSCollectionViewComponent: UICollectionViewDataSource {
+    
+    public func numberOfSections(in collectionView: UICollectionView) -> Int {
+        if isSectionFramework {
+            return dataSource.count
+        }
+        return 1
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        if isSectionFramework {
+            if section < dataSource.count {
+                return (dataSource[section] as? TSCollectionViewSectionComponent)?.cells.count ?? 0
+            }
+            return 0
+        }
+        return dataSource.count
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
+        itemWillDisplayHandler?(cell, cellComponent(at: indexPath), indexPath)
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
+        itemDidEndDisplayingHandler?(cell, cellComponent(at: indexPath), indexPath)
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        guard let cellCp = cellComponent(at: indexPath) else {
+            return collectionView.dequeueReusableCell(withReuseIdentifier: Self.defaultCellID, for: indexPath)
+        }
+        let cellClass = cellCp.cellClass
+        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellClass.description(), for: indexPath)
+        if let cell = cell as? TSComponentView {
+            var atts = attributes ?? [:]
+            atts[kIndexPath] = indexPath
+            cell.renderView(with: cellCp, component: self, attributes: atts)
+        }
+        return cell
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
+        guard let sectionCp = dataSource[indexPath.section] as? TSCollectionViewSectionComponent else {
+            return UICollectionReusableView()
+        }
+        if kind == UICollectionView.elementKindSectionHeader {
+            if let headerCp = sectionCp.headerComponent {
+                let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerCp.reuseIdentifier, for: indexPath)
+                if let headerView = header as? TSComponentView {
+                    var atts = attributes ?? [:]
+                    atts[kIndexPath] = indexPath
+                    headerView.renderView(with: headerCp, component: self, attributes: atts)
+                }
+                return header
+            }
+        } else if kind == UICollectionView.elementKindSectionFooter {
+            if let footerCp = sectionCp.headerComponent {
+                let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: footerCp.reuseIdentifier, for: indexPath)
+                if let footerView = footer as? TSComponentView {
+                    var atts = attributes ?? [:]
+                    atts[kIndexPath] = indexPath
+                    footerView.renderView(with: footerCp, component: self, attributes: atts)
+                }
+                return footer
+            }
+        }
+        return UICollectionReusableView()
+    }
+}
+
+extension TSCollectionViewComponent: UICollectionViewDelegateFlowLayout {
+    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+        guard let cellCp = cellComponent(at: indexPath) else {
+            return .zero
+        }
+        return cellCp.cellSize(with: attributes)
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
+        if isSectionFramework {
+            if section < dataSource.count {
+                return (dataSource[section] as? TSCollectionViewSectionComponent)?.sectionInset ?? .zero
+            }
+        }
+        return UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16)
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
+        if section < dataSource.count {
+            return (dataSource[section] as? TSCollectionViewSectionComponent)?.lineSpacing ?? 12.0
+        }
+        return 12.0
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
+        if section < dataSource.count {
+            return (dataSource[section] as? TSCollectionViewSectionComponent)?.itemSpacing ?? 12.0
+        }
+        return 12.0
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
+        if isSectionFramework {
+            if section < dataSource.count,
+                let sectionCp = dataSource[section] as? TSCollectionViewSectionComponent {
+                return sectionCp.footerComponent?.viewSize ?? .zero
+            }
+        }
+        return .zero
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
+        if isSectionFramework {
+            if section < dataSource.count,
+                let sectionCp = dataSource[section] as? TSCollectionViewSectionComponent {
+                return sectionCp.headerComponent?.viewSize ?? .zero
+            }
+        }
+        return .zero
+    }
+}
+
+extension TSCollectionViewComponent: UICollectionViewDelegate {
+    public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        itemDidSelectedHandler?(cellComponent(at: indexPath), indexPath)
+    }
+    
+    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
+        didScrollHandler?(scrollView)
+    }
+    
+    public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
+        willEndDraggingWithVelocityHandler?(scrollView, velocity)
+    }
+    
+    public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
+        willBeginDraggingHandler?(scrollView)
+    }
+    
+    public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
+        didEndDraggingHandler?(scrollView, decelerate)
+    }
+    
+    public func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
+        didScrollToTopHandler?(scrollView)
+    }
+    
+    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
+        didEndDeceleratingHandler?(scrollView)
+    }
+}

+ 63 - 0
AIPlayRingtones/Classes/View/UICollectionView+Component/CommonSectionComponent.swift

@@ -0,0 +1,63 @@
+//
+//  CommonSectionComponent.swift
+//  ClockWidget
+//
+//  Created by TSYH on 2023/10/17.
+//
+
+import UIKit
+
+open class CommonSectionComponent: TSCollectionViewSectionComponent {
+    open var cellComponents: [TSCollectionViewCellComponent]
+    open var header: TSCollectionViewReuseViewComponent?
+    open var footer: TSCollectionViewReuseViewComponent?
+    open var inset: UIEdgeInsets
+    open var rowSpacing: CGFloat
+    open var columnSpacing: CGFloat
+    
+    public init(cellComponents: [TSCollectionViewCellComponent],
+         header: TSCollectionViewReuseViewComponent? = nil,
+         footer: TSCollectionViewReuseViewComponent? = nil,
+         inset: UIEdgeInsets = .zero,
+         rowSpacing: CGFloat = 10.0,
+         columnSpacing: CGFloat = 10.0) {
+        self.cellComponents = cellComponents
+        self.header = header
+        self.footer = footer
+        self.inset = inset
+        self.rowSpacing = rowSpacing
+        self.columnSpacing = columnSpacing
+    }
+    
+    open var cells: [TSCollectionViewCellComponent] {
+        return cellComponents
+    }
+    
+    open var headerComponent: TSCollectionViewReuseViewComponent? {
+        return header
+    }
+    
+    open var footerComponent: TSCollectionViewReuseViewComponent? {
+        return footer
+    }
+    
+    open func clear() {
+        cellComponents.removeAll()
+    }
+    
+    open func append(_ cellComponents: [TSCollectionViewCellComponent]) {
+        self.cellComponents.append(contentsOf: cellComponents)
+    }
+   
+    open var sectionInset: UIEdgeInsets {
+        return inset
+    }
+    
+    open var lineSpacing: CGFloat {
+        return rowSpacing
+    }
+    
+    open var itemSpacing: CGFloat {
+        return columnSpacing
+    }
+}

+ 64 - 0
AIPlayRingtones/Classes/View/UICollectionView+Component/Component.swift

@@ -0,0 +1,64 @@
+//
+//  TSComponent.swift
+//  ClockWidget
+//
+//  Created by TSYH on 2023/10/7.
+//
+
+import UIKit
+
+public protocol TSComponent {}
+
+public protocol TSComponentView {
+    // UI 渲染
+    func renderView(with object: Any?, component: TSCollectionViewComponent, attributes: [String: Any]?)
+}
+
+public protocol TSCollectionViewSectionComponent: TSComponent {
+    var cells: [TSCollectionViewCellComponent] { get }
+    var headerComponent: TSCollectionViewReuseViewComponent? { get }
+    var footerComponent: TSCollectionViewReuseViewComponent? { get }
+    var sectionInset: UIEdgeInsets { get }
+    var lineSpacing: CGFloat { get }
+    var itemSpacing: CGFloat { get }
+}
+
+public extension TSCollectionViewSectionComponent {
+    public var headerComponent: TSCollectionViewReuseViewComponent? { return nil }
+    public var footerComponent: TSCollectionViewReuseViewComponent? { return nil }
+    
+    public var sectionInset: UIEdgeInsets {
+        return UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16)
+    }
+    
+    public var lineSpacing: CGFloat {
+        return 12.0
+    }
+    
+    public var itemSpacing: CGFloat {
+        return 12.0
+    }
+}
+
+public enum TSCollectionViewReuseViewType {
+    case header
+    case footer
+}
+
+public protocol TSCollectionViewReuseViewComponent: TSComponent {
+    var viewClass: UICollectionReusableView.Type { get }
+    var viewType: TSCollectionViewReuseViewType { get }
+    var viewSize: CGSize { get }
+    var reuseIdentifier: String { get }
+}
+
+public extension TSCollectionViewReuseViewComponent {
+    var reuseIdentifier: String{
+        return viewClass.description()
+    }
+}
+
+public protocol TSCollectionViewCellComponent: TSComponent {
+    var cellClass: UICollectionViewCell.Type { get }
+    func cellSize(with attrubites: [String: Any]?) -> CGSize
+}

+ 66 - 0
AIPlayRingtones/Classes/View/UILabel/PaddedLabel.swift

@@ -0,0 +1,66 @@
+//
+//  Untitled.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/1/17.
+//
+
+import UIKit
+open class TopLeftLabel: UILabel {
+    
+    // 设置内间距
+    var textInsets: UIEdgeInsets = .zero {
+        didSet {
+            setNeedsDisplay()
+        }
+    }
+    
+    open override func drawText(in rect: CGRect) {
+        // 根据内间距调整绘制区域
+        let insetRect = rect.inset(by: textInsets)
+        
+        // 获取文本实际大小
+        guard let text = text else { return }
+        let textSize = (text as NSString).boundingRect(
+            with: CGSize(width: insetRect.width, height: .greatestFiniteMagnitude),
+            options: [.usesLineFragmentOrigin, .usesFontLeading],
+            attributes: [.font: font ?? UIFont.systemFont(ofSize: 17)],
+            context: nil
+        ).size
+        
+        // 重新定义绘制区域,使文本始终靠左上角
+        var adjustedRect = insetRect
+        adjustedRect.size.height = min(textSize.height, insetRect.height)
+        
+        // 调用父类的绘制方法
+        super.drawText(in: adjustedRect)
+    }
+    
+    open override var intrinsicContentSize: CGSize {
+        // 根据内容大小和内间距调整固有内容大小
+        guard let text = text else { return .zero }
+        let textSize = (text as NSString).boundingRect(
+            with: CGSize(width: bounds.width - textInsets.left - textInsets.right, height: .greatestFiniteMagnitude),
+            options: [.usesLineFragmentOrigin, .usesFontLeading],
+            attributes: [.font: font ?? UIFont.systemFont(ofSize: 17)],
+            context: nil
+        ).size
+        let width = textSize.width + textInsets.left + textInsets.right
+        let height = textSize.height + textInsets.top + textInsets.bottom
+        return CGSize(width: width, height: height)
+    }
+    
+    open override func sizeThatFits(_ size: CGSize) -> CGSize {
+        // 调整 `sizeThatFits` 的结果以包含内间距
+        guard let text = text else { return .zero }
+        let textSize = (text as NSString).boundingRect(
+            with: CGSize(width: size.width - textInsets.left - textInsets.right, height: .greatestFiniteMagnitude),
+            options: [.usesLineFragmentOrigin, .usesFontLeading],
+            attributes: [.font: font ?? UIFont.systemFont(ofSize: 17)],
+            context: nil
+        ).size
+        let width = textSize.width + textInsets.left + textInsets.right
+        let height = textSize.height + textInsets.top + textInsets.bottom
+        return CGSize(width: width, height: height)
+    }
+}

+ 148 - 0
AIPlayRingtones/Classes/View/UIStackView/KLMultiScrollContainer.swift

@@ -0,0 +1,148 @@
+//
+//  ScrollContainer.swift
+//  TmpProject
+//
+//  Created by nkl on 2025/4/27.
+//
+
+import Foundation
+import UIKit
+
+open class KLMultiScrollContainer: UIScrollView {
+    
+    public override init(frame: CGRect) {
+        super.init(frame: frame)
+        
+        // 在某些情况下,contentView中的点击事件会被panGestureRecognizer拦截,导致不能响应,
+        // 这里设置cancelsTouchesInView表示不拦截
+        // panGestureRecognizer.cancelsTouchesInView = false
+        
+        if #available(iOS 13.0, *) {
+            automaticallyAdjustsScrollIndicatorInsets = false
+        }
+        if #available(iOS 11.0, *) {
+            contentInsetAdjustmentBehavior = .never
+        } else {
+        
+        }
+        delegate = self
+        showsVerticalScrollIndicator = false
+    }
+    
+    required public init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    // scroller最大偏移量(最多只能滑动多少)
+    @objc public var maxContentOffset: CGFloat = 0.0
+    
+    // 是否能滑动/或者正在滑动 超过了 maxContentOffset 就不能滑动
+    @objc public private(set) var isScrolling: Bool = true {
+        didSet {
+            if isScrolling == oldValue {
+                return
+            }
+            updateSubScollViewsState()
+        }
+    }
+    
+    @objc public var subScrollViews: [UIScrollView] = [UIScrollView]() {
+        willSet{
+            subScrollViews.forEach {
+                $0.removeObserver(self, forKeyPath: kObserveKeyPath)
+            }
+        }
+        didSet {
+            subScrollViews.forEach {
+                $0.addObserver(self, forKeyPath: kObserveKeyPath, options: .new, context: nil)
+                $0.scrollsToTop = false
+                if #available(iOS 11.0, *) {
+                    $0.contentInsetAdjustmentBehavior = .never
+                } else {
+                
+                }
+            }
+        }
+    }
+    
+    public func addScrollView(_ scrollView : UIScrollView){
+        if subScrollViews.contains(scrollView) {return}
+        subScrollViews.append(scrollView)
+    }
+        
+    deinit {
+        subScrollViews.forEach { $0.removeObserver(self, forKeyPath: kObserveKeyPath) }
+    }
+}
+
+//MARK: - UIGestureRecognizerDelegate
+
+extension KLMultiScrollContainer : UIGestureRecognizerDelegate {
+    
+    /// 返回true表示可以继续传递触摸事件,这样两个嵌套的scrollView才能同时滚动
+    open func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+        if let view = otherGestureRecognizer.view as? UIScrollView {
+            return subScrollViews.contains(view)
+        }
+        return false
+    }
+}
+
+//MARK: - Observer
+
+extension KLMultiScrollContainer {
+    
+    open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
+        guard let newValue = change?[.newKey] as? CGPoint else {
+            return
+        }
+        
+        guard let scrollView = object as? UIScrollView else {
+            return
+        }
+        
+        // 只有在拖动的时候才考虑设置
+        // 当点击某个单元格,push到另一个页面时,也会触发该监听,
+        // 同时offset被置为0了,导致自动滚到顶部,因此要添加该判断
+        if !scrollView.isDragging {
+            return
+        }
+        
+        if isScrolling && maxContentOffset > contentOffset.y {
+            // 先移除一下监听,contentOffset被设置时也会调用监听,导致重复调用
+            // 虽然可以使用 ‘scrollView.setContentOffset(.zero, animated: false)’ 保证不被重复监听,但是滑动效果不顺畅
+            scrollView.removeObserver(self, forKeyPath: kObserveKeyPath)
+            scrollView.contentOffset = .zero
+            scrollView.addObserver(self, forKeyPath: kObserveKeyPath, options: .new, context: nil)
+            return
+        }
+        if  newValue.y <= 0 {
+            isScrolling = true
+        } else {
+            isScrolling = false
+        }
+        
+    }
+    
+    fileprivate func updateSubScollViewsState() -> Void {
+        subScrollViews.forEach { scrollView in
+            if isScrolling {
+                scrollView.setContentOffset(.zero, animated: false)
+            }
+            scrollView.showsVerticalScrollIndicator = !isScrolling
+        }
+    }
+}
+
+extension KLMultiScrollContainer : UIScrollViewDelegate {
+    
+    open func scrollViewDidScroll(_ scrollView: UIScrollView) {
+        if  (scrollView.contentOffset.y > maxContentOffset) ||
+            (scrollView.contentOffset.y < maxContentOffset && !isScrolling) {
+            scrollView.contentOffset = CGPoint(x: 0, y: maxContentOffset)
+        }
+    }
+}
+
+fileprivate var kObserveKeyPath = "contentOffset"
+fileprivate var kOffsetContext = "content_offset_context"

+ 164 - 0
AIPlayRingtones/Classes/View/UIStackView/TSCustomStackView.swift

@@ -0,0 +1,164 @@
+//
+//  CustomStackView.swift
+//  TestUIKit
+//
+//  Created by 100Years on 2025/2/24.
+//
+
+import UIKit
+import SnapKit
+
+open class TSCustomStackView: UIView {
+    // 内部的 UIScrollView 和 UIStackView
+    public let scrollView: UIScrollView
+//    public let scrollView: KLMultiScrollContainer
+    
+    
+    
+    
+    public let stackView: UIStackView
+    
+    // 开放的属性,用于设置方向和间距
+    public var axis: NSLayoutConstraint.Axis {
+        get {
+            return stackView.axis
+        }
+        set {
+            stackView.axis = newValue
+            updateScrollViewConstraints()
+        }
+    }
+    
+    public var spacing: CGFloat {
+        get {
+            return stackView.spacing
+        }
+        set {
+            stackView.spacing = newValue
+        }
+    }
+    
+    public var viewH:CGFloat {
+        get {
+            return scrollView.contentSize.height
+        }
+    }
+    
+    // 初始化方法
+    public init(axis: NSLayoutConstraint.Axis = .vertical,alignment:UIStackView.Alignment = .leading, spacing: CGFloat = 0) {
+        self.scrollView = UIScrollView()
+//        self.scrollView = KLMultiScrollContainer()
+        self.scrollView.showsVerticalScrollIndicator = false
+        self.scrollView.showsHorizontalScrollIndicator = false
+        self.stackView = UIStackView()
+        self.stackView.axis = axis
+        self.stackView.spacing = spacing
+        self.stackView.alignment = alignment
+        self.stackView.distribution = .fill
+        super.init(frame: .zero)
+        setupUI()
+    }
+    
+    required public init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    // 设置 UI
+    private func setupUI() {
+        // 添加 scrollView
+        addSubview(scrollView)
+        scrollView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+        
+        // 添加 stackView 到 scrollView
+        scrollView.addSubview(stackView)
+        updateScrollViewConstraints()
+    }
+    
+    // 根据轴方向更新约束
+    private func updateScrollViewConstraints() {
+        stackView.snp.remakeConstraints { make in
+            make.edges.equalToSuperview()
+            
+            // 根据轴方向设置 contentSize
+            if axis == .vertical {
+                make.width.equalTo(scrollView)
+            } else {
+                make.height.equalTo(scrollView)
+            }
+        }
+    }
+    
+    // 动态添加子视图的方法
+    public func addSubviewToStack(_ view: UIView,length:CGFloat? = nil) {
+        stackView.addArrangedSubview(view)
+        view.snp.makeConstraints { make in
+            if axis == .vertical {
+                make.width.equalTo(stackView)
+                if let length = length {
+                    make.height.equalTo(length)
+                }
+            } else {
+                make.height.equalTo(stackView)
+                if let length = length {
+                    make.width.equalTo(length)
+                }
+            }
+        }
+    }
+    
+    //动态添加子视图的方法(添加到白板一个空板 View)
+    public func addSubviewToStackWhiteBoard(_ view: UIView,length:CGFloat? = nil) {
+        let bgView = UIView()
+        bgView.addSubview(view)
+        addSubviewToStack(bgView,length: length)
+    }
+    
+    // 在指定位置插入子视图
+    public func insertViewToStack(_ view: UIView, at stackIndex: Int) {
+        stackView.insertArrangedSubview(view, at: stackIndex)
+        // 可以根据需要对子视图进行额外的布局设置
+        view.snp.makeConstraints { make in
+            if axis == .vertical {
+                make.width.equalTo(stackView)
+            } else {
+                make.height.equalTo(stackView)
+            }
+        }
+    }
+    
+    // 移除子视图
+    public func removeViewToStack(_ view: UIView) {
+        stackView.removeArrangedSubview(view)
+        view.removeFromSuperview()
+    }
+    
+    public func addSpacing(length:CGFloat) {
+        let view = UIView()
+        addSubviewToStack(view)
+        view.snp.makeConstraints { make in
+            if axis == .vertical {
+                make.height.equalTo(length)
+            } else {
+                make.width.equalTo(length)
+            }
+        }
+    }
+}
+
+extension UIStackView {
+    public func addSpacing(length:CGFloat) {
+        let view = UIView()
+        self.addArrangedSubview(view)
+        view.snp.makeConstraints { make in
+            if axis == .vertical {
+                make.width.equalToSuperview()
+                make.height.equalTo(length)
+            } else {
+                make.width.equalTo(length)
+                make.height.equalToSuperview()
+            }
+        }
+    }
+}

+ 83 - 0
AIPlayRingtones/Classes/View/UITableView+TSItemModel/TSSimpleTableView.swift

@@ -0,0 +1,83 @@
+//
+//  TSTableView.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2025/1/4.
+//
+
+
+
+open class TSSimpleTableView : NSObject{
+    open var cellID:String = "TSSimpleTableViewCell"
+    open lazy var tableView:UITableView = {
+        let tableView = UITableView()
+        tableView.initBaseTableView(reuseClass: reuseClass, isUseMJRefresh: false, delegate: self)
+        return tableView
+    }()
+    
+    open var reuseClass:[String] = [] {
+        didSet{
+            if let string = reuseClass.first {
+                cellID = string
+            }
+            tableView.registerCellClass(cellIds: reuseClass)
+        }
+    }
+    
+    open var dataArray:[TSBasicSectionModel] = [TSBasicSectionModel](){
+        didSet{
+            tableView.reloadData()
+        }
+    }
+
+}
+
+
+extension TSSimpleTableView : UITableViewDataSource, UITableViewDelegate {
+    
+    public func numberOfSections(in tableView: UITableView) -> Int {
+        return dataArray.count
+    }
+    
+    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        if let sectionModel = dataArray.safeObj(At: section){
+            return sectionModel.itemsArray.count
+        }
+        return 0
+    }
+    
+    public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        if let sectionModel = dataArray.safeObj(At: indexPath.section),let itemModel = sectionModel.itemsArray.safeObj(At: indexPath.row){
+            return itemModel.height
+        }
+        return 0
+    }
+    
+    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: cellID)
+        
+        if let cell = cell as? TSSimpleTableViewCell {
+            if let sectionModel = dataArray.safeObj(At: indexPath.section),let itemModel = sectionModel.itemsArray.safeObj(At: indexPath.row){
+                cell.itemModel = itemModel
+            }
+            return cell
+        }
+        
+        return UITableViewCell()
+    }
+    
+    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        if let sectionModel = dataArray.safeObj(At: indexPath.section),let itemModel = sectionModel.itemsArray.safeObj(At: indexPath.row){
+            itemModel.tapBlock?(itemModel,indexPath.row,nil)
+        }
+    }
+}
+
+
+open class TSSimpleTableViewCell: TSBaseTabViewCell {
+    open var itemModel:TSBasicItemModel = TSBasicItemModel()
+    
+    open func didEndDisplaying(indexPath: IndexPath){
+        
+    }
+}

+ 121 - 2
AIPlayRingtones/Common/Common.swift

@@ -5,11 +5,130 @@
 //  Created by 100Years on 2025/5/15.
 //
 
-@_exported import TSSmalCoacopods
-
 func kAddCommonGradientBg(view:UIView){
     view.setNeedsLayout()
     DispatchQueue.main.async {
         view.addGradientBg(colors: ["#202030".uiCGColor,"#343352".uiCGColor],startPoint: CGPoint(x: 0, y: 0),endPoint: CGPoint(x: 1, y: 0))
     }
 }
+
+
+var modelIdentifier: String {
+    var systemInfo = utsname()
+    uname(&systemInfo)
+    
+    let machineMirror = Mirror(reflecting: systemInfo.machine)
+    let identifier = machineMirror.children.reduce("") { (identifier, element) in
+        guard let value = element.value as? Int8, value != 0 else {
+            return identifier
+        }
+        return identifier + String(UnicodeScalar(UInt8(value)))
+    }
+    
+    return identifier
+}
+
+var deviceName: String = {
+    
+    let identifier = modelIdentifier
+    
+    switch identifier {
+    case "iPhone8,1":
+        return "iPhone 6s"
+    case "iPhone8,2":
+        return "iPhone 6s Plus"
+    case "iPhone8,4":
+        return "iPhone SE"
+    case "iPhone9,1":
+        return "iPhone 7"; //国行、日版、港行
+    case "iPhone9,2":
+        return "iPhone 7 Plus"; //国行、港行
+    case "iPhone9,3":
+        return "iPhone 7"; //美版、台版
+    case "iPhone9,4":
+        return "iPhone 7 Plus"; //美版、台版
+    case "iPhone10,1":
+        return "iPhone 8"; //国行(A1863)、日行(A1906)
+    case "iPhone10,2":
+        return "iPhone 8 Plus"; //国行(A1864)、日行(A1898)
+    case "iPhone10,3":
+        return "iPhone X"; //国行(A1865)、日行(A1902)
+    case "iPhone10,4":
+        return "iPhone 8"; //美版(Global/A1905)
+    case "iPhone10,5":
+        return "iPhone 8 Plus"; //美版(Global/A1897)
+    case "iPhone10,6":
+        return "iPhone X";//美版(Global/A1901)
+        
+    // 刘海屏(除SE外)
+    case "iPhone11,8":
+        return "iPhone XR"
+    case "iPhone11,2":
+        return "iPhone XS"
+    case "iPhone11,6":
+        return "iPhone XS Max"
+    case "iPhone11,4":
+        return "iPhone XS Max"
+    case "iPhone12,1":
+        return "iPhone 11"
+    case "iPhone12,3":
+        return "iPhone 11 Pro"
+    case "iPhone12,5":
+        return "iPhone 11 Pro Max"
+    case "iPhone12,8":
+        return "iPhone SE2"; //(2nd generation)
+    case "iPhone13,1":
+        return "iPhone 12 mini"
+    case "iPhone13,2":
+        return "iPhone 12"
+    case "iPhone13,3":
+        return "iPhone 12 Pro"
+    case "iPhone13,4":
+        return "iPhone 12 Pro Max"
+    case "iPhone14,2":
+        return "iPhone 13 Pro"
+    case "iPhone14,3":
+        return "iPhone 13 Pro Max"
+    case "iPhone14,4":
+        return "iPhone 13 mini"
+    case "iPhone14,5":
+        return "iPhone 13"
+    case "iPhone14,6":
+        return "iPhone SE3"; //(2nd generation)
+    case "iPhone14,7":
+        return "iPhone 14"
+    case "iPhone14,8":
+        return "iPhone 14 Plus"
+        
+    // 灵动岛
+    case "iPhone15,2":
+        return "iPhone 14 Pro"
+    case "iPhone15,3":
+        return "iPhone 14 Pro Max"
+        
+    case "iPhone16,2":
+        return "iPhone 15 Pro Max"
+    case "iPhone16,1":
+        return "iPhone 15 Pro"
+    case "iPhone15,5":
+        return "iPhone 15 Plus"
+    case "iPhone15,4":
+        return "iPhone 15"
+
+        
+    case "iPhone17,3":
+        return "iPhone 16"
+    case "iPhone17,4":
+        return "iPhone 16 Plus"
+    case "iPhone17,1":
+        return "iPhone 16 Pro"
+    case "iPhone17,2":
+        return "iPhone 16 Pro Max"
+        
+        
+        
+    default:
+        return "--"
+    }
+}()
+

+ 0 - 394
AIPlayRingtones/Common/CpuMapManager.swift

@@ -1,394 +0,0 @@
-//
-//  CpuMapManager.swift
-//  ClockWidget
-//
-//  Created by fff on 2023/7/28.
-//
-
-import UIKit
-
-class CpuMapManager: NSObject {
-    static let shared = CpuMapManager()
-    
-    /// 数据来源 https://kylebing.cn/tools/iphone/
-    ///         http://kylebing.cn/tools/iphone/
-    ///
-    private let deviceMap: [String: [String: String]] = [
-        
-        "iPhone 16 Pro Max" : ["CPU":"A18 Pro",
-                               "Freq": "4.04GHz",
-                               "Battery": "4685 mah"],
-        "iPhone 16 Pro"     : ["CPU":"A18 Pro",
-                               "Freq": "4.04GHz",
-                               "Battery": "3582 mah"],
-        "iPhone 16 Plus"    : ["CPU":"A18",
-                               "Freq": "3.78GHz",
-                               "Battery": "4674 mah"],
-        "iPhone 16"         : ["CPU":"A18",
-                               "Freq": "3.78GHz",
-                               "Battery": "3561 mah"],
-        
-        "iPhone 15 Pro Max" : ["CPU":"A17 Pro",
-                               "Freq": "3.77GHz",
-                               "Battery": "4422 mah"],
-        "iPhone 15 Pro"     : ["CPU":"A17 Pro",
-                               "Freq": "3.77GHz",
-                               "Battery": "3274 mah"],
-        "iPhone 15 Plus"    : ["CPU":"A16",
-                               "Freq": "3.46GHz",
-                               "Battery": "4383 mah"],
-        "iPhone 15"         : ["CPU":"A16",
-                               "Freq": "3.46GHz",
-                               "Battery": "3349 mah"],
-        
-        "iPhone 14 Pro Max" : ["CPU":"A16",
-                               "Freq": "3.46GHz",
-                               "Battery": "4323 mah"],
-        "iPhone 14 Pro"     : ["CPU":"A16",
-                               "Freq": "3.46GHz",
-                               "Battery": "3200 mah"],
-        "iPhone 14 Plus"    : ["CPU":"A15",
-                               "Freq": "3.20GHz",
-                               "Battery": "4325 mah"],
-        "iPhone 14"         : ["CPU":"A15",
-                               "Freq": "3.20GHz",
-                               "Battery": "3279 mah"],
-        
-        "iPhone SE3"        : ["CPU":"A15",
-                               "Freq": "3.20GHz",
-                               "Battery": "2018 mah"],
-        "iPhone 13 Pro Max" : ["CPU":"A15",
-                               "Freq": "3.20GHz",
-                               "Battery": "4352 mah"],
-        "iPhone 13 Pro"     : ["CPU":"A15",
-                               "Freq": "3.20GHz",
-                               "Battery": "3095 mah"],
-        "iPhone 13"         : ["CPU":"A15",
-                               "Freq": "3.20GHz",
-                               "Battery": "3227 mah"],
-        "iPhone 13 Mini"    : ["CPU":"A15",
-                               "Freq": "3.20GHz",
-                               "Battery": "2406 mah"],
-        
-        "iPhone 12 Pro Max" : ["CPU":"A14",
-                               "Freq": "3.00GHz",
-                               "Battery": "3687 mah"],
-        "iPhone 12 Pro"     : ["CPU":"A14",
-                               "Freq": "3.00GHz",
-                               "Battery": "2815 mah"],
-        "iPhone 12"         : ["CPU":"A14",
-                               "Freq": "3.00GHz",
-                               "Battery": "2851 mah"],
-        "iPhone 12 Mini"    : ["CPU":"A14",
-                               "Freq": "3.00GHz",
-                               "Battery": "2227 mah"],
-        
-        "iPhone SE2"        : ["CPU":"A13",
-                               "Freq": "2.0GHz",
-                               "Battery": "1821 mah"],
-        
-        "iPhone 11 Pro Max" : ["CPU":"A13",
-                               "Freq": "2.0GHz",
-                               "Battery": "3969 mah"],
-        "iPhone 11 Pro"     : ["CPU":"A13",
-                               "Freq": "2.0GHz",
-                               "Battery": "3046 mah"],
-        "iPhone 11"         : ["CPU":"A13",
-                               "Freq": "2.0GHz",
-                               "Battery": "3110 mah"],
-        
-        "iPhone XR"         : ["CPU":"A13",
-                               "Freq": "2.0GHz",
-                               "Battery": "2942 mah"],
-        "iPhone XS Max"     : ["CPU":"A12",
-                               "Freq": "2.49GHz",
-                               "Battery": "3174 mah"],
-        "iPhone XS"         : ["CPU":"A12",
-                               "Freq": "2.49GHz",
-                               "Battery": "2658 mah"],
-        "iPhone X"          : ["CPU":"A11",
-                               "Freq": "1.4GHz",
-                               "Battery": "2716 mah"],
-        
-        "iPhone 8 Plus"     : ["CPU":"A11",
-                               "Freq": "1.4GHz",
-                               "Battery": "2691 mah"],
-        "iPhone 8"          : ["CPU":"A11",
-                               "Freq": "1.4GHz",
-                               "Battery": "1821 mah"],
-        "iPhone 7 Plus"     : ["CPU":"A11",
-                               "Freq": "1.4GHz",
-                               "Battery": "2900 mah"],
-        "iPhone 7"          : ["CPU":"A10",
-                               "Freq": "1.3GHz",
-                               "Battery": "1960 mah"],
-        "iPhone SE"         : ["CPU":"A9",
-                               "Freq": "1.85GHz",
-                               "Battery": "1624 mah"],
-        "iPhone 6s Plus"    : ["CPU":"A9",
-                               "Freq": "1.85GHz",
-                               "Battery": "2750 mah"],
-        "iPhone 6s"         : ["CPU":"A9",
-                               "Freq": "1.85GHz",
-                               "Battery": "1715 mah"],
-    ]
-    
-    private(set) var modelName: String
-    private(set) var cpuName: String?
-    /// CPU频率
-    private(set) var cpuFreq: String?
-    private(set) var batteryCapacity: String?
-    
-    override init() {
-        
-        modelName = UIDevice.current.modelName
-        cpuName = deviceMap[modelName]?["CPU"]
-        cpuFreq = deviceMap[modelName]? ["Freq"]
-        batteryCapacity = deviceMap[modelName]?["Battery"]
-        
-        super.init()
-    }
-}
-
-extension UIDevice {
-    
-    var modelIdentifier: String {
-        var systemInfo = utsname()
-        uname(&systemInfo)
-        
-        let machineMirror = Mirror(reflecting: systemInfo.machine)
-        let identifier = machineMirror.children.reduce("") { (identifier, element) in
-            guard let value = element.value as? Int8, value != 0 else {
-                return identifier
-            }
-            return identifier + String(UnicodeScalar(UInt8(value)))
-        }
-        
-        return identifier
-    }
-    
-    /// 数据来源 https://www.jianshu.com/p/d9068fee295e
-    var modelName: String {
-        
-        let identifier = modelIdentifier
-        
-        switch identifier {
-        case "iPhone8,1":
-            return "iPhone 6s"
-        case "iPhone8,2":
-            return "iPhone 6s Plus"
-        case "iPhone8,4":
-            return "iPhone SE"
-        case "iPhone9,1":
-            return "iPhone 7"; //国行、日版、港行
-        case "iPhone9,2":
-            return "iPhone 7 Plus"; //国行、港行
-        case "iPhone9,3":
-            return "iPhone 7"; //美版、台版
-        case "iPhone9,4":
-            return "iPhone 7 Plus"; //美版、台版
-        case "iPhone10,1":
-            return "iPhone 8"; //国行(A1863)、日行(A1906)
-        case "iPhone10,2":
-            return "iPhone 8 Plus"; //国行(A1864)、日行(A1898)
-        case "iPhone10,3":
-            return "iPhone X"; //国行(A1865)、日行(A1902)
-        case "iPhone10,4":
-            return "iPhone 8"; //美版(Global/A1905)
-        case "iPhone10,5":
-            return "iPhone 8 Plus"; //美版(Global/A1897)
-        case "iPhone10,6":
-            return "iPhone X";//美版(Global/A1901)
-            
-        // 刘海屏(除SE外)
-        case "iPhone11,8":
-            return "iPhone XR"
-        case "iPhone11,2":
-            return "iPhone XS"
-        case "iPhone11,6":
-            return "iPhone XS Max"
-        case "iPhone11,4":
-            return "iPhone XS Max"
-        case "iPhone12,1":
-            return "iPhone 11"
-        case "iPhone12,3":
-            return "iPhone 11 Pro"
-        case "iPhone12,5":
-            return "iPhone 11 Pro Max"
-        case "iPhone12,8":
-            return "iPhone SE2"; //(2nd generation)
-        case "iPhone13,1":
-            return "iPhone 12 mini"
-        case "iPhone13,2":
-            return "iPhone 12"
-        case "iPhone13,3":
-            return "iPhone 12 Pro"
-        case "iPhone13,4":
-            return "iPhone 12 Pro Max"
-        case "iPhone14,2":
-            return "iPhone 13 Pro"
-        case "iPhone14,3":
-            return "iPhone 13 Pro Max"
-        case "iPhone14,4":
-            return "iPhone 13 mini"
-        case "iPhone14,5":
-            return "iPhone 13"
-        case "iPhone14,6":
-            return "iPhone SE3"; //(2nd generation)
-        case "iPhone14,7":
-            return "iPhone 14"
-        case "iPhone14,8":
-            return "iPhone 14 Plus"
-            
-        // 灵动岛
-        case "iPhone15,2":
-            return "iPhone 14 Pro"
-        case "iPhone15,3":
-            return "iPhone 14 Pro Max"
-            
-        case "iPhone16,2":
-            return "iPhone 15 Pro Max"
-        case "iPhone16,1":
-            return "iPhone 15 Pro"
-        case "iPhone15,5":
-            return "iPhone 15 Plus"
-        case "iPhone15,4":
-            return "iPhone 15"
-
-            
-        case "iPhone17,3":
-            return "iPhone 16"
-        case "iPhone17,4":
-            return "iPhone 16 Plus"
-        case "iPhone17,1":
-            return "iPhone 16 Pro"
-        case "iPhone17,2":
-            return "iPhone 16 Pro Max"
-            
-            
-            
-        default:
-            return "--"
-        }
-    }
-    
-    var currentSysVersion: Int {
-        let version = UIDevice.current.systemVersion
-        let v1 = version.components(separatedBy: ".").first ?? "0"
-        let doubleVersion = Double(v1)
-        if let doubleVersion = doubleVersion {
-            return Int(doubleVersion)
-        }
-        return 0
-    }
-    
-    // 版本判断
-    func currentSysVersionEqualOrGreater(than version: String) -> Bool {
-        var sysValues = UIDevice.current.systemVersion.components(separatedBy: ".")
-        var values = version.components(separatedBy: ".")
-        
-        // 位数不同,少的补0
-        let count = max(sysValues.count, values.count)
-        if sysValues.count < count {
-            let more = Array(repeating: "0", count: count-sysValues.count)
-            sysValues.append(contentsOf: more)
-        }
-        if values.count < count {
-            let more = Array(repeating: "0", count: count-values.count)
-            values.append(contentsOf: more)
-        }
-        
-        for idx in 0..<count {
-            if let sv = Int(sysValues.safeObj(At: idx) ?? "0"),
-                let v = Int(values.safeObj(At: idx) ?? "0") {
-                if sv > v {
-                    return true
-                } else if sv < v {
-                    return false
-                }
-            }
-        }
-        return true
-    }
-    
-    var iPhoneXids: [String] {
-        return ["iPhone10,3", "iPhone10,6"]
-    }
-    
-    var iPhoneSEids: [String] {
-        return ["iPhone8,4", "iPhone12,8", "iPhone14,6"]
-    }
-}
-
-// 传感器样式: 无 | 刘海 | 灵动岛
-enum DeviceSensorStyle {
-    case none
-    case bang
-    case island
-}
-
-extension UIDevice {
-    func sensorStyle() -> DeviceSensorStyle {
-#if targetEnvironment(simulator)
-        return .island
-#endif
-        let identifier = UIDevice.current.modelIdentifier
-        // SE
-        if iPhoneSEids.contains(identifier) {
-            return .none
-        }
-        // X
-        if iPhoneXids.contains(identifier) {
-            return .bang
-        }
-        if identifier.hasPrefix("iPhone") {
-            let digital = identifier.replacingOccurrences(of: "iPhone", with: "")
-            let arr = digital.components(separatedBy: ",")
-            if let v1 = arr.safeObj(At: 0),
-               let vv1 = Double(v1) {
-                if vv1 >= 15 {
-                    return .island
-                } else {
-                    if vv1 < 11 {
-                        return .none
-                    }
-                    return .bang
-                }
-            }
-        }
-        return .none
-    }
-    
-    // 刘海屏 及以后设备才可以升级到16.1
-    var supportLiveActivity: Bool {
-        guard #available(iOS 16.1, *) else {
-            return false
-        }
-        let style = sensorStyle()
-        return style != .none
-    }
-    
-    var sensorSize: CGSize {
-        let style = sensorStyle()
-        switch style {
-        case .none:
-            return .zero
-        case .bang:
-            let ratio: CGFloat = UIScreen.main.bounds.width/375.0
-            return CGSize(width: 219*ratio, height: 30*ratio)
-        case .island:
-            return CGSize(width: 125, height: 36.7)//16s 和 16sp 是 126*37.3
-        }
-    }
-    
-    var sensorTop:Double{
-        let kScreenWidth = UIScreen.main.bounds.size.width
-        let kScreenHeight = UIScreen.main.bounds.size.height
-        //16s和 16sp
-        if (kScreenWidth == 440 && kScreenHeight == 956) || (kScreenWidth == 402 && kScreenHeight == 874) ||  modelName.contains("iPhone 16 Pro") {
-            return 14.0
-        } else {
-            return 11.3
-        }
-    }
-}

+ 15 - 5
AIPlayRingtones/Common/TSNetWork/TSNetWork+Business.swift

@@ -43,7 +43,7 @@ func getUserInfoJsonString()->[String:Any] {
     }
     
     let dic:[String:Any] = [
-        "device":UIDevice.current.modelName,
+        "device":deviceName,
         "deviceId":uuid,
         "iosVersion":UIDevice.current.systemVersion,
         "appVersion":appShortVersion(),
@@ -73,11 +73,16 @@ extension TSNetworkManager {
         urlType: TSNeURLType,
         parameters: [String: Any]? = nil,
         responseType: T.Type? = nil,
-        completion: @escaping (Result<Any, Error>) -> Void
+        completion: @escaping (Any?, Error?) -> Void
     ) -> Request  {
         let urlString = urlType.getUrlString()
         return request(method: .get, urlString: urlString, parameters:parameters) { result in
-            completion(result)
+            switch result {
+            case .success(let data):
+                completion(data,nil)
+            case .failure(let error):
+                completion(nil,error)
+            }
         }
     }
 
@@ -92,11 +97,16 @@ extension TSNetworkManager {
         urlType: TSNeURLType,
         parameters: [String: Any]? = nil,
         responseType: T.Type? = nil,
-        completion: @escaping (Result<Any, Error>) -> Void
+        completion: @escaping (Any?, Error?) -> Void
     ) -> Request {
         let urlString = urlType.getUrlString()
         return request(method: .post, urlString: urlString, parameters:parameters) { result in
-            completion(result)
+            switch result {
+            case .success(let data):
+                completion(data,nil)
+            case .failure(let error):
+                completion(nil,error)
+            }
         }
     }
     

+ 0 - 86
AIPlayRingtones/Common/TSNetWork/TSNetworkManager+Loading.swift

@@ -1,86 +0,0 @@
-//
-//  TSNetworkManager+Loading.swift
-//  AIEmoji
-//
-//  Created by 100Years on 2025/1/16.
-//
-import Alamofire
-extension TSNetworkManager {
-    
-    /// 通用 get 请求
-    func get<T: TSBaseModel>(
-        urlType: TSNeURLType,
-        parameters: [String: Any]? = nil,
-        responseType: T.Type? = nil,
-        animationView:UIView? = nil,
-        completion: @escaping (Any?, Error?) -> Void
-    ) -> Request {
-        
-        var isShowAnimation = false
-        if animationView != nil {
-            isShowAnimation = true
-        }
-        
-        if isShowAnimation,let view = animationView {
-            TSToastShared.showLoading(containerView: view)
-        }
-
-        return get(urlType: urlType, parameters:parameters,responseType:responseType) { result in
-            
-            if isShowAnimation {
-                TSToastShared.hideLoading()
-            }
-    
-            switch result {
-            case .success(let data):
-//                print("Response data: \(data)")
-                completion(data,nil)
-            case .failure(let error):
-//                print("Error: \(error)")
-                completion(nil,error)
-            }
-        }
-    }
-
-
-    /// 通用 POST 请求
-    /// - Parameters:
-    ///   - endpoint: 接口路径
-    ///   - parameters: 请求参数
-    ///   - responseType: 响应数据模型(可选)
-    ///   - completion: 请求完成的回调
-    func post<T: TSBaseModel>(
-        urlType: TSNeURLType,
-        parameters: [String: Any]? = nil,
-        responseType: T.Type? = nil,
-        animationView:UIView? = nil,
-        completion: @escaping (Any?, Error?) -> Void
-    ) -> Request {
-        var isShowAnimation = false
-        if animationView != nil {
-            isShowAnimation = true
-        }
-        
-        if isShowAnimation,let view = animationView {
-            TSToastShared.showLoading(containerView: view)
-        }
-
-        return post(urlType: urlType, parameters:parameters,responseType:responseType){ result in
-            
-            if isShowAnimation {
-                TSToastShared.hideLoading()
-            }
-    
-            switch result {
-            case .success(let data):
-//                print("Response data: \(data)")
-                completion(data,nil)
-            case .failure(let error):
-//                print("Error: \(error)")
-                completion(nil,error)
-            }
-        }
-    
-    }
-    
-}

+ 2 - 0
AIPlayRingtones/OperationQueue/Generate/ASGenerateBaseOperation.swift

@@ -119,6 +119,8 @@ class ASGenerateBaseOperation: ASBaseOperation , @unchecked Sendable{
 
     func getActionInfo(action_id:Int){
         self.action_id = action_id
+        
+              
         queryRequest = TSNetworkShared.get(urlType: .actionInfo,parameters: ["action_id":action_id]) { [weak self] data,error in
             guard let self = self else { return }
             

+ 3 - 1
Podfile

@@ -15,8 +15,10 @@ target 'AIPlayRingtones' do
   #pod 'MJRefresh'
   pod 'IQKeyboardManagerSwift'
   pod 'SwipeCellKit'
-  pod 'TSSmalCoacopods', :path => '../TSSmalCoacopods'
+  #pod 'TSSmalCoacopods', :path => '../TSSmalCoacopods'
   pod 'RealmSwift', '~>10'
+  pod 'SVProgressHUD'
+  
 end
 
 

+ 2 - 13
Podfile.lock

@@ -52,12 +52,6 @@ PODS:
     - SVProgressHUD/Core (= 2.3.1)
   - SVProgressHUD/Core (2.3.1)
   - SwipeCellKit (2.7.1)
-  - TSSmalCoacopods (1.0.0):
-    - Alamofire
-    - Kingfisher (= 7.10.0)
-    - ObjectMapper (~> 4.2)
-    - SnapKit
-    - SVProgressHUD
 
 DEPENDENCIES:
   - Alamofire
@@ -66,8 +60,8 @@ DEPENDENCIES:
   - ObjectMapper
   - RealmSwift (~> 10)
   - SnapKit
+  - SVProgressHUD
   - SwipeCellKit
-  - TSSmalCoacopods (from `../TSSmalCoacopods`)
 
 SPEC REPOS:
   trunk:
@@ -88,10 +82,6 @@ SPEC REPOS:
     - SVProgressHUD
     - SwipeCellKit
 
-EXTERNAL SOURCES:
-  TSSmalCoacopods:
-    :path: "../TSSmalCoacopods"
-
 SPEC CHECKSUMS:
   Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
   IQKeyboardCore: 28c8bf3bcd8ba5aa1570b318cbc4da94b861711e
@@ -109,8 +99,7 @@ SPEC CHECKSUMS:
   SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
   SVProgressHUD: 4837c74bdfe2e51e8821c397825996a8d7de6e22
   SwipeCellKit: 3972254a826da74609926daf59b08d6c72e619ea
-  TSSmalCoacopods: 6aa97167f0c76b16fc7d1fd1eb198bb6aece4f68
 
-PODFILE CHECKSUM: e5f2aa00c39d162821649d58c8f7f73aff6cf589
+PODFILE CHECKSUM: 20b0d18adc2c2ae41c11f9d74f5e31fe5b678fe0
 
 COCOAPODS: 1.16.2