Kaynağa Gözat

feat:图片融合开发一部分

100Years 3 hafta önce
ebeveyn
işleme
e394b1519f
33 değiştirilmiş dosya ile 1102 ekleme ve 393 silme
  1. 3 0
      Podfile
  2. 12 1
      Podfile.lock
  3. 26 0
      TSLiveWallpaper.xcodeproj/project.pbxproj
  4. BIN
      TSLiveWallpaper/Assets.xcassets/.DS_Store
  5. 22 0
      TSLiveWallpaper/Assets.xcassets/AIList/add.imageset/Contents.json
  6. BIN
      TSLiveWallpaper/Assets.xcassets/AIList/add.imageset/add@2x.png
  7. BIN
      TSLiveWallpaper/Assets.xcassets/AIList/add.imageset/add@3x.png
  8. 22 0
      TSLiveWallpaper/Assets.xcassets/AIList/aiList_PortraitFusion.imageset/Contents.json
  9. BIN
      TSLiveWallpaper/Assets.xcassets/AIList/aiList_PortraitFusion.imageset/aiList_PortraitFusion@2x.png
  10. BIN
      TSLiveWallpaper/Assets.xcassets/AIList/aiList_PortraitFusion.imageset/aiList_PortraitFusion@3x.png
  11. 22 0
      TSLiveWallpaper/Assets.xcassets/Hint/hint_PortraitFusion_good.imageset/Contents.json
  12. BIN
      TSLiveWallpaper/Assets.xcassets/Hint/hint_PortraitFusion_good.imageset/hint_PortraitFusion_good@2x.png
  13. BIN
      TSLiveWallpaper/Assets.xcassets/Hint/hint_PortraitFusion_good.imageset/hint_PortraitFusion_good@3x.png
  14. BIN
      TSLiveWallpaper/Assets.xcassets/Music/.DS_Store
  15. 21 19
      TSLiveWallpaper/Business/TSAIListVC/TSAIAgeImageHintVC/TSAIListHintBaseVC.swift
  16. 13 0
      TSLiveWallpaper/Business/TSAIListVC/TSAIList+Enmu.swift
  17. 35 34
      TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/TSAIListVC.swift
  18. 186 192
      TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/TSAIListVM.swift
  19. 1 3
      TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/VM/TSAIListDataVM+Click.swift
  20. 12 0
      TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/VM/TSAIListDataVM+Dic.swift
  21. 16 1
      TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/VM/TSAIListDataVM.swift
  22. 54 0
      TSLiveWallpaper/Business/TSAIListVC/TSAIPhotoDetailsVC/TSAIPhotoDetailsVC/TSAIPhotoDetailsVC+Comparison.swift
  23. 18 34
      TSLiveWallpaper/Business/TSAIListVC/TSAIPhotoDetailsVC/TSAIPhotoDetailsVC/TSAIPhotoDetailsVC+Image.swift
  24. 4 1
      TSLiveWallpaper/Business/TSAIListVC/TSAIPhotoDetailsVC/TSAIPhotoDetailsVC/TSAIPhotoDetailsVC.swift
  25. 4 8
      TSLiveWallpaper/Business/TSAIListVC/TSAIUploadPhotoVC/TSAIUploadPhotoVC+View.swift
  26. 1 2
      TSLiveWallpaper/Business/TSAIListVC/TSAIUploadPhotoVC/TSAIUploadPhotoVC.swift
  27. 136 0
      TSLiveWallpaper/Business/TSAIListVC/TSFusionImageVC/TSAISmallUploadView.swift
  28. 195 0
      TSLiveWallpaper/Business/TSAIListVC/TSFusionImageVC/TSFusionImageVC.swift
  29. 3 3
      TSLiveWallpaper/Common/Purchase/TSPurchaseManager.swift
  30. 51 0
      TSLiveWallpaper/Common/ViewTool/TSPhotoPickerManager/TSHXPhotoPickerManager.swift
  31. 235 95
      TSLiveWallpaper/Common/ViewTool/TSPhotoPickerManager/TSPhotoPickerManager.swift
  32. 5 0
      TSLiveWallpaper/Data/Model/TSActionInfoModel.swift
  33. 5 0
      TSLiveWallpaper/Data/OperationQueue/TSGenerateBaseOperation/TSGenerateBasePhotoOperation.swift

+ 3 - 0
Podfile

@@ -29,6 +29,9 @@ target 'TSLiveWallpaper' do
   pod "DynamicBlurView"
 #  pod 'TYCyclePagerView'
   pod "KingfisherWebP"
+  
+  pod "HXPhotoPicker/Picker" #只有选择器
+  pod "HXPhotoPicker/Camera/Lite"#不包含定位功能
 end
 
 

+ 12 - 1
Podfile.lock

@@ -17,6 +17,13 @@ PODS:
   - Alamofire (5.10.2)
   - BetterSegmentedControl (2.0.1)
   - DynamicBlurView (5.0.3)
+  - HXPhotoPicker/Camera/Lite (5.0.3):
+    - HXPhotoPicker/Core
+  - HXPhotoPicker/Core (5.0.3):
+    - HXPhotoPicker/Resources
+  - HXPhotoPicker/Picker (5.0.3):
+    - HXPhotoPicker/Core
+  - HXPhotoPicker/Resources (5.0.3)
   - Kingfisher (7.10.0)
   - KingfisherWebP (1.5.4):
     - Kingfisher (~> 7.9)
@@ -71,6 +78,8 @@ DEPENDENCIES:
   - Alamofire
   - BetterSegmentedControl (~> 2.0)
   - DynamicBlurView
+  - HXPhotoPicker/Camera/Lite
+  - HXPhotoPicker/Picker
   - Kingfisher (= 7.10.0)
   - KingfisherWebP
   - KLExtension (from `https://gitee.com/WanlanNeel/klextension.git`)
@@ -91,6 +100,7 @@ SPEC REPOS:
     - Alamofire
     - BetterSegmentedControl
     - DynamicBlurView
+    - HXPhotoPicker
     - Kingfisher
     - KingfisherWebP
     - libwebp
@@ -131,6 +141,7 @@ SPEC CHECKSUMS:
   Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
   BetterSegmentedControl: 09607b27861d49cbce48b7673b74f9150a3d371a
   DynamicBlurView: b57e2f6aa33f85b2bcca272265162a3c7c5cc499
+  HXPhotoPicker: 3250e3d7b5c1e9a35888a35f8b8abc99a18ed17f
   Kingfisher: a18f05d3b6d37d8650ee4a3e61d57a28fc6207f6
   KingfisherWebP: 2c8999d566cce1d3fa28edb7c8a0d259938402cd
   KLExtension: f8b2a92125ad4bbfc8920ed5e7269aefcdcaa0b3
@@ -148,6 +159,6 @@ SPEC CHECKSUMS:
   TYCyclePagerView: 2b051dade0615c70784aa34f40c646feeddb7344
   TZImagePickerController: d084a7b97c82d387e7669dd86dc9a9057500aacf
 
-PODFILE CHECKSUM: bca527c6f78cf9ec6fb822611cf787ac22a09933
+PODFILE CHECKSUM: 303a47295259c1155536009c1e316f7497504520
 
 COCOAPODS: 1.16.2

+ 26 - 0
TSLiveWallpaper.xcodeproj/project.pbxproj

@@ -35,6 +35,9 @@
 		A81CA4AA2D16943800A3AAC8 /* TSMineCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81CA4A92D16943200A3AAC8 /* TSMineCell.swift */; };
 		A81CA4B82D16A6BD00A3AAC8 /* View+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81CA4B72D16A6B600A3AAC8 /* View+Ex.swift */; };
 		A81CA4BA2D16B6E300A3AAC8 /* TSHomeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81CA4B92D16B6DC00A3AAC8 /* TSHomeCell.swift */; };
+		A81E81632E24F05E00207EB8 /* TSFusionImageVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81E81622E24F05D00207EB8 /* TSFusionImageVC.swift */; };
+		A81E81652E24F09600207EB8 /* TSAISmallUploadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81E81642E24F09500207EB8 /* TSAISmallUploadView.swift */; };
+		A81E81672E25065C00207EB8 /* TSAIPhotoDetailsVC+Comparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81E81662E25065A00207EB8 /* TSAIPhotoDetailsVC+Comparison.swift */; };
 		A81F5B3C2D19087100740085 /* TSRandomWallpaperCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81F5B3B2D19086B00740085 /* TSRandomWallpaperCell.swift */; };
 		A81F5B3E2D19088100740085 /* TSRandomWallpaperBannerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81F5B3D2D19087600740085 /* TSRandomWallpaperBannerCell.swift */; };
 		A81F5B402D194EA900740085 /* UIDevice+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81F5B3F2D194EA900740085 /* UIDevice+Extension.swift */; };
@@ -87,6 +90,7 @@
 		A84C239C2D1E3A4300B61B55 /* GPVideoClipperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A84C23972D1E3A4300B61B55 /* GPVideoClipperView.swift */; };
 		A84C239D2D1E3A4300B61B55 /* GPVideoConfigMaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A84C23952D1E3A4300B61B55 /* GPVideoConfigMaker.swift */; };
 		A84C239F2D1E88CD00B61B55 /* TSFileManagerTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = A84C239E2D1E88C500B61B55 /* TSFileManagerTool.swift */; };
+		A864C1732E211C8C0077DADF /* TSHXPhotoPickerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A864C1722E211C810077DADF /* TSHXPhotoPickerManager.swift */; };
 		A868577C2DF819BB0089D222 /* TSDBManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868577B2DF819BA0089D222 /* TSDBManager.swift */; };
 		A868577E2DF81A940089D222 /* TSDBActionInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868577D2DF81A930089D222 /* TSDBActionInfoModel.swift */; };
 		A86857822DF81AD40089D222 /* TSActionInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86857812DF81ACF0089D222 /* TSActionInfoModel.swift */; };
@@ -213,6 +217,9 @@
 		A81CA4A92D16943200A3AAC8 /* TSMineCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSMineCell.swift; sourceTree = "<group>"; };
 		A81CA4B72D16A6B600A3AAC8 /* View+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Ex.swift"; sourceTree = "<group>"; };
 		A81CA4B92D16B6DC00A3AAC8 /* TSHomeCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSHomeCell.swift; sourceTree = "<group>"; };
+		A81E81622E24F05D00207EB8 /* TSFusionImageVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSFusionImageVC.swift; sourceTree = "<group>"; };
+		A81E81642E24F09500207EB8 /* TSAISmallUploadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAISmallUploadView.swift; sourceTree = "<group>"; };
+		A81E81662E25065A00207EB8 /* TSAIPhotoDetailsVC+Comparison.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSAIPhotoDetailsVC+Comparison.swift"; sourceTree = "<group>"; };
 		A81F5B3B2D19086B00740085 /* TSRandomWallpaperCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSRandomWallpaperCell.swift; sourceTree = "<group>"; };
 		A81F5B3D2D19087600740085 /* TSRandomWallpaperBannerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSRandomWallpaperBannerCell.swift; sourceTree = "<group>"; };
 		A81F5B3F2D194EA900740085 /* UIDevice+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Extension.swift"; sourceTree = "<group>"; };
@@ -281,6 +288,7 @@
 		A864C16E2E20C3B10077DADF /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/LaunchScreen.strings"; sourceTree = "<group>"; };
 		A864C16F2E20C3B10077DADF /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
 		A864C1702E20C3B10077DADF /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
+		A864C1722E211C810077DADF /* TSHXPhotoPickerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSHXPhotoPickerManager.swift; sourceTree = "<group>"; };
 		A868577B2DF819BA0089D222 /* TSDBManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDBManager.swift; sourceTree = "<group>"; };
 		A868577D2DF81A930089D222 /* TSDBActionInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDBActionInfoModel.swift; sourceTree = "<group>"; };
 		A86857812DF81ACF0089D222 /* TSActionInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSActionInfoModel.swift; sourceTree = "<group>"; };
@@ -645,6 +653,15 @@
 			path = Font;
 			sourceTree = "<group>";
 		};
+		A81E81612E24F05700207EB8 /* TSFusionImageVC */ = {
+			isa = PBXGroup;
+			children = (
+				A81E81642E24F09500207EB8 /* TSAISmallUploadView.swift */,
+				A81E81622E24F05D00207EB8 /* TSFusionImageVC.swift */,
+			);
+			path = TSFusionImageVC;
+			sourceTree = "<group>";
+		};
 		A81F5B3A2D19085F00740085 /* TSRandomWallpaperView */ = {
 			isa = PBXGroup;
 			children = (
@@ -915,6 +932,7 @@
 		A86857A72DF9204B0089D222 /* TSPhotoPickerManager */ = {
 			isa = PBXGroup;
 			children = (
+				A864C1722E211C810077DADF /* TSHXPhotoPickerManager.swift */,
 				A86857A62DF9204B0089D222 /* TSPhotoPickerManager.swift */,
 			);
 			path = TSPhotoPickerManager;
@@ -923,6 +941,7 @@
 		A86857A92DF9210D0089D222 /* TSAIListVC */ = {
 			isa = PBXGroup;
 			children = (
+				A81E81612E24F05700207EB8 /* TSFusionImageVC */,
 				A8F8BCD72E0407C800EF4AA6 /* TSAIListVideoPlayerVC */,
 				A86857D82DF9943E0089D222 /* TSAIPhotoDetailsVC */,
 				A86857D42DF97A2A0089D222 /* TSAIExpandChangeView.swift */,
@@ -1157,6 +1176,7 @@
 			isa = PBXGroup;
 			children = (
 				A86857D92DF9945F0089D222 /* TSAIPhotoDetailsVC.swift */,
+				A81E81662E25065A00207EB8 /* TSAIPhotoDetailsVC+Comparison.swift */,
 				A8F8BCE22E0423AD00EF4AA6 /* TSAIPhotoDetailsVC+View.swift */,
 				A8F8BCDD2E0420EB00EF4AA6 /* TSAIPhotoDetailsVC+Image.swift */,
 				A8F8BCDF2E0420FB00EF4AA6 /* TSAIPhotoDetailsVC+Video.swift */,
@@ -1361,6 +1381,7 @@
 				A8FD8F382DFBD660008CAACF /* TSMineTopView.swift in Sources */,
 				A81CA4832D157F5C00A3AAC8 /* UIImageView+Ex.swift in Sources */,
 				A81F5B492D1956EA00740085 /* UIScreen.swift in Sources */,
+				A81E81632E24F05E00207EB8 /* TSFusionImageVC.swift in Sources */,
 				A8EE92CD2DFFF2860077DFFD /* TSBaseOperation.swift in Sources */,
 				A8F8BCCF2E03F8BC00EF4AA6 /* TSAIUploadPhotoTextView.swift in Sources */,
 				A8EE92CE2DFFF2860077DFFD /* TSGenerateBaseOperation.swift in Sources */,
@@ -1375,6 +1396,7 @@
 				A895B5002E01370E004F9B85 /* TSTextToastView.swift in Sources */,
 				A81F5B5B2D1A5F2300740085 /* TSHomeTopBannerCell.swift in Sources */,
 				A83F288F2E162B7C009A4975 /* TSAILIstStyleMoreCell.swift in Sources */,
+				A81E81672E25065C00207EB8 /* TSAIPhotoDetailsVC+Comparison.swift in Sources */,
 				A83946312D1D66A000ABFF0D /* TSTermsServiceVC.swift in Sources */,
 				A8F8BCD52E03FE5000EF4AA6 /* TSAIUploadPhotoVC+View.swift in Sources */,
 				A8C4C0982D242154003C46FC /* LivePhoto.swift in Sources */,
@@ -1412,6 +1434,7 @@
 				A81CA49F2D1655CE00A3AAC8 /* UICollectionView+Ex.swift in Sources */,
 				A81CA4BA2D16B6E300A3AAC8 /* TSHomeCell.swift in Sources */,
 				A87CF8542E029B450063CB7E /* TSAIPhotoDetailsBrowserCell.swift in Sources */,
+				A864C1732E211C8C0077DADF /* TSHXPhotoPickerManager.swift in Sources */,
 				A8F778B22D1BA07200BF55D5 /* TSRandomWallpaperBrowseSelectView.swift in Sources */,
 				A8477C9F2D22ABDA00DF0B93 /* TSEditLiveEidtCell.swift in Sources */,
 				A81CA4652D15685F00A3AAC8 /* TSLaunchVC.swift in Sources */,
@@ -1489,6 +1512,7 @@
 				A81CA48F2D15857B00A3AAC8 /* TSTabBarController.swift in Sources */,
 				A8C4C0A52D24218A003C46FC /* Converter4Video.swift in Sources */,
 				A8C4C0A62D24218A003C46FC /* AVAssetExtension.swift in Sources */,
+				A81E81652E24F09600207EB8 /* TSAISmallUploadView.swift in Sources */,
 				A8C4C0A72D24218A003C46FC /* LivePhotoUtil.m in Sources */,
 				A8F8BCDE2E0420EE00EF4AA6 /* TSAIPhotoDetailsVC+Image.swift in Sources */,
 				A86857AC2DF921160089D222 /* TSAIListHintBaseVC.swift in Sources */,
@@ -1580,6 +1604,7 @@
 				GENERATE_INFOPLIST_FILE = YES;
 				INFOPLIST_FILE = TSLiveWallpaper/Info.plist;
 				INFOPLIST_KEY_CFBundleDisplayName = "Old Photo";
+				INFOPLIST_KEY_NSCameraUsageDescription = "是否允许此App使用你的相机";
 				INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "This app needs to access your location information while you're using it to provide relevant location services and features. We promise to only obtain your location information when you're using the app and to use it solely for providing services and features.";
 				INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "Allow us to access Photos in order to save wallpapers to your device.";
 				INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
@@ -1620,6 +1645,7 @@
 				GENERATE_INFOPLIST_FILE = YES;
 				INFOPLIST_FILE = TSLiveWallpaper/Info.plist;
 				INFOPLIST_KEY_CFBundleDisplayName = "Old Photo";
+				INFOPLIST_KEY_NSCameraUsageDescription = "是否允许此App使用你的相机";
 				INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "This app needs to access your location information while you're using it to provide relevant location services and features. We promise to only obtain your location information when you're using the app and to use it solely for providing services and features.";
 				INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "Allow us to access Photos in order to save wallpapers to your device.";
 				INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;

BIN
TSLiveWallpaper/Assets.xcassets/.DS_Store


+ 22 - 0
TSLiveWallpaper/Assets.xcassets/AIList/add.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "add@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "add@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
TSLiveWallpaper/Assets.xcassets/AIList/add.imageset/add@2x.png


BIN
TSLiveWallpaper/Assets.xcassets/AIList/add.imageset/add@3x.png


+ 22 - 0
TSLiveWallpaper/Assets.xcassets/AIList/aiList_PortraitFusion.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "aiList_PortraitFusion@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "aiList_PortraitFusion@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
TSLiveWallpaper/Assets.xcassets/AIList/aiList_PortraitFusion.imageset/aiList_PortraitFusion@2x.png


BIN
TSLiveWallpaper/Assets.xcassets/AIList/aiList_PortraitFusion.imageset/aiList_PortraitFusion@3x.png


+ 22 - 0
TSLiveWallpaper/Assets.xcassets/Hint/hint_PortraitFusion_good.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "hint_PortraitFusion_good@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "hint_PortraitFusion_good@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
TSLiveWallpaper/Assets.xcassets/Hint/hint_PortraitFusion_good.imageset/hint_PortraitFusion_good@2x.png


BIN
TSLiveWallpaper/Assets.xcassets/Hint/hint_PortraitFusion_good.imageset/hint_PortraitFusion_good@3x.png


BIN
TSLiveWallpaper/Assets.xcassets/Music/.DS_Store


+ 21 - 19
TSLiveWallpaper/Business/TSAIListVC/TSAIAgeImageHintVC/TSAIListHintBaseVC.swift

@@ -23,7 +23,8 @@ class TSAIListHintBaseVC: TSBaseVC {
         var badText:String
         var badInfoText:String
         
-        init(imageMaxBitSize: Int, goodImageNamed: String, badImageNamed: String, titleText: String, titleSubText: String, goodText: String, goodInfoText: String, badText: String, badInfoText: String) {
+        var btnText:String
+        init(imageMaxBitSize: Int, goodImageNamed: String, badImageNamed: String, titleText: String, titleSubText: String, goodText: String, goodInfoText: String, badText: String, badInfoText: String,btnText:String = "Upload Photo".localized) {
             self.imageMaxBitSize = imageMaxBitSize
             self.goodImageNamed = goodImageNamed
             self.badImageNamed = badImageNamed
@@ -33,6 +34,7 @@ class TSAIListHintBaseVC: TSBaseVC {
             self.goodInfoText = goodInfoText
             self.badText = badText
             self.badInfoText = badInfoText
+            self.btnText = btnText
         }
 
         static var enhanceConfig:Config {
@@ -107,6 +109,21 @@ class TSAIListHintBaseVC: TSBaseVC {
                           badText: "Unsuitable".localized,
                           badInfoText: "B&W or Severely damaged photos".localized)
         }
+        
+        static var portraitFusionConfig:Config {
+            return Config(imageMaxBitSize: kUploadImageMaxBit10Size,
+                          goodImageNamed: "hint_PortraitFusion_good",
+                          badImageNamed: "hint_Animated_bad",
+                          titleText: "Portrait Fusion Tips".localized,
+                          titleSubText: "",
+                          goodText: "Suitable".localized,
+                          goodInfoText: "Portraits,faces should be visible".localized,
+                          badText: "Unsuitable".localized,
+                          badInfoText: "B&W or Severely damaged photos".localized,
+                          btnText:String(format: "Select %d photos".localized, 2)
+            )
+        }
+        
     }
     
     var config:Config = Config.enhanceConfig
@@ -127,13 +144,8 @@ class TSAIListHintBaseVC: TSBaseVC {
         return popupContentView
     }()
     
-    lazy var photoPickerManager: TSPhotoPickerManager = {
-        let photoPickerManager = TSPhotoPickerManager(viewController: self)
-        return photoPickerManager
-    }()
-    
     lazy var submitBtn: UIButton = {
-        let submitBtn = kCreateNormalSubmitBtn(title: "Upload Photo".localized) { [weak self]  in
+        let submitBtn = kCreateNormalSubmitBtn(title: config.btnText) { [weak self]  in
             guard let self = self else { return }
             pickSinglePhoto()
         }
@@ -259,21 +271,11 @@ class TSAIListHintBaseVC: TSBaseVC {
     }
     
     func pickSinglePhoto()  {
-        photoPickerManager.pickCustomSinglePhoto() { [weak self] image, errorString in
-            guard let self = self else { return }
-            if let errorString = errorString {
-                TSToastShared.showToast(text: errorString)
-            }else{
-                self.clickUpImageHandle?(image)
-            }
-        }
-        
-  
+        self.clickUpImageHandle?(nil)
     }
     
     func dismissPageVC(){
         self.view.isHidden = true
-        self.photoPickerManager.dismissPageVC()
-        self.dismiss(animated: true)
+        self.dismiss(animated: false)
     }
 }

+ 13 - 0
TSLiveWallpaper/Business/TSAIListVC/TSAIList+Enmu.swift

@@ -16,6 +16,8 @@ enum TSGeneratorImageStyle:String {
     case remove = "remove"      //删除图片的东西
     case ptp = "ptp"
     
+    case portraitFusion = "PortraitFusion"
+    
     var imageMaxKb:Int{
         switch self {
         case .descratch:
@@ -57,6 +59,8 @@ enum TSGeneratorImageStyle:String {
             return .recreateConfig
         case .creatVideo:
             return .animatedConfig
+        case .portraitFusion:
+            return .portraitFusionConfig
         default:
             return .animatedConfig
         }
@@ -87,10 +91,19 @@ enum TSGeneratorImageStyle:String {
             return "Animated Photo".localized
         case .remove:
             return "Remove".localized
+        case .portraitFusion:
+            return "Fusion".localized
         default:
             return "Photo".localized
         }
     }
+    
+    var needPhotos:Int {
+        if self == .portraitFusion {
+            return 2
+        }
+        return 1
+    }
 }
 
 extension TSGeneratorImageStyle {

+ 35 - 34
TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/TSAIListVC.swift

@@ -186,63 +186,64 @@ extension TSAIListVC {
 //    }
 //    
     static func clickCell(target:UIViewController,indexPath:IndexPath,itemModel:TSDiscoverItemModel,secModel:TSDiscoverSectionModel) {
-
         if kShowGenerateBusyView(view: target.view) { return }
-        Self.enterSelectPhotos(target: target,style: itemModel.style) { image in
+        Self.enterSelectPhotos(target: target,style: itemModel.style) { images in
             if itemModel.style == .remove {
-                kPushVC(target: target, modelVC: TSAIRemovePhotlVC(titleString: itemModel.name,upLoadImage: image, generatorStyle: itemModel.style,disCoverItemModel: itemModel))
-                return
+                target.push(TSAIRemovePhotlVC(titleString: itemModel.name,upLoadImage: images.first!, generatorStyle: itemModel.style,disCoverItemModel: itemModel),animated: false) {
+                    closeSelectPhotos()
+                }
+            }else if itemModel.style == .portraitFusion {
+                target.push(TSFusionImageVC(titleString: itemModel.name,upLoadImages: images, generatorStyle: itemModel.style,disCoverItemModel: itemModel),animated: false) {
+                    closeSelectPhotos()
+                }
+            }else{
+                target.push(TSAIUploadPhotoVC(titleString: itemModel.name,upLoadImage: images.first!, generatorStyle: itemModel.style,disCoverItemModel: itemModel),animated: false) {
+                    closeSelectPhotos()
+                }
             }
-
-            kPushVC(target: target, modelVC: TSAIUploadPhotoVC(titleString: itemModel.name,upLoadImage: image, generatorStyle: itemModel.style,disCoverItemModel: itemModel))
         }
     }
 }
 
 extension TSAIListVC{
-    static func enterSelectPhotos(target:UIViewController,style:TSGeneratorImageStyle,complete: @escaping (UIImage)->Void){
+    static var hintBaseVC:TSAIListHintBaseVC?
+    static var photoPickerManager:TSPhotoPickerManager?
+    
+    static func enterSelectPhotos(target:UIViewController,style:TSGeneratorImageStyle,complete: @escaping ([UIImage])->Void){
         if style.userDefaultsKey.count == 0 {
-            self.pickSinglePhoto(target:target,complete:complete)
+            self.pickSinglePhoto(target:target,style:style,complete:complete)
         }else{
             TSAIListHintBaseVC.userDefaultsKey = style.userDefaultsKey
             if TSAIListHintBaseVC.isShowUploadImageHint{
                 TSAIListHintBaseVC.isShowUploadImageHint = false
-                self.presentModalHintVC(target:target,config:style.config,complete:complete)
+                self.presentModalHintVC(target:target,style:style,complete:complete)
             }else {
-                self.pickSinglePhoto(target:target,complete:complete)
+                self.pickSinglePhoto(target:target,style:style,complete:complete)
             }
         }
     }
 
-    static func presentModalHintVC(target:UIViewController,config:TSAIListHintBaseVC.Config,complete:@escaping (UIImage)->Void){
-        let hintBaseVC = TSAIListHintBaseVC(config: config)
+    static func presentModalHintVC(target:UIViewController,style:TSGeneratorImageStyle,complete:@escaping ([UIImage])->Void){
+        let hintBaseVC = TSAIListHintBaseVC(config: style.config)
         hintBaseVC.clickUpImageHandle = { image in
-            if let image = image {
-                complete(image)
-            }else{
-                dePrint("图片异常")
-            }
-            kDelayMainShort {
-                hintBaseVC.dismissPageVC()
-            }
+            pickSinglePhoto(target: Self.hintBaseVC!, style: style, complete: complete)
         }
         kPresentModalVC(target: target, modelVC: hintBaseVC,transitionStyle: .crossDissolve)
+        Self.hintBaseVC = hintBaseVC
     }
-    
-    static func pickSinglePhoto(target:UIViewController,complete: @escaping (UIImage)->Void)  {
+
+    static func pickSinglePhoto(target:UIViewController,style:TSGeneratorImageStyle,complete: @escaping ([UIImage])->Void)  {
         let photoPickerManager = TSPhotoPickerManager(viewController: target)
-        photoPickerManager.pickCustomSinglePhoto() { image, errorString in
-            if let errorString = errorString {
-                TSToastShared.showToast(text: errorString)
-            }else if let image = image {
-                complete(image)
-            }else{
-                dePrint("图片异常")
-            }
-            kDelayMainShort {
-                photoPickerManager.dismissPageVC()
-            }
-            
+        photoPickerManager.pickPhoto(maxSelected: style.needPhotos) {images in
+            complete(images)
         }
+        Self.photoPickerManager = photoPickerManager
+    }
+    
+    static func closeSelectPhotos(){
+        photoPickerManager?.dismissPageVC()
+        hintBaseVC?.dismissPageVC()
+        hintBaseVC = nil
+        photoPickerManager = nil
     }
 }

+ 186 - 192
TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/TSAIListVM.swift

@@ -5,195 +5,189 @@
 //  Created by 100Years on 2025/6/16.
 //
 
-class TSAIListVM {
-    
-    init(target: UIViewController) {
-        self.target = target
-    }
-    
-    var target:UIViewController
-    var hintBaseVC:TSAIListHintBaseVC = TSAIListHintBaseVC(config: .descratchConfig)
-    
-    lazy var photoPickerManager: TSPhotoPickerManager = {
-        let photoPickerManager = TSPhotoPickerManager(viewController: self.target)
-        return photoPickerManager
-    }()
-
-    lazy var dataArray: [TSBasicSectionModel] = {
-        var dataArray = [TSBasicSectionModel]()
-        let sectionModel = TSBasicSectionModel()
-        dataArray.append(sectionModel)
-    
-        //黑白照片变颜色
-        sectionModel.addSubItemModel(
-            createItemModel(
-                leftImageName:"aiList_Colorize",
-                leftTitle: "Colorize".localized,
-                leftSubTitle: "Add colors to black-and-white photos".localized,
-                rightViewStyle: 0,
-                tapBlock: { [weak self] model, _, _ in
-                   guard let self = self else { return }
-                    enterSelectPhotos(style: .colorize) { image in
-                    let baseVc = TSAIUploadPhotoVC(titleString: model.leftTitle ?? "",upLoadImage: image, generatorStyle: .colorize)
-                        kPushVC(target: self.target, modelVC: baseVc)
-                    }
-        }))
-        
-        //老照片修复
-        sectionModel.addSubItemModel(
-            createItemModel(
-                leftImageName:"aiList_Descratch",
-                leftTitle: "Descratch".localized,
-                leftSubTitle: "Remove scratches and dirt".localized,
-                rightViewStyle: 0,
-                tapBlock: { [weak self] model, _, _ in
-                   guard let self = self else { return }
-                    enterSelectPhotos(style: .descratch) { image in
-                        let baseVc = TSAIUploadPhotoVC(titleString: model.leftTitle ?? "",upLoadImage: image, generatorStyle: .descratch)
-                        kPushVC(target: self.target, modelVC: baseVc)
-                    }
-        }))
-        
-        
-        //视频照片
-        let animatedItemModel = createItemModel(
-            leftImageName:"aiList_Animated",
-            leftTitle: "Animated Photo".localized,
-            leftSubTitle: "Turn photos into live moments".localized,
-            rightViewStyle: 0,
-            tapBlock: { [weak self] model, _, _ in
-               guard let self = self else { return }
-                enterSelectPhotos(style: .creatVideo) { image in
-                    let baseVc = TSAIUploadPhotoVC(titleString: model.leftTitle ?? "",upLoadImage: image, generatorStyle: .creatVideo)
-                    kPushVC(target: self.target, modelVC: baseVc)
-                }
-            })
-        
-        animatedItemModel.leftImagePath = "animated_example.gif"
-//        animatedItemModel.leftImagePath = "animated_photos.webp"
-        sectionModel.addSubItemModel(animatedItemModel)
-
-        
-        //照片高清修复
-        sectionModel.addSubItemModel(
-            createItemModel(
-                leftImageName:"aiList_Enhance",
-                leftTitle: "Enhance".localized,
-                leftSubTitle: "Remove blur, sharpen, add details".localized,
-                rightViewStyle: 0,
-                tapBlock: { [weak self] model, _, _ in
-                   guard let self = self else { return }
-                    enterSelectPhotos(style: .enhance) { image in
-                    let baseVc = TSAIUploadPhotoVC(titleString: model.leftTitle ?? "",upLoadImage: image, generatorStyle: .enhance)
-                        kPushVC(target: self.target, modelVC: baseVc)
-                    }
-        }))
-        
-        //修复和上色老照片
-        sectionModel.addSubItemModel(
-            createItemModel(
-                leftImageName:"aiList_Recreate",
-                leftTitle: "Recreate".localized,
-                leftSubTitle: "Bring new life to old photos".localized,
-                rightViewStyle: 0,
-                tapBlock: { [weak self] model, _, _ in
-                   guard let self = self else { return }
-                    enterSelectPhotos(style: .recreate) { image in
-                    let baseVc = TSAIUploadPhotoVC(titleString: model.leftTitle ?? "",upLoadImage: image, generatorStyle: .recreate)
-                        kPushVC(target: self.target, modelVC: baseVc)
-                    }
-        }))
-        
-        //调整光线
-        sectionModel.addSubItemModel(
-            createItemModel(
-                leftImageName:"aiList_Enlighten",
-                leftTitle: "Adjust Light".localized,
-                leftSubTitle: "Easily fix lighting issues on photos".localized,
-                rightViewStyle: 0,
-                tapBlock: { [weak self] model, _, _ in
-                   guard let self = self else { return }
-                    enterSelectPhotos(style: .enlighten) { image in
-                    let baseVc = TSAIUploadPhotoVC(titleString: model.leftTitle ?? "",upLoadImage: image, generatorStyle: .enlighten)
-                        kPushVC(target: self.target, modelVC: baseVc)
-                    }
-        }))
-        
-
-        return dataArray
-
-    }()
-    
-    
-}
-
-extension TSAIListVM {
-
-    func createItemModel(leftImageName: String,
-                         leftTitle: String,
-                         leftSubTitle: String,
-                        rightViewStyle: Int,
-                        tapBlock: @escaping ((TSBasicItemModel, Int, Any?) -> Void)) -> TSBasicItemModel {
-        let model = TSBasicItemModel()
-        model.leftImageName = leftImageName
-        model.leftTitle = leftTitle
-        model.leftSubTitle = leftSubTitle
-        model.rightViewStyle = rightViewStyle
-        model.tapBlock = tapBlock
-        return model
-    }
-}
-
-extension TSAIListVM{
-    
-    func enterSelectPhotos(style:TSGeneratorImageStyle,complete: @escaping (UIImage)->Void){
-        if style.userDefaultsKey.isEmpty{
-            self.pickSinglePhoto(maxBitSize:style.imageMaxBitSize,complete:complete)
-        }else{
-            TSAIListHintBaseVC.userDefaultsKey = style.userDefaultsKey
-            if TSAIListHintBaseVC.isShowUploadImageHint{
-                TSAIListHintBaseVC.isShowUploadImageHint = false
-                self.presentModalHintVC(config:style.config,complete:complete)
-            }else {
-                self.pickSinglePhoto(maxBitSize:style.imageMaxBitSize,complete:complete)
-            }
-        }
-    }
-    
-    
-    func presentModalHintVC(config:TSAIListHintBaseVC.Config,complete:@escaping (UIImage)->Void){
-        hintBaseVC = TSAIListHintBaseVC(config: config) { [weak self] image in
-            guard let self = self else { return }
-            if let image = image {
-                complete(image)
-            }else{
-                dePrint("图片异常")
-            }
-            kDelayMainShort {
-                self.hintBaseVC.dismissPageVC()
-            }
-        }
- 
-        kPresentModalVC(target: self.target, modelVC: hintBaseVC,transitionStyle: .crossDissolve)
-    }
-    
-    func pickSinglePhoto(maxBitSize:Int,complete: @escaping (UIImage)->Void)  {
-//        photoPickerManager.pickSinglePhoto(maxBitSize: maxBitSize) { [weak self] image, errorString in
-        photoPickerManager.pickCustomSinglePhoto() { [weak self] image, errorString in
-            guard let self = self else { return }
-            if let errorString = errorString {
-                TSToastShared.showToast(text: errorString)
-            }else if let image = image {
-                complete(image)
-            }else{
-                dePrint("图片异常")
-            }
-            kDelayMainShort {
-                self.photoPickerManager.dismissPageVC()
-            }
-            
-        }
-    }
-}
-
-
+//class TSAIListVM {
+//    
+//    init(target: UIViewController) {
+//        self.target = target
+//    }
+//    
+//    var target:UIViewController
+//    var hintBaseVC:TSAIListHintBaseVC = TSAIListHintBaseVC(config: .descratchConfig)
+//    
+//    lazy var photoPickerManager: TSPhotoPickerManager = {
+//        let photoPickerManager = TSPhotoPickerManager(viewController: self.target)
+//        return photoPickerManager
+//    }()
+//
+//    lazy var dataArray: [TSBasicSectionModel] = {
+//        var dataArray = [TSBasicSectionModel]()
+//        let sectionModel = TSBasicSectionModel()
+//        dataArray.append(sectionModel)
+//    
+//        //黑白照片变颜色
+//        sectionModel.addSubItemModel(
+//            createItemModel(
+//                leftImageName:"aiList_Colorize",
+//                leftTitle: "Colorize".localized,
+//                leftSubTitle: "Add colors to black-and-white photos".localized,
+//                rightViewStyle: 0,
+//                tapBlock: { [weak self] model, _, _ in
+//                   guard let self = self else { return }
+//                    enterSelectPhotos(style: .colorize) { image in
+//                    let baseVc = TSAIUploadPhotoVC(titleString: model.leftTitle ?? "",upLoadImage: image, generatorStyle: .colorize)
+//                        kPushVC(target: self.target, modelVC: baseVc)
+//                    }
+//        }))
+//        
+//        //老照片修复
+//        sectionModel.addSubItemModel(
+//            createItemModel(
+//                leftImageName:"aiList_Descratch",
+//                leftTitle: "Descratch".localized,
+//                leftSubTitle: "Remove scratches and dirt".localized,
+//                rightViewStyle: 0,
+//                tapBlock: { [weak self] model, _, _ in
+//                   guard let self = self else { return }
+//                    enterSelectPhotos(style: .descratch) { image in
+//                        let baseVc = TSAIUploadPhotoVC(titleString: model.leftTitle ?? "",upLoadImage: image, generatorStyle: .descratch)
+//                        kPushVC(target: self.target, modelVC: baseVc)
+//                    }
+//        }))
+//        
+//        
+//        //视频照片
+//        let animatedItemModel = createItemModel(
+//            leftImageName:"aiList_Animated",
+//            leftTitle: "Animated Photo".localized,
+//            leftSubTitle: "Turn photos into live moments".localized,
+//            rightViewStyle: 0,
+//            tapBlock: { [weak self] model, _, _ in
+//               guard let self = self else { return }
+//                enterSelectPhotos(style: .creatVideo) { image in
+//                    let baseVc = TSAIUploadPhotoVC(titleString: model.leftTitle ?? "",upLoadImage: image, generatorStyle: .creatVideo)
+//                    kPushVC(target: self.target, modelVC: baseVc)
+//                }
+//            })
+//        
+//        animatedItemModel.leftImagePath = "animated_example.gif"
+////        animatedItemModel.leftImagePath = "animated_photos.webp"
+//        sectionModel.addSubItemModel(animatedItemModel)
+//
+//        
+//        //照片高清修复
+//        sectionModel.addSubItemModel(
+//            createItemModel(
+//                leftImageName:"aiList_Enhance",
+//                leftTitle: "Enhance".localized,
+//                leftSubTitle: "Remove blur, sharpen, add details".localized,
+//                rightViewStyle: 0,
+//                tapBlock: { [weak self] model, _, _ in
+//                   guard let self = self else { return }
+//                    enterSelectPhotos(style: .enhance) { image in
+//                    let baseVc = TSAIUploadPhotoVC(titleString: model.leftTitle ?? "",upLoadImage: image, generatorStyle: .enhance)
+//                        kPushVC(target: self.target, modelVC: baseVc)
+//                    }
+//        }))
+//        
+//        //修复和上色老照片
+//        sectionModel.addSubItemModel(
+//            createItemModel(
+//                leftImageName:"aiList_Recreate",
+//                leftTitle: "Recreate".localized,
+//                leftSubTitle: "Bring new life to old photos".localized,
+//                rightViewStyle: 0,
+//                tapBlock: { [weak self] model, _, _ in
+//                   guard let self = self else { return }
+//                    enterSelectPhotos(style: .recreate) { image in
+//                    let baseVc = TSAIUploadPhotoVC(titleString: model.leftTitle ?? "",upLoadImage: image, generatorStyle: .recreate)
+//                        kPushVC(target: self.target, modelVC: baseVc)
+//                    }
+//        }))
+//        
+//        //调整光线
+//        sectionModel.addSubItemModel(
+//            createItemModel(
+//                leftImageName:"aiList_Enlighten",
+//                leftTitle: "Adjust Light".localized,
+//                leftSubTitle: "Easily fix lighting issues on photos".localized,
+//                rightViewStyle: 0,
+//                tapBlock: { [weak self] model, _, _ in
+//                   guard let self = self else { return }
+//                    enterSelectPhotos(style: .enlighten) { image in
+//                    let baseVc = TSAIUploadPhotoVC(titleString: model.leftTitle ?? "",upLoadImage: image, generatorStyle: .enlighten)
+//                        kPushVC(target: self.target, modelVC: baseVc)
+//                    }
+//        }))
+//        
+//
+//        return dataArray
+//
+//    }()
+//    
+//    
+//}
+//
+//extension TSAIListVM {
+//
+//    func createItemModel(leftImageName: String,
+//                         leftTitle: String,
+//                         leftSubTitle: String,
+//                        rightViewStyle: Int,
+//                        tapBlock: @escaping ((TSBasicItemModel, Int, Any?) -> Void)) -> TSBasicItemModel {
+//        let model = TSBasicItemModel()
+//        model.leftImageName = leftImageName
+//        model.leftTitle = leftTitle
+//        model.leftSubTitle = leftSubTitle
+//        model.rightViewStyle = rightViewStyle
+//        model.tapBlock = tapBlock
+//        return model
+//    }
+//}
+//
+//extension TSAIListVM{
+//    
+//    func enterSelectPhotos(style:TSGeneratorImageStyle,complete: @escaping (UIImage)->Void){
+//        if style.userDefaultsKey.isEmpty{
+//            self.pickSinglePhoto(maxBitSize:style.imageMaxBitSize,complete:complete)
+//        }else{
+//            TSAIListHintBaseVC.userDefaultsKey = style.userDefaultsKey
+//            if TSAIListHintBaseVC.isShowUploadImageHint{
+//                TSAIListHintBaseVC.isShowUploadImageHint = false
+//                self.presentModalHintVC(config:style.config,complete:complete)
+//            }else {
+//                self.pickSinglePhoto(maxBitSize:style.imageMaxBitSize,complete:complete)
+//            }
+//        }
+//    }
+//    
+//    
+//    func presentModalHintVC(config:TSAIListHintBaseVC.Config,complete:@escaping (UIImage)->Void){
+//        hintBaseVC = TSAIListHintBaseVC(config: config) { [weak self] image in
+//            guard let self = self else { return }
+//            if let image = image {
+//                complete(image)
+//            }else{
+//                dePrint("图片异常")
+//            }
+//            kDelayMainShort {
+//                self.hintBaseVC.dismissPageVC()
+//            }
+//        }
+// 
+//        kPresentModalVC(target: self.target, modelVC: hintBaseVC,transitionStyle: .crossDissolve)
+//    }
+//    
+//    func pickSinglePhoto(maxBitSize:Int,complete: @escaping (UIImage)->Void)  {
+//        photoPickerManager.pickPhoto() { [weak self] images in
+//            guard let self = self else { return }
+//            if let image = images.first {
+//                complete(image)
+//            }else{
+//                dePrint("图片异常")
+//            }
+//            self.photoPickerManager.dismissPageVC()
+//        }
+//    }
+//}
+//
+//

+ 1 - 3
TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/VM/TSAIListDataVM+Click.swift

@@ -48,9 +48,7 @@
 //            }else{
 //                dePrint("图片异常")
 //            }
-//            kDelayMainShort {
-//                self.photoPickerManager.dismissPageVC()
-//            }
+//            self.photoPickerManager.dismissPageVC()
 //            
 //        }
 //    }

+ 12 - 0
TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/VM/TSAIListDataVM+Dic.swift

@@ -173,4 +173,16 @@ extension TSAIListDataVM {
         ]
     }
     
+    var ptp_PortraitFusion:[String:Any]{
+        [
+            imageName: "",
+           imageText: "Portrait Fusion",
+           prompt:"Create a single realistic group photo by merging two input images. Include every unique person and animal exactly once — no duplicates. Preserve each individual's original facial features, clothing, posture, and identity. Arrange everyone naturally in one scene, with realistic body positioning, consistent perspective, lighting, and background. The final image should look like a genuine group photo taken together. Do not remove anyone or add extra copies of any subject. All original faces and animals must be clearly visible and only appear once.",
+            specialStyle:0,
+            isVip: true,
+            unionType: 1,
+            model:kie
+        ]
+    }
+    
 }

+ 16 - 1
TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/VM/TSAIListDataVM.swift

@@ -47,7 +47,21 @@ class TSAIListDataVM {
         return section
     }()
     
-    
+    lazy var portraitSection: TSDiscoverSectionModel = {
+        let section = TSDiscoverSectionModel()
+        section.style = .fullCard
+        section.items = [
+            TSDiscoverItemModel(style: .portraitFusion,
+                                viewModel: TSDiscoverBaseItemVM(
+                                    title: "Portrait Fusion".localized,
+                                    imageNamed: "aiList_PortraitFusion",
+                                    info: "Merge two portraits into one unique image seamlessly".localized
+                                ),
+                                generateModel: TSGenerateModel(json: ptp_PortraitFusion)),
+        ]
+        
+        return section
+    }()
     
     lazy var colorizeSection: TSDiscoverSectionModel = {
         let section = TSDiscoverSectionModel()
@@ -167,6 +181,7 @@ class TSAIListDataVM {
     lazy var listDatas: [TSDiscoverSectionModel] = {
         return [
             videoSection,
+            portraitSection,
             colorizeSection,
             Descratchection,
 //            creatVideoSection,

+ 54 - 0
TSLiveWallpaper/Business/TSAIListVC/TSAIPhotoDetailsVC/TSAIPhotoDetailsVC/TSAIPhotoDetailsVC+Comparison.swift

@@ -0,0 +1,54 @@
+//
+//  TSAIPhotoDetailsVC+comparison.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2025/7/14.
+//
+
+
+extension TSAIPhotoDetailsVC {
+    
+    
+    func setUpPhotoSomeView(){
+        contentView.addSubview(panComparisonView)
+        panComparisonView.snp.makeConstraints { make in
+            make.height.equalTo(k_ScreenHeight)
+            make.leading.trailing.equalTo(0)
+            make.centerY.equalToSuperview()
+        }
+    }
+    
+    
+    func uploadPanComparisonView(){
+        guard let infoModel = infoModel else { return }
+        DispatchQueue.global(qos: .userInitiated).async {
+            var oldImage:UIImage?
+            var newImage:UIImage?
+            let grounp = DispatchGroup()
+            grounp.enter()
+            TSImageStoreTool.downloadImageWithProgress(urlString: infoModel.request.imageUrl) { image in
+                grounp.leave()
+                oldImage = image
+                
+            }
+            
+            grounp.enter()
+            TSImageStoreTool.downloadImageWithProgress(urlString: infoModel.response.resultUrl) { image in
+                grounp.leave()
+                newImage = image
+            }
+            
+            grounp.notify(queue: .main) {
+                if let oldImage = oldImage,let newImage = newImage {
+                    let size = oldImage.size.height > newImage.size.height ? oldImage.size : newImage.size
+                    self.panComparisonView.snp.updateConstraints { make in
+                        make.height.equalTo(kGetUIWdith(designSize: size, currentW: k_ScreenWidth))
+                    }
+                }
+                
+                self.panComparisonView.configure(oldImage: oldImage, newImage: newImage)
+            }
+        }
+    }
+    
+}

+ 18 - 34
TSLiveWallpaper/Business/TSAIListVC/TSAIPhotoDetailsVC/TSAIPhotoDetailsVC/TSAIPhotoDetailsVC+Image.swift

@@ -1,16 +1,26 @@
 //
-//  Untitled.swift
+//  TSAIPhotoDetailsVC.swift
 //  TSLiveWallpaper
 //
 //  Created by 100Years on 2025/6/19.
 //
 
+
+
 extension TSAIPhotoDetailsVC {
     
+    func creatNetWorkImageView() -> UIImageView {
+        let netWorkImageView = UIImageView(frame: CGRectMake(0, 0, k_ScreenWidth, k_ScreenHeight-bottomViewH))
+        netWorkImageView.backgroundColor = "#111111".uiColor
+        netWorkImageView.contentMode = .scaleAspectFit
+        netWorkImageView.isUserInteractionEnabled = true
+        return netWorkImageView
+    }
+    
     
-    func setUpPhotoSomeView(){
-        contentView.addSubview(panComparisonView)
-        panComparisonView.snp.makeConstraints { make in
+    func setUpNetWorkImageView(){
+        contentView.addSubview(netWorkImageView)
+        netWorkImageView.snp.makeConstraints { make in
             make.height.equalTo(k_ScreenHeight)
             make.leading.trailing.equalTo(0)
             make.centerY.equalToSuperview()
@@ -18,36 +28,10 @@ extension TSAIPhotoDetailsVC {
     }
     
     
-    func uploadPanComparisonView(){
+    
+    func updateNetWorkImageView(){
         guard let infoModel = infoModel else { return }
-        DispatchQueue.global(qos: .userInitiated).async {
-            var oldImage:UIImage?
-            var newImage:UIImage?
-            let grounp = DispatchGroup()
-            grounp.enter()
-            TSImageStoreTool.downloadImageWithProgress(urlString: infoModel.request.imageUrl) { image in
-                grounp.leave()
-                oldImage = image
-                
-            }
-            
-            grounp.enter()
-            TSImageStoreTool.downloadImageWithProgress(urlString: infoModel.response.resultUrl) { image in
-                grounp.leave()
-                newImage = image
-            }
-            
-            grounp.notify(queue: .main) {
-                if let oldImage = oldImage,let newImage = newImage {
-                    let size = oldImage.size.height > newImage.size.height ? oldImage.size : newImage.size
-                    self.panComparisonView.snp.updateConstraints { make in
-                        make.height.equalTo(kGetUIWdith(designSize: size, currentW: k_ScreenWidth))
-                    }
-                }
-                
-                self.panComparisonView.configure(oldImage: oldImage, newImage: newImage)
-            }
-        }
+        self.netWorkImageView.setAsyncImage(urlString: infoModel.response.resultUrl,placeholder:kPlaceholderImage,backgroundColor:netWorkImageView.backgroundColor!)
     }
-    
 }
+

+ 4 - 1
TSLiveWallpaper/Business/TSAIListVC/TSAIPhotoDetailsVC/TSAIPhotoDetailsVC/TSAIPhotoDetailsVC.swift

@@ -17,7 +17,8 @@ class TSAIPhotoDetailsVC: TSBaseVC {
     lazy var panComparisonView : TSImageIPanComparisonView = TSImageIPanComparisonView()
     var videoPlayerVC: TSAIListVideoPlayerVC = TSAIListVideoPlayerVC(videoURL: URL(string: "www.baidu.com")!)
     lazy var expandAreaView: TSAIExpandChangeView = TSAIExpandChangeView()
-//    lazy var switchOriginalPictureBtn: TSUIExpandedTouchButton = creatSwitchOriginalPictureBtn()
+    lazy var netWorkImageView: UIImageView = creatNetWorkImageView()//图片生成
+    
     lazy var shareBtn: TSVerticalButton = creatShareBtn()
     lazy var saveBtn: UIButton = creatSaveBtn()
     var navRightBtn:UIButton = UIButton()
@@ -159,6 +160,8 @@ extension TSAIPhotoDetailsVC {
             guard let infoModel = self.infoModel else { return }
             if infoModel.isVideo {
                 self.setVideoURL()
+            }else if infoModel.isShowSingleImage{
+                self.updateNetWorkImageView()
             }else{
                 self.uploadPanComparisonView()
             }

+ 4 - 8
TSLiveWallpaper/Business/TSAIListVC/TSAIUploadPhotoVC/TSAIUploadPhotoVC+View.swift

@@ -98,19 +98,15 @@ extension TSAIUploadPhotoVC {
 
 extension TSAIUploadPhotoVC {
     func pickSinglePhoto(complete: @escaping (UIImage)->Void)  {
-        photoPickerManager.pickCustomSinglePhoto() { [weak self] image, errorString in
+        photoPickerManager.pickPhoto() { [weak self] images in
             guard let self = self else { return }
-            if let errorString = errorString {
-                TSToastShared.showToast(text: errorString)
-            }else if let image = image {
+            if let image = images.first {
                 complete(image)
             }else{
                 dePrint("图片异常")
             }
-            kDelayMainShort {
-                self.photoPickerManager.dismissPageVC()
-            }
-            
+         
+            self.photoPickerManager.dismissPageVC() 
         }
     }
 }

+ 1 - 2
TSLiveWallpaper/Business/TSAIListVC/TSAIUploadPhotoVC/TSAIUploadPhotoVC.swift

@@ -49,7 +49,6 @@ class TSAIUploadPhotoVC: TSBaseVC {
     let lineSpacing = 6.0
     lazy var photoPickerManager: TSPhotoPickerManager = TSPhotoPickerManager(viewController: self)
     var hintBaseVC:TSAIListHintBaseVC = TSAIListHintBaseVC(config: .colorizeConfig)
-    lazy var viewModel: TSAIListVM = TSAIListVM(target: self)
 
     var upLoadImage:UIImage? = nil {
         didSet{
@@ -112,7 +111,7 @@ class TSAIUploadPhotoVC: TSBaseVC {
     }
     
     @objc func updateVipView() {
-        kExecuteOnMainThread {
+        kMainAsync {
             kSetBtnVipIcon(btn: self.submitBtn, show: kPurchaseBusiness.generateVipShow(type: self.generatorStyle == .creatVideo ? .generalVideo :.general))
         }
     }

+ 136 - 0
TSLiveWallpaper/Business/TSAIListVC/TSFusionImageVC/TSAISmallUploadView.swift

@@ -0,0 +1,136 @@
+//
+//  TSAISmallUploadView.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2025/7/14.
+//
+
+class TSAISmallUploadView: TSBaseView {
+    
+    var viewH: CGFloat = 220.0*kDesignScale+44.0
+    @Published var upLoadImage:UIImage? = nil {
+        didSet{
+            if let image = upLoadImage {
+                upLoadView.isHidden = true
+                uploadImageView.isHidden = false
+                deleteBtn.isHidden = false
+                uploadImageView.image = image
+            }else {
+                upLoadView.isHidden = false
+                uploadImageView.isHidden = true
+                deleteBtn.isHidden = true
+                uploadImageView.image = nil
+            }
+        }
+    }
+
+    var clickHandle:((Int)->Void)?
+    weak var targetVC:UIViewController?
+    lazy var titleLab : UILabel = {
+        let titleLab = UILabel.createLabel(font: .font(size: 16),textColor: .white.withAlphaComponent(0.6))
+        return titleLab
+    }()
+    
+    lazy var upLoadView: UIView = {
+        let bgView = UIView()
+        
+        let addImageView = UIImageView.createImageView(imageName: "add")
+        bgView.addSubview(addImageView)
+        addImageView.snp.makeConstraints { make in
+            make.top.equalTo(0)
+            make.centerX.equalToSuperview()
+            make.width.height.equalTo(24)
+        }
+        
+        let textLabel = UILabel.createLabel(text: "Upload Photo".localized,font: .font(size: 16),textColor: .white)
+        bgView.addSubview(textLabel)
+        textLabel.snp.makeConstraints { make in
+            make.top.equalTo(addImageView.snp.bottom).offset(16)
+            make.centerX.equalToSuperview()
+            make.bottom.equalToSuperview()
+        }
+        
+        return bgView
+    }()
+
+    lazy var uploadImageView: UIImageView = {
+        let uploadImageView = UIImageView()
+        uploadImageView.contentMode = .scaleAspectFit
+        upLoadView.isHidden = false
+        return uploadImageView
+    }()
+    
+    
+    lazy var bgView: UIView = {
+        
+        let bgView = UIView()
+        bgView.addGestureRecognizer(UITapGestureRecognizer(target: self, action:#selector(clickBgView)))
+        let bgImageView = UIImageView.createImageView()
+        bgImageView.backgroundColor = .white.withAlphaComponent(0.1)
+        bgImageView.contentMode = .scaleToFill
+        bgView.addSubview(bgImageView)
+        bgImageView.snp.makeConstraints { make in
+            make.leading.bottom.trailing.top.equalTo(0)
+        }
+
+        bgView.addSubview(upLoadView)
+        upLoadView.snp.makeConstraints { make in
+            make.top.equalTo(88)
+            make.centerX.equalToSuperview()
+        }
+
+        bgView.addSubview(uploadImageView)
+        uploadImageView.snp.makeConstraints { make in
+//            make.top.leading.equalTo(8)
+//            make.bottom.trailing.equalTo(-8)
+            make.edges.equalToSuperview()
+        }
+        
+        return bgView
+    }()
+    
+    
+    lazy var deleteBtn: UIButton = {
+        let deleteBtn = UIButton.createButton(backgroundImage: UIImage(named: "delete_redRound")) { [weak self]  in
+            guard let self = self else { return }
+            upLoadImage = nil
+            clickHandle?(0)
+        }
+        deleteBtn.isHidden = true
+        return deleteBtn
+    }()
+    
+    override func creatUI() {
+        
+        contentView.addSubview(titleLab)
+        titleLab.snp.makeConstraints { make in
+            make.leading.equalTo(0)
+            make.top.equalTo(16)
+            make.height.equalTo(16)
+        }
+   
+        contentView.addSubview(bgView)
+        bgView.snp.makeConstraints { make in
+            make.leading.equalTo(0)
+            make.trailing.equalTo(0)
+            make.bottom.equalTo(0)
+            make.top.equalTo(titleLab.snp.bottom).offset(12)
+        }
+        
+        bgView.addSubview(deleteBtn)
+        deleteBtn.snp.makeConstraints { make in
+            make.top.equalTo(-8)
+            make.trailing.equalTo(8)
+            make.width.height.equalTo(32)
+        }
+    }
+    
+
+    @objc func clickBgView() {
+        clickHandle?(1)
+    }
+    
+    
+
+
+}

+ 195 - 0
TSLiveWallpaper/Business/TSAIListVC/TSFusionImageVC/TSFusionImageVC.swift

@@ -0,0 +1,195 @@
+//
+//  TSFusionImageVC.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2025/7/14.
+//
+
+import Combine
+class TSFusionImageVC: TSBaseVC{
+    var generatorStyle:TSGeneratorImageStyle
+    var titleString:String
+    var disCoverItemModel:TSDiscoverItemModel?
+    var upLoadImages:[UIImage?]{
+        didSet{
+            if let image = upLoadImages.safeObj(At: 0) {
+                photo1UploadView.upLoadImage = image
+            }
+            
+            if let image = upLoadImages.safeObj(At: 1) {
+                photo2UploadView.upLoadImage = image
+            }
+        }
+    }
+    init(titleString:String,upLoadImages:[UIImage],generatorStyle:TSGeneratorImageStyle,disCoverItemModel:TSDiscoverItemModel? = nil) {
+        self.titleString = titleString
+        self.upLoadImages = upLoadImages
+        self.generatorStyle = generatorStyle
+        self.disCoverItemModel = disCoverItemModel
+        super.init()
+    }
+    
+    @MainActor required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    @Published private(set) var isCanGennerate: Bool = false
+    lazy var photoPickerManager: TSPhotoPickerManager = TSPhotoPickerManager(viewController: self)
+    
+    //###### UI ######
+    lazy var cusStackView: TSCustomStackView = TSCustomStackView(axis: .vertical,spacing: 0)
+    
+    //##############################上传图片##############################
+    lazy var imageStackView: TSCustomStackView = {
+        let imageStackView = TSCustomStackView(axis: .horizontal,alignment: .center,spacing: 0.0)
+        return imageStackView
+    }()
+    
+    lazy var photo1UploadView: TSAISmallUploadView = {
+        let photo1UploadView = TSAISmallUploadView()
+        photo1UploadView.tag = 0
+        photo1UploadView.targetVC = self
+        photo1UploadView.titleLab.text = "Photo 1".localized
+        photo1UploadView.clickHandle = { [weak self] index in
+            guard let self = self else { return }
+            photoUploadView(handleIndex: index, uploadView: photo1UploadView)
+        }
+
+        return photo1UploadView
+    }()
+    
+    lazy var photo2UploadView: TSAISmallUploadView = {
+        let photo2UploadView = TSAISmallUploadView()
+        photo2UploadView.tag = 1
+        photo2UploadView.targetVC = self
+        photo2UploadView.titleLab.text = "Photo 2".localized
+        photo2UploadView.clickHandle = { [weak self] index in
+            guard let self = self else { return }
+            photoUploadView(handleIndex: index, uploadView: photo2UploadView)
+        }
+        return photo2UploadView
+    }()
+
+    //##############################按钮##############################
+    lazy var submitBtn: UIButton = {
+        let submitBtn = kCreateNormalSubmitBtn(title: generatorStyle.generatorBtnTitle) { [weak self]  in
+            guard let self = self else { return }
+            generateImage()
+        }
+        return submitBtn
+    }()
+    override func createView() {
+        
+        addNormalNavBarView()
+        setPageTitle(titleString)
+        _ = setNavigationItem("", imageName: "replace_photo", direction: .right, action: #selector(clickNavRight))
+        
+        contentView.addSubview(submitBtn)
+        submitBtn.snp.makeConstraints { make in
+            make.centerX.equalToSuperview()
+            make.width.equalTo(250*kDesignScale)
+            make.height.equalTo(48)
+            make.bottom.equalTo(-12-k_Height_safeAreaInsetsBottom())
+        }
+        
+        contentView.addSubview(cusStackView)
+        cusStackView.snp.makeConstraints { make in
+            make.top.equalTo(0)
+            make.leading.trailing.equalToSuperview()
+            make.bottom.equalTo(submitBtn.snp.top).offset(-16)
+        }
+        
+        let upImageW = (k_ScreenWidth - 32.0 - 10.0) / 2.0
+        let upImageH = photo1UploadView.viewH
+        
+        cusStackView.addSubviewToStack(imageStackView,length: upImageH)
+        imageStackView.addSpacing(length: 16.0)
+        imageStackView.addSubviewToStack(photo1UploadView,length: upImageW)
+        imageStackView.addSpacing(length: 10.0)
+        imageStackView.addSubviewToStack(photo2UploadView,length: upImageW)
+        cusStackView.addSpacing(length: 24.0)
+        
+        
+        let a = upLoadImages
+        upLoadImages = a
+    }
+
+    override func dealThings() {
+        Publishers.CombineLatest(
+            photo1UploadView.$upLoadImage,
+            photo2UploadView.$upLoadImage
+        )
+        .map { (image1, image2) -> Bool in
+            return image1 != nil && image2 != nil
+        }
+        .assign(to: \.isCanGennerate, on: self)
+        .store(in: &cancellable)
+        
+        self.$isCanGennerate.receive(on: DispatchQueue.main).sink { [weak self] enble in
+            guard let self = self else { return }
+            submitBtn.isEnabled = enble
+        }.store(in: &cancellable)
+        
+        NotificationCenter.default.addObserver(self, selector: #selector(updateVipView), name: .kPurchaseDidChanged, object: nil)
+        updateVipView()
+    }
+    
+    @objc func updateVipView() {
+        kMainAsync{
+            kSetBtnVipIcon(btn: self.submitBtn, show: kPurchaseBusiness.generateVipShow(type: .general))
+        }
+    }
+}
+
+extension TSFusionImageVC {
+    func photoUploadView(handleIndex:Int,uploadView:TSAISmallUploadView){
+        if handleIndex == 0 {
+            self.upLoadImages[uploadView.tag] = nil
+        }else{
+            photoPickerManager.pickPhoto(maxSelected: 1) { [weak self] images in
+                guard let self = self else { return }
+                
+                if self.upLoadImages.count > uploadView.tag {
+                    self.upLoadImages[uploadView.tag] = images.first
+                }else{
+                    self.upLoadImages.append(images.first)
+                }
+                self.photoPickerManager.dismissPageVC()
+            }
+        }
+    }
+    
+    @objc func clickNavRight() {
+        photoPickerManager.pickPhoto(maxSelected: 2) { [weak self] images in
+            guard let self = self else { return }
+            self.upLoadImages = images
+            self.photoPickerManager.dismissPageVC()
+        }
+    }
+    
+    func generateImage() {
+        if kPurchaseBusiness.kJudgeVipFreeType(vipFreeNumType: generatorStyle == .creatVideo ? .generalVideo : .general){ return }//判断 vip
+    
+        guard let upLoadImages = upLoadImages as? [UIImage] else { return }
+        guard upLoadImages.count == 2 else { return }
+        let generatorModel = TSAIListPhotoGeneratorModel(upLoadImage: upLoadImages.first!, generatorStyle: generatorStyle,additionalPrompt: "")
+        generatorModel.upLoadImages = upLoadImages
+        if let generateModel = disCoverItemModel?.generateModel{
+            generatorModel.prompt = generateModel.prompt
+            generatorModel.model = generateModel.model
+        }
+        
+        let gennerateVC = TSAIListPhotoGeneratorVC(generatorModel:generatorModel){[weak self] model in
+            guard let self = self else { return }
+            updateVipView()
+        }
+        
+        gennerateVC.backstageBlock = { [weak self]  in
+            guard let self = self else { return }
+            TSAIListHistoryVC.showPosition()
+            self.navigationController?.popToRootViewController(animated: false)
+        }
+        kPresentModalVC(target: self, modelVC: gennerateVC,transitionStyle: .crossDissolve)
+    }
+
+}

+ 3 - 3
TSLiveWallpaper/Common/Purchase/TSPurchaseManager.swift

@@ -82,9 +82,9 @@ public class PurchaseManager: NSObject {
     }
 
     @objc public var isVip: Bool {
-//        #if DEBUG
-//        return true
-//        #endif
+        #if DEBUG
+        return true
+        #endif
         guard let expiresDate = expiredDate else {
             return false
         }

+ 51 - 0
TSLiveWallpaper/Common/ViewTool/TSPhotoPickerManager/TSHXPhotoPickerManager.swift

@@ -0,0 +1,51 @@
+//
+//  TSHXPhotoPickerManager.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2025/7/11.
+//
+
+//import HXPhotoPicker
+
+
+
+//let kPhotoPickerManager = TSHXPhotoPickerManager()
+//class TSHXPhotoPickerManager {
+//    private var completionHandler: ((UIImage?) -> Void)!
+//    lazy var config: PickerConfiguration = {
+//        var config = PickerConfiguration()
+//        config.modalPresentationStyle = .automatic
+//        config.selectOptions = .photo
+//        config.selectMode = .single
+//        config.photoList.sort = .desc
+//        config.photoSelectionTapAction = .quickSelect
+//        config.photoList.finishSelectionAfterTakingPhoto = true
+//        var a = SystemCameraConfiguration()
+//        a.allowsEditing = false
+//        config.photoList.cameraType = .system(a)
+//        return config
+//    }()
+//    
+//    func pickSinglePhoto(target:UIViewController,completionHandler:@escaping ((UIImage?) -> Void)) -> Void {
+//        self.completionHandler = completionHandler
+//        let picker = PhotoPickerController(splitPicker: config)
+//        picker.pickerDelegate = self
+//        picker.autoDismiss = false
+//        let split = PhotoSplitViewController(picker: picker)
+//        target.present(split, animated: true, completion: nil)
+//    }
+//
+//}
+//
+//extension TSHXPhotoPickerManager: PhotoPickerControllerDelegate {
+//    func pickerController(_ pickerController: PhotoPickerController, didFinishSelection result: PickerResult) {
+//        result.getImage { images in
+//            self.completionHandler(images.first)
+//        }
+//        pickerController.dismiss(animated: true, completion: nil)
+//    }
+//    
+//    func pickerController(didCancel pickerController: PhotoPickerController) {
+//        pickerController.dismiss(animated: true, completion: nil)
+//    }
+//}

+ 235 - 95
TSLiveWallpaper/Common/ViewTool/TSPhotoPickerManager/TSPhotoPickerManager.swift

@@ -7,14 +7,51 @@
 
 import UIKit
 import PhotosUI
-
+import HXPhotoPicker
 class TSPhotoPickerManager: NSObject {
+    lazy var config: PickerConfiguration = {
+        var config = PickerConfiguration()
+        config.modalPresentationStyle = .automatic
+        config.selectOptions = .photo
+        config.selectMode = .single
+        config.isSelectedOriginal = true
+        config.photoList.sort = .desc
+        config.photoSelectionTapAction = .quickSelect
+        config.photoList.finishSelectionAfterTakingPhoto = true
+        config.photoList.isSaveSystemAlbum = false
+        config.photoList.takePictureCompletionToSelected = true
+        
+        var cameraConfig = SystemCameraConfiguration()
+        cameraConfig.allowsEditing = false
+        config.photoList.cameraType = .system(cameraConfig)
+        return config
+    }()
+    
+    var picker: PhotoPickerController?
+    lazy var multipleConfig: PickerConfiguration = {
+        var config = PickerConfiguration()
+        config.modalPresentationStyle = .automatic
+        config.selectOptions = .photo
+        config.selectMode = .multiple
+        config.maximumSelectedCount = 2
+        config.isSelectedOriginal = true
+        config.photoList.sort = .desc
+        config.photoSelectionTapAction = .preview
+        config.photoList.finishSelectionAfterTakingPhoto = false
+        config.photoList.isSaveSystemAlbum = false
+        config.photoList.takePictureCompletionToSelected = true
+        var cameraConfig = SystemCameraConfiguration()
+        cameraConfig.allowsEditing = false
+        config.photoList.cameraType = .system(cameraConfig)
+        return config
+    }()
+    
+    
     
     // MARK: - Properties
     private weak var viewController: UIViewController?
-    private var completionHandler: ((UIImage?,PHAsset?) -> Void)?
-    private var completionSizeHandler: ((UIImage?,String?) -> Void)?
-    private var imagePicker = UIImagePickerController()
+    private var completionHandler: (([UIImage]) -> Void)?
+    private var maxSelected:Int = 1
     // MARK: - Initializers
     init(viewController: UIViewController) {
         self.viewController = viewController
@@ -22,8 +59,9 @@ class TSPhotoPickerManager: NSObject {
     
     // MARK: - Public Methods
     /// 打开照片选择器,单选一张照片
-    func pickSinglePhoto(completion: @escaping (UIImage?,PHAsset?) -> Void) {
+    func pickPhoto(maxSelected:Int = 1,completion: @escaping ([UIImage]) -> Void) {
         self.completionHandler = completion
+        self.maxSelected = maxSelected
         // 检查相册权限
         checkPhotoLibraryPermission { [weak self] authorized in
             guard let self = self else { return }
@@ -55,23 +93,18 @@ class TSPhotoPickerManager: NSObject {
     
     /// 打开照片选择器
     private func openPhotoPicker() {
-        TSToastShared.showLoading(containerView: viewController?.view)
-        imagePicker = UIImagePickerController()
-        imagePicker.sourceType = .photoLibrary
-        imagePicker.delegate = self
-        imagePicker.mediaTypes = ["public.image"] // 只选择照片
-//        imagePicker.modalPresentationStyle = .custom
-//        imagePicker.modalTransitionStyle = .crossDissolve
-        if #available(iOS 13.0, *) {
-            imagePicker.overrideUserInterfaceStyle = .dark
-        }
-        viewController?.present(imagePicker, animated: true){
-            TSToastShared.hideLoading()
-        }
         
-        kMainAfter(3.0) {
-            TSToastShared.hideLoading()
+        var config = config
+        if maxSelected > 1 {
+            config = multipleConfig
+            config.maximumSelectedCount = maxSelected
         }
+
+        let picker = PhotoPickerController(splitPicker: config)
+        picker.pickerDelegate = self
+        picker.autoDismiss = false
+        viewController?.present(picker, animated: true, completion: nil)
+        self.picker = picker
     }
     
     /// 显示权限提示
@@ -93,88 +126,195 @@ class TSPhotoPickerManager: NSObject {
     deinit {
         debugPrint("♻️♻️♻️ TSPhotoPickerManager -> TSPhotoPickerManager deinit ♻️♻️♻️")
     }
+    
+    func dismissPageVC(){
+        self.picker?.view.isHidden = true
+        self.picker?.dismiss(animated: false)
+        self.picker = nil
+    }
 }
 
-// MARK: - UIImagePickerControllerDelegate & UINavigationControllerDelegate (iOS 14 以下)
-extension TSPhotoPickerManager: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
-    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
-//        picker.dismiss(animated: true) {
-            if let image = info[.originalImage] as? UIImage {
-                self.completionHandler?(image,info[.phAsset] as? PHAsset )
-            } else {
-                self.completionHandler?(nil,nil)
-            }
-//        }
-        
-        if completionSizeHandler == nil {
-            picker.dismiss(animated: true, completion: nil)
+extension TSPhotoPickerManager: PhotoPickerControllerDelegate {
+    func pickerController(_ pickerController: PhotoPickerController, didFinishSelection result: PickerResult) {
+        result.getImage(compressionScale: 1.0) { images in
+            self.completionHandler?(images)
         }
-
     }
     
-    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
-//        self.completionHandler?(nil,nil)
-//        if completionSizeHandler == nil {
-            picker.dismiss(animated: true, completion: nil)
-//        }
+    func pickerController(didCancel pickerController: PhotoPickerController) {
+        pickerController.dismiss(animated: true, completion: nil)
     }
 }
 
 
 
-extension TSPhotoPickerManager{
-    
-    func pickCustomSinglePhoto(completion: @escaping (UIImage?,String?) -> Void) {
-        self.completionSizeHandler = completion
-        pickSinglePhoto { [weak self] image,phAsset in
-            guard let self = self else { return }
-            self.completionSizeHandler?(image,nil)
-            self.completionSizeHandler = nil
-        }
-    }
-    
-    // MARK: - Public Methods
-    /// 打开照片选择器,单选一张照片
-    func pickSinglePhoto(maxBitSize:Int, completion: @escaping (UIImage?,String?) -> Void) {
-        self.completionSizeHandler = completion
-
-        let maxmbSize = Int(Double(maxBitSize) / (1024 * 1024))
-        pickSinglePhoto { [weak self] image,phAsset in
-            guard let self = self else { return }
-            if let image = image,let phAsset = phAsset {
-                // 方法2:异步获取详细大小(不阻塞主线程)
-                TSPhotoSizeHelper.getImageFileSizeAsync(asset: phAsset) {[weak self] size in
-                    guard let self = self else { return }
-                    
-                    let mbSize = Double(size) / (1024 * 1024)
-                    print("精确大小: \(mbSize) MB,size = \(size)")
-                    if size > maxBitSize {
-                        self.completionSizeHandler?(nil,String(format: "Photo must be smaller than %dMB.".localized, maxmbSize))
-                    }else{
-                        self.completionSizeHandler?(image,nil)
-                        self.completionSizeHandler = nil
-//                        imagePicker.dismiss(animated: true)
-                    }
-                }
-            }else{
-                if let image = image {
-                    if image.isLargerThan(byteSize: maxBitSize) {
-                        self.completionSizeHandler?(nil,String(format: "Photo must be smaller than %dMB.".localized, maxmbSize))
-                    }else{
-                        self.completionSizeHandler?(image,nil)
-                        self.completionSizeHandler = nil
-//                        imagePicker.dismiss(animated: true)
-                    }
-                }else{
-                    self.completionSizeHandler?(nil,nil)
-//                    imagePicker.dismiss(animated: true)
-                }
-            }
-        }
-    }
-    
-    func dismissPageVC(){
-        self.imagePicker.view.isHidden = true
-        self.imagePicker.dismiss(animated: true)
-    }
-}
+//class TSPhotoPickerManager: NSObject {
+//    
+//    // MARK: - Properties
+//    private weak var viewController: UIViewController?
+//    private var completionHandler: ((UIImage?,PHAsset?) -> Void)?
+//    private var completionSizeHandler: ((UIImage?,String?) -> Void)?
+//    private var imagePicker = UIImagePickerController()
+//    // MARK: - Initializers
+//    init(viewController: UIViewController) {
+//        self.viewController = viewController
+//    }
+//    
+//    // MARK: - Public Methods
+//    /// 打开照片选择器,单选一张照片
+//    func pickSinglePhoto(completion: @escaping (UIImage?,PHAsset?) -> Void) {
+//        self.completionHandler = completion
+//        // 检查相册权限
+//        checkPhotoLibraryPermission { [weak self] authorized in
+//            guard let self = self else { return }
+//            if authorized {
+//                self.openPhotoPicker()
+//            } else {
+//                self.showPermissionAlert()
+//            }
+//        }
+//    }
+//    
+//    // MARK: - Private Methods
+//    /// 检查相册权限
+//    private func checkPhotoLibraryPermission(completion: @escaping (Bool) -> Void) {
+//        let status = PHPhotoLibrary.authorizationStatus()
+//        switch status {
+//        case .authorized:
+//            completion(true)
+//        case .notDetermined:
+//            PHPhotoLibrary.requestAuthorization { newStatus in
+//                DispatchQueue.main.async {
+//                    completion(newStatus == .authorized)
+//                }
+//            }
+//        default:
+//            completion(false)
+//        }
+//    }
+//    
+//    /// 打开照片选择器
+//    private func openPhotoPicker() {
+//        TSToastShared.showLoading(containerView: viewController?.view)
+//        imagePicker = UIImagePickerController()
+//        imagePicker.sourceType = .photoLibrary
+//        imagePicker.delegate = self
+//        imagePicker.mediaTypes = ["public.image"] // 只选择照片
+////        imagePicker.modalPresentationStyle = .custom
+////        imagePicker.modalTransitionStyle = .crossDissolve
+//        if #available(iOS 13.0, *) {
+//            imagePicker.overrideUserInterfaceStyle = .dark
+//        }
+//        viewController?.present(imagePicker, animated: true){
+//            TSToastShared.hideLoading()
+//        }
+//        
+//        kMainAfter(3.0) {
+//            TSToastShared.hideLoading()
+//        }
+//    }
+//    
+//    /// 显示权限提示
+//    private func showPermissionAlert() {
+//        let alert = UIAlertController(
+//            title: "No photos permission".localized,
+//            message: "Please enable photo permission in settings to select photos".localized,
+//            preferredStyle: .alert
+//        )
+//        alert.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil))
+//        alert.addAction(UIAlertAction(title: "Go to Settings".localized, style: .default) { _ in
+//            if let url = URL(string: UIApplication.openSettingsURLString) {
+//                UIApplication.shared.open(url, options: [:], completionHandler: nil)
+//            }
+//        })
+//        viewController?.present(alert, animated: true, completion: nil)
+//    }
+//    
+//    deinit {
+//        debugPrint("♻️♻️♻️ TSPhotoPickerManager -> TSPhotoPickerManager deinit ♻️♻️♻️")
+//    }
+//}
+//
+//// MARK: - UIImagePickerControllerDelegate & UINavigationControllerDelegate (iOS 14 以下)
+//extension TSPhotoPickerManager: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
+//    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
+////        picker.dismiss(animated: true) {
+//            if let image = info[.originalImage] as? UIImage {
+//                self.completionHandler?(image,info[.phAsset] as? PHAsset )
+//            } else {
+//                self.completionHandler?(nil,nil)
+//            }
+////        }
+//        
+//        if completionSizeHandler == nil {
+//            picker.dismiss(animated: true, completion: nil)
+//        }
+//
+//    }
+//    
+//    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
+////        self.completionHandler?(nil,nil)
+////        if completionSizeHandler == nil {
+//            picker.dismiss(animated: true, completion: nil)
+////        }
+//    }
+//}
+//
+//
+//
+//extension TSPhotoPickerManager{
+//    
+//    func pickCustomSinglePhoto(completion: @escaping (UIImage?,String?) -> Void) {
+//        self.completionSizeHandler = completion
+//        pickSinglePhoto { [weak self] image,phAsset in
+//            guard let self = self else { return }
+//            self.completionSizeHandler?(image,nil)
+//            self.completionSizeHandler = nil
+//        }
+//    }
+//    
+//    // MARK: - Public Methods
+//    /// 打开照片选择器,单选一张照片
+//    func pickSinglePhoto(maxBitSize:Int, completion: @escaping (UIImage?,String?) -> Void) {
+//        self.completionSizeHandler = completion
+//
+//        let maxmbSize = Int(Double(maxBitSize) / (1024 * 1024))
+//        pickSinglePhoto { [weak self] image,phAsset in
+//            guard let self = self else { return }
+//            if let image = image,let phAsset = phAsset {
+//                // 方法2:异步获取详细大小(不阻塞主线程)
+//                TSPhotoSizeHelper.getImageFileSizeAsync(asset: phAsset) {[weak self] size in
+//                    guard let self = self else { return }
+//                    
+//                    let mbSize = Double(size) / (1024 * 1024)
+//                    print("精确大小: \(mbSize) MB,size = \(size)")
+//                    if size > maxBitSize {
+//                        self.completionSizeHandler?(nil,String(format: "Photo must be smaller than %dMB.".localized, maxmbSize))
+//                    }else{
+//                        self.completionSizeHandler?(image,nil)
+//                        self.completionSizeHandler = nil
+////                        imagePicker.dismiss(animated: true)
+//                    }
+//                }
+//            }else{
+//                if let image = image {
+//                    if image.isLargerThan(byteSize: maxBitSize) {
+//                        self.completionSizeHandler?(nil,String(format: "Photo must be smaller than %dMB.".localized, maxmbSize))
+//                    }else{
+//                        self.completionSizeHandler?(image,nil)
+//                        self.completionSizeHandler = nil
+////                        imagePicker.dismiss(animated: true)
+//                    }
+//                }else{
+//                    self.completionSizeHandler?(nil,nil)
+////                    imagePicker.dismiss(animated: true)
+//                }
+//            }
+//        }
+//    }
+//    
+//    func dismissPageVC(){
+//        self.imagePicker.view.isHidden = true
+//        self.imagePicker.dismiss(animated: true)
+//    }
+//}

+ 5 - 0
TSLiveWallpaper/Data/Model/TSActionInfoModel.swift

@@ -60,6 +60,11 @@ extension TSActionInfoModel {
     var isVideo:Bool{
         return request.generatorStyle == TSGeneratorImageStyle.creatVideo
     }
+    
+    //是否是只显示单张的图片
+    var isShowSingleImage:Bool{
+        return request.generatorStyle == TSGeneratorImageStyle.portraitFusion
+    }
 
     var upImageURLExpired:Bool{
         if request.imageUrl.count == 0 {

+ 5 - 0
TSLiveWallpaper/Data/OperationQueue/TSGenerateBaseOperation/TSGenerateBasePhotoOperation.swift

@@ -334,6 +334,11 @@ extension TSGenerateBasePhotoOperation {
         case .remove:
             urlType = .imageInpaint
             postDict["maskUrl"] = request.imageUrls.last ?? request.imageUrl
+        case .portraitFusion:
+            guard request.imageUrls.count > 0 else { return nil}
+            postDict["prompt"] = request.prompt
+            postDict["imageUrls"] = request.imageUrls
+            postDict.removeValue(forKey: "imageUrl")
         default:
             postDict["prompt"] = request.prompt
             break;