Browse Source

AI 聊天的输入框开发完毕

100Years 2 months ago
parent
commit
2d8844da4c
27 changed files with 1262 additions and 615 deletions
  1. 36 16
      AIEmoji.xcodeproj/project.pbxproj
  2. 22 0
      AIEmoji/Assets.xcassets/AIChat/chat_send.imageset/Contents.json
  3. BIN
      AIEmoji/Assets.xcassets/AIChat/chat_send.imageset/chat_send@2x.png
  4. BIN
      AIEmoji/Assets.xcassets/AIChat/chat_send.imageset/chat_send@3x.png
  5. 22 0
      AIEmoji/Assets.xcassets/AIChat/chat_send_magnify.imageset/Contents.json
  6. BIN
      AIEmoji/Assets.xcassets/AIChat/chat_send_magnify.imageset/chat_send_magnify@2x.png
  7. BIN
      AIEmoji/Assets.xcassets/AIChat/chat_send_magnify.imageset/chat_send_magnify@3x.png
  8. 22 0
      AIEmoji/Assets.xcassets/AIChat/chat_send_minify.imageset/Contents.json
  9. BIN
      AIEmoji/Assets.xcassets/AIChat/chat_send_minify.imageset/chat_send_minify@2x.png
  10. BIN
      AIEmoji/Assets.xcassets/AIChat/chat_send_minify.imageset/chat_send_minify@3x.png
  11. 19 0
      AIEmoji/Business/AIChat/TSAIChatContainerVC.swift
  12. 41 20
      AIEmoji/Business/AIChat/TSAIChatHistoryVC/TSAIChatHistoryVC.swift
  13. 5 0
      AIEmoji/Business/AIChat/TSAIChatHistoryVC/VM/TSAIChatHistoryVM.swift
  14. 8 9
      AIEmoji/Business/AIChat/TSChatViewController/Layout/CustomMessageFlowLayout.swift
  15. 0 209
      AIEmoji/Business/AIChat/TSChatViewController/Models/CustomLayoutSizeCalculator.swift
  16. 0 78
      AIEmoji/Business/AIChat/TSChatViewController/Models/CustomTextLayoutSizeCalculator.swift
  17. 46 0
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSChatCellConfig.swift
  18. 38 31
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSDBAIChatList.swift
  19. 76 0
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSLayoutSizeCalculator.swift
  20. 72 0
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSTextLayoutSizeCalculator.swift
  21. 162 0
      AIEmoji/Business/AIChat/TSChatViewController/TSChatInputBarVC/TSChatInputBarVC.swift
  22. 135 0
      AIEmoji/Business/AIChat/TSChatViewController/TSChatInputBarVC/TSChatInputFullScreenVC.swift
  23. 188 209
      AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController+Ex.swift
  24. 72 40
      AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController.swift
  25. 31 3
      AIEmoji/Business/AIChat/TSChatViewController/ViewModel/TSAIChatVM.swift
  26. 170 0
      AIEmoji/Business/AIChat/TSChatViewController/Views/TSMessageContentCell.swift
  27. 97 0
      AIEmoji/Business/AIChat/TSChatViewController/Views/TSTextMessageContentCell.swift

+ 36 - 16
AIEmoji.xcodeproj/project.pbxproj

@@ -39,15 +39,15 @@
 		A80E73E42D533EB000C64288 /* TSPurchaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E73E22D533EB000C64288 /* TSPurchaseManager.swift */; };
 		A80E73E62D5348D000C64288 /* SettingPurchaseTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E73E52D5348CF00C64288 /* SettingPurchaseTopView.swift */; };
 		A89EA64B2D59A588000EB181 /* MessageKit in Frameworks */ = {isa = PBXBuildFile; productRef = A89EA64A2D59A588000EB181 /* MessageKit */; };
-		A89EA6542D59A9F4000EB181 /* CustomTextLayoutSizeCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA64F2D59A9F4000EB181 /* CustomTextLayoutSizeCalculator.swift */; };
+		A89EA6542D59A9F4000EB181 /* TSTextLayoutSizeCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA64F2D59A9F4000EB181 /* TSTextLayoutSizeCalculator.swift */; };
 		A89EA6552D59A9F4000EB181 /* MockMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6502D59A9F4000EB181 /* MockMessage.swift */; };
 		A89EA6562D59A9F4000EB181 /* TSChatUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6522D59A9F4000EB181 /* TSChatUser.swift */; };
-		A89EA6582D59A9F4000EB181 /* CustomLayoutSizeCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA64E2D59A9F4000EB181 /* CustomLayoutSizeCalculator.swift */; };
+		A89EA6582D59A9F4000EB181 /* TSLayoutSizeCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA64E2D59A9F4000EB181 /* TSLayoutSizeCalculator.swift */; };
 		A89EA6592D59A9F4000EB181 /* CustomMessageFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA64C2D59A9F4000EB181 /* CustomMessageFlowLayout.swift */; };
 		A89EA65F2D59AA11000EB181 /* TSChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA65E2D59AA11000EB181 /* TSChatViewController.swift */; };
 		A89EA6692D59AA31000EB181 /* CameraInputBarAccessoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6632D59AA31000EB181 /* CameraInputBarAccessoryView.swift */; };
-		A89EA66B2D59AA31000EB181 /* CustomTextMessageContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6662D59AA31000EB181 /* CustomTextMessageContentCell.swift */; };
-		A89EA66C2D59AA31000EB181 /* CustomMessageContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6652D59AA31000EB181 /* CustomMessageContentCell.swift */; };
+		A89EA66B2D59AA31000EB181 /* TSTextMessageContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6662D59AA31000EB181 /* TSTextMessageContentCell.swift */; };
+		A89EA66C2D59AA31000EB181 /* TSMessageContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6652D59AA31000EB181 /* TSMessageContentCell.swift */; };
 		A89EA66D2D59AA31000EB181 /* TableViewCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6672D59AA31000EB181 /* TableViewCells.swift */; };
 		A89EA6762D59C93E000EB181 /* TSAIChatContainerVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6752D59C932000EB181 /* TSAIChatContainerVC.swift */; };
 		A89EA67A2D59D25F000EB181 /* TSAIChatVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6792D59D248000EB181 /* TSAIChatVM.swift */; };
@@ -62,6 +62,9 @@
 		A89EA6BA2D5DDE5B000EB181 /* TSPageNullView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6B92D5DDE4E000EB181 /* TSPageNullView.swift */; };
 		A89EA6BC2D5DFB12000EB181 /* TSViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6BB2D5DFB0D000EB181 /* TSViewController.swift */; };
 		A89EA6BF2D5E03D6000EB181 /* Notification+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6BE2D5E03D3000EB181 /* Notification+Ex.swift */; };
+		A89EA6C12D5ED289000EB181 /* TSChatCellConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6C02D5ED278000EB181 /* TSChatCellConfig.swift */; };
+		A89EA6C42D5F40CC000EB181 /* TSChatInputBarVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6C32D5F40CB000EB181 /* TSChatInputBarVC.swift */; };
+		A89EA6C62D5F5C22000EB181 /* TSChatInputFullScreenVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6C52D5F5C21000EB181 /* TSChatInputFullScreenVC.swift */; };
 		A8EEADD42D3E6C660032C5A0 /* Flower💐.json in Resources */ = {isa = PBXBuildFile; fileRef = A8EEADD32D3E6C610032C5A0 /* Flower💐.json */; };
 		A8EEADD62D3E6CD80032C5A0 /* Fish🐠.json in Resources */ = {isa = PBXBuildFile; fileRef = A8EEADD52D3E6CD30032C5A0 /* Fish🐠.json */; };
 		A8EEADD82D3E74D20032C5A0 /* Pink🩷.json in Resources */ = {isa = PBXBuildFile; fileRef = A8EEADD72D3E74CB0032C5A0 /* Pink🩷.json */; };
@@ -154,14 +157,14 @@
 		A80E73E22D533EB000C64288 /* TSPurchaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPurchaseManager.swift; sourceTree = "<group>"; };
 		A80E73E52D5348CF00C64288 /* SettingPurchaseTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingPurchaseTopView.swift; sourceTree = "<group>"; };
 		A89EA64C2D59A9F4000EB181 /* CustomMessageFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomMessageFlowLayout.swift; sourceTree = "<group>"; };
-		A89EA64E2D59A9F4000EB181 /* CustomLayoutSizeCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomLayoutSizeCalculator.swift; sourceTree = "<group>"; };
-		A89EA64F2D59A9F4000EB181 /* CustomTextLayoutSizeCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextLayoutSizeCalculator.swift; sourceTree = "<group>"; };
+		A89EA64E2D59A9F4000EB181 /* TSLayoutSizeCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSLayoutSizeCalculator.swift; sourceTree = "<group>"; };
+		A89EA64F2D59A9F4000EB181 /* TSTextLayoutSizeCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTextLayoutSizeCalculator.swift; sourceTree = "<group>"; };
 		A89EA6502D59A9F4000EB181 /* MockMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMessage.swift; sourceTree = "<group>"; };
 		A89EA6522D59A9F4000EB181 /* TSChatUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSChatUser.swift; sourceTree = "<group>"; };
 		A89EA65E2D59AA11000EB181 /* TSChatViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSChatViewController.swift; sourceTree = "<group>"; };
 		A89EA6632D59AA31000EB181 /* CameraInputBarAccessoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraInputBarAccessoryView.swift; sourceTree = "<group>"; };
-		A89EA6652D59AA31000EB181 /* CustomMessageContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomMessageContentCell.swift; sourceTree = "<group>"; };
-		A89EA6662D59AA31000EB181 /* CustomTextMessageContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextMessageContentCell.swift; sourceTree = "<group>"; };
+		A89EA6652D59AA31000EB181 /* TSMessageContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSMessageContentCell.swift; sourceTree = "<group>"; };
+		A89EA6662D59AA31000EB181 /* TSTextMessageContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTextMessageContentCell.swift; sourceTree = "<group>"; };
 		A89EA6672D59AA31000EB181 /* TableViewCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewCells.swift; sourceTree = "<group>"; };
 		A89EA6752D59C932000EB181 /* TSAIChatContainerVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAIChatContainerVC.swift; sourceTree = "<group>"; };
 		A89EA6792D59D248000EB181 /* TSAIChatVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAIChatVM.swift; sourceTree = "<group>"; };
@@ -176,6 +179,9 @@
 		A89EA6B92D5DDE4E000EB181 /* TSPageNullView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPageNullView.swift; sourceTree = "<group>"; };
 		A89EA6BB2D5DFB0D000EB181 /* TSViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSViewController.swift; sourceTree = "<group>"; };
 		A89EA6BE2D5E03D3000EB181 /* Notification+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+Ex.swift"; sourceTree = "<group>"; };
+		A89EA6C02D5ED278000EB181 /* TSChatCellConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSChatCellConfig.swift; sourceTree = "<group>"; };
+		A89EA6C32D5F40CB000EB181 /* TSChatInputBarVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSChatInputBarVC.swift; sourceTree = "<group>"; };
+		A89EA6C52D5F5C21000EB181 /* TSChatInputFullScreenVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSChatInputFullScreenVC.swift; sourceTree = "<group>"; };
 		A8EEADD32D3E6C610032C5A0 /* Flower💐.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "Flower💐.json"; sourceTree = "<group>"; };
 		A8EEADD52D3E6CD30032C5A0 /* Fish🐠.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "Fish🐠.json"; sourceTree = "<group>"; };
 		A8EEADD72D3E74CB0032C5A0 /* Pink🩷.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "Pink🩷.json"; sourceTree = "<group>"; };
@@ -434,8 +440,9 @@
 			isa = PBXGroup;
 			children = (
 				A89EA6A22D5B26E3000EB181 /* TSDBAIChatList.swift */,
-				A89EA64E2D59A9F4000EB181 /* CustomLayoutSizeCalculator.swift */,
-				A89EA64F2D59A9F4000EB181 /* CustomTextLayoutSizeCalculator.swift */,
+				A89EA6C02D5ED278000EB181 /* TSChatCellConfig.swift */,
+				A89EA64E2D59A9F4000EB181 /* TSLayoutSizeCalculator.swift */,
+				A89EA64F2D59A9F4000EB181 /* TSTextLayoutSizeCalculator.swift */,
 				A89EA6502D59A9F4000EB181 /* MockMessage.swift */,
 				A89EA6522D59A9F4000EB181 /* TSChatUser.swift */,
 			);
@@ -446,8 +453,8 @@
 			isa = PBXGroup;
 			children = (
 				A89EA6632D59AA31000EB181 /* CameraInputBarAccessoryView.swift */,
-				A89EA6652D59AA31000EB181 /* CustomMessageContentCell.swift */,
-				A89EA6662D59AA31000EB181 /* CustomTextMessageContentCell.swift */,
+				A89EA6652D59AA31000EB181 /* TSMessageContentCell.swift */,
+				A89EA6662D59AA31000EB181 /* TSTextMessageContentCell.swift */,
 				A89EA6672D59AA31000EB181 /* TableViewCells.swift */,
 			);
 			path = Views;
@@ -456,6 +463,7 @@
 		A89EA6772D59D224000EB181 /* TSChatViewController */ = {
 			isa = PBXGroup;
 			children = (
+				A89EA6C22D5F4094000EB181 /* TSChatInputBarVC */,
 				A89EA6782D59D238000EB181 /* ViewModel */,
 				A89EA6682D59AA31000EB181 /* Views */,
 				A89EA65E2D59AA11000EB181 /* TSChatViewController.swift */,
@@ -519,6 +527,15 @@
 			path = Ex;
 			sourceTree = "<group>";
 		};
+		A89EA6C22D5F4094000EB181 /* TSChatInputBarVC */ = {
+			isa = PBXGroup;
+			children = (
+				A89EA6C52D5F5C21000EB181 /* TSChatInputFullScreenVC.swift */,
+				A89EA6C32D5F40CB000EB181 /* TSChatInputBarVC.swift */,
+			);
+			path = TSChatInputBarVC;
+			sourceTree = "<group>";
+		};
 		A8F774602D38E8B000AA6E93 = {
 			isa = PBXGroup;
 			children = (
@@ -1047,10 +1064,10 @@
 				A89EA6B42D5C9D43000EB181 /* TSAIChatHistoryVM.swift in Sources */,
 				A80E72532D3F985E00C64288 /* TSWallpaperVC.swift in Sources */,
 				A8FB02B32D3E39A40031A396 /* TSEmojisModel.swift in Sources */,
-				A89EA6542D59A9F4000EB181 /* CustomTextLayoutSizeCalculator.swift in Sources */,
+				A89EA6542D59A9F4000EB181 /* TSTextLayoutSizeCalculator.swift in Sources */,
 				A89EA6552D59A9F4000EB181 /* MockMessage.swift in Sources */,
 				A89EA6562D59A9F4000EB181 /* TSChatUser.swift in Sources */,
-				A89EA6582D59A9F4000EB181 /* CustomLayoutSizeCalculator.swift in Sources */,
+				A89EA6582D59A9F4000EB181 /* TSLayoutSizeCalculator.swift in Sources */,
 				A89EA6592D59A9F4000EB181 /* CustomMessageFlowLayout.swift in Sources */,
 				A80E72792D42285500C64288 /* TSBootPageVC.swift in Sources */,
 				A80E726A2D409E5400C64288 /* TSDiyTLYFlowersView.swift in Sources */,
@@ -1075,6 +1092,7 @@
 				A8F775432D39346400AA6E93 /* TSSetingModel.swift in Sources */,
 				A89EA6BA2D5DDE5B000EB181 /* TSPageNullView.swift in Sources */,
 				A8F774EC2D38EA8C00AA6E93 /* TSToastTool.swift in Sources */,
+				A89EA6C12D5ED289000EB181 /* TSChatCellConfig.swift in Sources */,
 				A8F775502D39ECED00AA6E93 /* PhotoManager.swift in Sources */,
 				A8F7763F2D3B68E100AA6E93 /* TSGenmojiGennerateViewModel.swift in Sources */,
 				A80E72352D3F473400C64288 /* DiyPaperTemplateBaseView.swift in Sources */,
@@ -1092,6 +1110,7 @@
 				A80E73E12D533E5800C64288 /* TSPurchaseVC.swift in Sources */,
 				A8F776352D3A7C2B00AA6E93 /* TSGenmojiColSectionView.swift in Sources */,
 				A80E724F2D3F6D7F00C64288 /* DiyFixedTextElement.swift in Sources */,
+				A89EA6C42D5F40CC000EB181 /* TSChatInputBarVC.swift in Sources */,
 				A8F775452D39347100AA6E93 /* TSSetingViewModel.swift in Sources */,
 				A80E72202D3F3A8600C64288 /* DiyElementBaseView.swift in Sources */,
 				A8F776212D3A3F0200AA6E93 /* TSEmojisChildVC.swift in Sources */,
@@ -1114,8 +1133,8 @@
 				A80E721E2D3F3A7500C64288 /* DiyElement.swift in Sources */,
 				A8F776372D3A806E00AA6E93 /* TSGenmojiItemCell.swift in Sources */,
 				A89EA6692D59AA31000EB181 /* CameraInputBarAccessoryView.swift in Sources */,
-				A89EA66B2D59AA31000EB181 /* CustomTextMessageContentCell.swift in Sources */,
-				A89EA66C2D59AA31000EB181 /* CustomMessageContentCell.swift in Sources */,
+				A89EA66B2D59AA31000EB181 /* TSTextMessageContentCell.swift in Sources */,
+				A89EA66C2D59AA31000EB181 /* TSMessageContentCell.swift in Sources */,
 				A89EA66D2D59AA31000EB181 /* TableViewCells.swift in Sources */,
 				A8F7750A2D38EA8C00AA6E93 /* TSNetworkTool.swift in Sources */,
 				A8F7762D2D3A74A100AA6E93 /* TSGenmojiGennerateCell.swift in Sources */,
@@ -1128,6 +1147,7 @@
 				A80E72382D3F473B00C64288 /* DiyPaperProtocol.swift in Sources */,
 				A8F775382D390C3C00AA6E93 /* TSNetworkManager.swift in Sources */,
 				A89EA65F2D59AA11000EB181 /* TSChatViewController.swift in Sources */,
+				A89EA6C62D5F5C22000EB181 /* TSChatInputFullScreenVC.swift in Sources */,
 				A89EA6B12D5C9D0C000EB181 /* TSAIChatHistoryVC.swift in Sources */,
 				A8FB02B72D3E3A3D0031A396 /* TSEmojisChildViewModel.swift in Sources */,
 				A8F7754B2D39376800AA6E93 /* TSSettingListView.swift in Sources */,

+ 22 - 0
AIEmoji/Assets.xcassets/AIChat/chat_send.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/AIChat/chat_send.imageset/chat_send@2x.png


BIN
AIEmoji/Assets.xcassets/AIChat/chat_send.imageset/chat_send@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/AIChat/chat_send_magnify.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/AIChat/chat_send_magnify.imageset/chat_send_magnify@2x.png


BIN
AIEmoji/Assets.xcassets/AIChat/chat_send_magnify.imageset/chat_send_magnify@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/AIChat/chat_send_minify.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/AIChat/chat_send_minify.imageset/chat_send_minify@2x.png


BIN
AIEmoji/Assets.xcassets/AIChat/chat_send_minify.imageset/chat_send_minify@3x.png


+ 19 - 0
AIEmoji/Business/AIChat/TSAIChatContainerVC.swift

@@ -6,6 +6,11 @@
 //
 
 class TSAIChatContainerVC: TSBaseVC {
+    
+    
+    
+    var deleteBlock:(()->Void)?
+    
 
     lazy var vipBtn: UIButton = {
         let vipBtn = UIButton.createButton(image: UIImage(named: "nav_vip")) { [weak self]  in
@@ -83,6 +88,7 @@ class TSAIChatContainerVC: TSBaseVC {
         if viewModel.uiStyle == .history {
             addNormalNavBarView()
             setPageTitle("History".localized)
+            _ = setNavigationItem("", imageName: "delete_white", direction: .right, action: #selector(clickDelete))
         }else{
             navBarContentView.addSubview(navBarView)
             navBarView.snp.makeConstraints { make in
@@ -98,4 +104,17 @@ class TSAIChatContainerVC: TSBaseVC {
         }
         
     }
+    
+    
+    @objc func clickDelete(){
+        showCustomAlert(message: "Are you sure to delete".localized, deleteHandler:  { [weak self]  in
+            guard let self = self else { return }
+            viewModel.dbAIChatList.delete()
+        
+            deleteBlock?()
+            pop()
+        })
+        
+        
+    }
 }

+ 41 - 20
AIEmoji/Business/AIChat/TSAIChatHistoryVC/TSAIChatHistoryVC.swift

@@ -45,12 +45,15 @@ class TSAIChatHistoryVC: TSBaseVC {
         return pageNullView
     }()
     
+    var deleteAllBtn:UIButton = UIButton()
     
     override func createView() {
         
         addNormalNavBarView()
         setPageTitle("History".localized)
         
+        deleteAllBtn = setNavigationItem("", imageName: "delete_white", direction: .right, action: #selector(deleteAll))
+        
         contentView.addSubview(pageNullView)
         contentView.addSubview(collectionView)
         collectionView.snp.makeConstraints { make in
@@ -59,19 +62,24 @@ class TSAIChatHistoryVC: TSBaseVC {
     }
 
     override func dealThings() {
-        
-        pageNullView.isHidden = viewModel.historyModelChatList.count > 0 ? true : false
-        
-        
+        updateListView()
     }
     
-    
-    
-    
+
     func updateListView(){
         pageNullView.isHidden = viewModel.historyModelChatList.count > 0 ? true : false
+        deleteAllBtn.isHidden = !pageNullView.isHidden
         collectionView.reloadData()
     }
+    
+    @objc func deleteAll(){
+        showCustomAlert(message: "Are you sure to delete".localized, deleteHandler:  { [weak self]  in
+            guard let self = self else { return }
+            viewModel.deleteAll()
+            self.viewModel.historyModelChatList.removeAll()
+            self.updateListView()
+        })
+    }
 }
 
 extension TSAIChatHistoryVC: UICollectionViewDataSource ,UICollectionViewDelegate {
@@ -110,6 +118,16 @@ extension TSAIChatHistoryVC: UICollectionViewDataSource ,UICollectionViewDelegat
             let chatVC = TSAIChatContainerVC()
             chatVC.viewModel.uiStyle = .history
             chatVC.viewModel.dbAIChatList = itemModel
+            chatVC.deleteBlock = { [weak self]  in
+                guard let self = self else { return }
+                //删除 UI 层的 cell
+                sectionModel.chatList.remove(at: indexPath.item)
+                if sectionModel.chatList.count == 0 {
+                    self.viewModel.historyModelChatList.remove(at: indexPath.section)
+                }
+                updateListView()
+                
+            }
             self.navigationController?.pushViewController(chatVC, animated: true)
         }
     }
@@ -136,18 +154,21 @@ extension TSAIChatHistoryVC: SwipeCollectionViewCellDelegate {
         // 删除操作
         let deleteAction = SwipeAction(style: .destructive, title: nil) {[weak self] action, indexPath in
             guard let self = self else { return }
-            if let sectionModel = viewModel.historyModelChatList.safeObj(At: indexPath.section),
-            let itemModel = sectionModel.chatList.safeObj(At: indexPath.item){
-                sectionModel.chatList.remove(at: indexPath.item)
-                itemModel.delete()
-            
-                if sectionModel.chatList.count == 0 {
-                    viewModel.historyModelChatList.remove(at: indexPath.section)
-                }
+            showCustomAlert(message: "Are you sure to delete".localized, deleteHandler:  {
+                if let sectionModel = self.viewModel.historyModelChatList.safeObj(At: indexPath.section),
+                let itemModel = sectionModel.chatList.safeObj(At: indexPath.item){
+                    sectionModel.chatList.remove(at: indexPath.item)
+                    itemModel.delete()
                 
-                updateListView()
-            }
+                    if sectionModel.chatList.count == 0 {
+                        self.viewModel.historyModelChatList.remove(at: indexPath.section)
+                    }
+                    
+                    self.updateListView()
+                }
+            })
         }
+        
         deleteAction.backgroundColor = "#E83E3E".uiColor
         deleteAction.image = UIImage(named: "delete_white")
         return [deleteAction]
@@ -155,7 +176,7 @@ extension TSAIChatHistoryVC: SwipeCollectionViewCellDelegate {
     
     func collectionView(_ collectionView: UICollectionView, editActionsOptionsForItemAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> SwipeOptions {
         var options = SwipeOptions()
-        options.expansionStyle = .destructive(automaticallyDelete: false) // 完全滑动时是否自动触发操作
+//        options.expansionStyle = .destructive(automaticallyDelete: false) // 完全滑动时是否自动触发操作
         options.transitionStyle = .border // 滑动动画样式
         return options
     }
@@ -189,7 +210,7 @@ class TSAIChatHistoryCell: SwipeCollectionViewCell {
     var model:TSDBAIChatList?{
         didSet{
             if let dbMessage = model?.messages.first {
-                titleLabel.text = dbMessage.kind
+                titleLabel.text = dbMessage.kindValue
                 infoLabel.text = dbMessage.sentDate.dateTimeString
             }
         }
@@ -232,7 +253,7 @@ class TSAIChatHistorySectionHeaderView: UICollectionReusableView {
         titleLabel.snp.makeConstraints { make in
             make.leading.equalTo(16)
             make.trailing.equalTo(-16)
-            make.centerY.equalTo(16)
+            make.centerY.equalToSuperview()
         }
     }
     

+ 5 - 0
AIEmoji/Business/AIChat/TSAIChatHistoryVC/VM/TSAIChatHistoryVM.swift

@@ -69,4 +69,9 @@ class TSAIChatHistoryVM {
         return result
     }
 
+    
+    func deleteAll() {
+        
+        TSDBAIChatList.deleteAll()
+    }
 }

+ 8 - 9
AIEmoji/Business/AIChat/TSChatViewController/Layout/CustomMessageFlowLayout.swift

@@ -25,19 +25,18 @@ import MessageKit
 import UIKit
 
 // MARK: - CustomMessagesFlowLayout
-
 open class CustomMessagesFlowLayout: MessagesCollectionViewFlowLayout {
-  open lazy var customMessageSizeCalculator = CustomMessageSizeCalculator(layout: self)
+  lazy var customMessageSizeCalculator = TSTextLayoutSizeCalculator(layout: self)
 
   open override func cellSizeCalculatorForItem(at indexPath: IndexPath) -> CellSizeCalculator {
-    if isSectionReservedForTypingIndicator(indexPath.section) {
-      return typingIndicatorSizeCalculator
-    }
-    let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
-    if case .custom = message.kind {
+//    if isSectionReservedForTypingIndicator(indexPath.section) {
+//      return typingIndicatorSizeCalculator
+//    }
+//    let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
+//    if case .custom = message.kind {
       return customMessageSizeCalculator
-    }
-    return super.cellSizeCalculatorForItem(at: indexPath)
+//    }
+//    return super.cellSizeCalculatorForItem(at: indexPath)
   }
 
   open override func messageSizeCalculators() -> [MessageSizeCalculator] {

+ 0 - 209
AIEmoji/Business/AIChat/TSChatViewController/Models/CustomLayoutSizeCalculator.swift

@@ -1,209 +0,0 @@
-//
-//  CustomLayoutSizeCalculator.swift
-//  ChatExample
-//
-//  Created by Vignesh J on 01/05/21.
-//  Copyright © 2021 MessageKit. All rights reserved.
-//
-
-import MessageKit
-import UIKit
-
-class CustomLayoutSizeCalculator: CellSizeCalculator {
-  // MARK: Lifecycle
-
-  init(layout: MessagesCollectionViewFlowLayout? = nil) {
-    super.init()
-
-    self.layout = layout
-  }
-
-  // MARK: Internal
-
-  var cellTopLabelVerticalPadding: CGFloat = 32
-  var cellTopLabelHorizontalPadding: CGFloat = 32
-  var cellMessageContainerHorizontalPadding: CGFloat = 48
-  var cellMessageContainerExtraSpacing: CGFloat = 16
-  var cellMessageContentVerticalPadding: CGFloat = 16
-  var cellMessageContentHorizontalPadding: CGFloat = 16
-  var cellDateLabelHorizontalPadding: CGFloat = 24
-  var cellDateLabelBottomPadding: CGFloat = 8
-
-  var messagesLayout: MessagesCollectionViewFlowLayout {
-    layout as! MessagesCollectionViewFlowLayout
-  }
-
-  var messageContainerMaxWidth: CGFloat {
-    messagesLayout.itemWidth -
-      cellMessageContainerHorizontalPadding -
-      cellMessageContainerExtraSpacing
-  }
-
-  var messagesDataSource: MessagesDataSource {
-    self.messagesLayout.messagesDataSource
-  }
-
-  override func sizeForItem(at indexPath: IndexPath) -> CGSize {
-    let dataSource = messagesDataSource
-    let message = dataSource.messageForItem(
-      at: indexPath,
-      in: messagesLayout.messagesCollectionView)
-    let itemHeight = cellContentHeight(
-      for: message,
-      at: indexPath)
-    return CGSize(
-      width: messagesLayout.itemWidth,
-      height: itemHeight)
-  }
-
-  func cellContentHeight(
-    for message: MessageType,
-    at indexPath: IndexPath)
-    -> CGFloat
-  {
-    cellTopLabelSize(
-      for: message,
-      at: indexPath).height +
-      cellMessageBottomLabelSize(
-        for: message,
-        at: indexPath).height +
-      messageContainerSize(
-        for: message,
-        at: indexPath).height
-  }
-
-  // MARK: - Top cell Label
-
-  func cellTopLabelSize(
-    for message: MessageType,
-    at indexPath: IndexPath)
-    -> CGSize
-  {
-    guard
-      let attributedText = messagesDataSource.cellTopLabelAttributedText(
-        for: message,
-        at: indexPath) else
-    {
-      return .zero
-    }
-
-    let maxWidth = messagesLayout.itemWidth - cellTopLabelHorizontalPadding
-    let size = attributedText.size(consideringWidth: maxWidth)
-    let height = size.height + cellTopLabelVerticalPadding
-
-    return CGSize(
-      width: maxWidth,
-      height: height)
-  }
-
-  func cellTopLabelFrame(
-    for message: MessageType,
-    at indexPath: IndexPath)
-    -> CGRect
-  {
-    let size = cellTopLabelSize(
-      for: message,
-      at: indexPath)
-    guard size != .zero else {
-      return .zero
-    }
-
-    let origin = CGPoint(
-      x: cellTopLabelHorizontalPadding / 2,
-      y: 0)
-
-    return CGRect(
-      origin: origin,
-      size: size)
-  }
-
-  func cellMessageBottomLabelSize(
-    for message: MessageType,
-    at indexPath: IndexPath)
-    -> CGSize
-  {
-    guard
-      let attributedText = messagesDataSource.messageBottomLabelAttributedText(
-        for: message,
-        at: indexPath) else
-    {
-      return .zero
-    }
-    let maxWidth = messageContainerMaxWidth - cellDateLabelHorizontalPadding
-
-    return attributedText.size(consideringWidth: maxWidth)
-  }
-
-  func cellMessageBottomLabelFrame(
-    for message: MessageType,
-    at indexPath: IndexPath)
-    -> CGRect
-  {
-    let messageContainerSize = messageContainerSize(
-      for: message,
-      at: indexPath)
-    let labelSize = cellMessageBottomLabelSize(
-      for: message,
-      at: indexPath)
-    let x = messageContainerSize.width - labelSize.width - (cellDateLabelHorizontalPadding / 2)
-    let y = messageContainerSize.height - labelSize.height - cellDateLabelBottomPadding
-    let origin = CGPoint(
-      x: x,
-      y: y)
-
-    return CGRect(
-      origin: origin,
-      size: labelSize)
-  }
-
-  // MARK: - MessageContainer
-
-  func messageContainerSize(
-    for message: MessageType,
-    at indexPath: IndexPath)
-    -> CGSize
-  {
-    let labelSize = cellMessageBottomLabelSize(
-      for: message,
-      at: indexPath)
-    let width = labelSize.width +
-      cellMessageContentHorizontalPadding +
-      cellDateLabelHorizontalPadding
-    let height = labelSize.height +
-      cellMessageContentVerticalPadding +
-      cellDateLabelBottomPadding
-
-    return CGSize(
-      width: width,
-      height: height)
-  }
-
-  func messageContainerFrame(
-    for message: MessageType,
-    at indexPath: IndexPath,
-    fromCurrentSender: Bool)
-    -> CGRect
-  {
-    let y = cellTopLabelSize(
-      for: message,
-      at: indexPath).height
-    let size = messageContainerSize(
-      for: message,
-      at: indexPath)
-    let origin: CGPoint
-    if fromCurrentSender {
-      let x = messagesLayout.itemWidth -
-        size.width -
-        (cellMessageContainerHorizontalPadding / 2)
-      origin = CGPoint(x: x, y: y)
-    } else {
-      origin = CGPoint(
-        x: cellMessageContainerHorizontalPadding / 2,
-        y: y)
-    }
-
-    return CGRect(
-      origin: origin,
-      size: size)
-  }
-}

+ 0 - 78
AIEmoji/Business/AIChat/TSChatViewController/Models/CustomTextLayoutSizeCalculator.swift

@@ -1,78 +0,0 @@
-//
-//  CustomTextMessageSizeCalculator.swift
-//  ChatExample
-//
-//  Created by Vignesh J on 30/04/21.
-//  Copyright © 2021 MessageKit. All rights reserved.
-//
-
-import MessageKit
-import UIKit
-
-class CustomTextLayoutSizeCalculator: CustomLayoutSizeCalculator {
-  var messageLabelFont = UIFont.preferredFont(forTextStyle: .body)
-  var cellMessageContainerRightSpacing: CGFloat = 16
-
-  override func messageContainerSize(
-    for message: MessageType,
-    at indexPath: IndexPath)
-    -> CGSize
-  {
-    let size = super.messageContainerSize(
-      for: message,
-      at: indexPath)
-    let labelSize = messageLabelSize(
-      for: message,
-      at: indexPath)
-    let selfWidth = labelSize.width +
-      cellMessageContentHorizontalPadding +
-      cellMessageContainerRightSpacing
-    let width = max(selfWidth, size.width)
-    let height = size.height + labelSize.height
-
-    return CGSize(
-      width: width,
-      height: height)
-  }
-
-  func messageLabelSize(
-    for message: MessageType,
-    at _: IndexPath)
-    -> CGSize
-  {
-    let attributedText: NSAttributedString
-
-    let textMessageKind = message.kind
-    switch textMessageKind {
-    case .attributedText(let text):
-      attributedText = text
-    case .text(let text), .emoji(let text):
-      attributedText = NSAttributedString(string: text, attributes: [.font: messageLabelFont])
-    default:
-      fatalError("messageLabelSize received unhandled MessageDataType: \(message.kind)")
-    }
-
-    let maxWidth = messageContainerMaxWidth -
-      cellMessageContentHorizontalPadding -
-      cellMessageContainerRightSpacing
-
-    return attributedText.size(consideringWidth: maxWidth)
-  }
-
-  func messageLabelFrame(
-    for message: MessageType,
-    at indexPath: IndexPath)
-    -> CGRect
-  {
-    let origin = CGPoint(
-      x: cellMessageContentHorizontalPadding / 2,
-      y: cellMessageContentVerticalPadding / 2)
-    let size = messageLabelSize(
-      for: message,
-      at: indexPath)
-
-    return CGRect(
-      origin: origin,
-      size: size)
-  }
-}

+ 46 - 0
AIEmoji/Business/AIChat/TSChatViewController/Models/TSChatCellConfig.swift

@@ -0,0 +1,46 @@
+//
+//  TSChatCellConfig.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/13.
+//
+
+
+private let topSpaceH = 0.0
+private let bottomSpaceH = 0.0
+
+
+struct TSChatCellConfig {
+    
+
+    static var avatarSize = CGSizeMake(40, 40)
+    //MARK: ###################################左边头像###################################
+    //左边头像的
+    static var leadingAvatarEdge:UIEdgeInsets = UIEdgeInsets(top: topSpaceH, left: 16, bottom: 0, right: 0)
+ 
+    //MARK: ###################################右边头像###################################
+    //左边头像的
+    static var traingAvatarEdge:UIEdgeInsets = UIEdgeInsets(top: topSpaceH, left: 0, bottom: 0, right: 16)
+    
+    
+    //MARK: ###################################消息容器###################################
+    //消息容器的 UIEdgeInsets
+    static var cellMessageContainerEdge:UIEdgeInsets = UIEdgeInsets(top: topSpaceH, left: 8, bottom: 0, right: 16)
+    
+    static var cellMessageContainerMinSize:CGSize = CGSizeMake(48, 48)
+    
+//    static var cellMessageContainerMaxSize:CGSize{
+//        
+//        
+//        
+//        return CGSizeMake(40, 99999)
+//        
+//    }
+    
+    
+    
+    
+    //MARK: ###################################消息label###################################
+    static var cellMessageLabelEdge:UIEdgeInsets = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
+    
+}

+ 38 - 31
AIEmoji/Business/AIChat/TSChatViewController/Models/TSDBAIChatList.swift

@@ -10,22 +10,12 @@ import MessageKit
 
 //MARK: TSDBAIChatList - 用于存储会话及消息列表
 class TSDBAIChatList: Object {
-    @objc dynamic var sessionId = UUID().uuidString
-    dynamic var messages = List<TSDBChatMessage>()
-    @objc dynamic var creatTimestampInt:Int = 0
-    
-    
-    
-//    var messageList: [TSChatMessage] = []
-    
     override static func primaryKey() -> String? {
         return "sessionId"
     }
-
-//    //获取 App 层的 最新的 TSChatMessage 数组
-//    func getMessageListLatestData() {
-//        messageList = getMessageList()
-//    }
+    @objc dynamic var sessionId = UUID().uuidString
+    dynamic var messages = List<TSDBChatMessage>()
+    @objc dynamic var creatTimestampInt:Int = 0
     
     //获取 App 层的 TSChatMessage 数组
     func getMessageList() -> [TSChatMessage] {
@@ -54,6 +44,21 @@ class TSDBAIChatList: Object {
         TSRMShared.delete(self)
     }
     
+    static func deleteAll() {
+        do {
+            let realm = try Realm()
+            // 开始写入事务
+            try realm.write {
+                // 查询 Person 模型的所有对象
+                let allPersons = realm.objects(TSDBAIChatList.self)
+                // 删除所有查询到的对象
+                realm.delete(allPersons)
+            }
+            print("Person 模型的所有数据删除成功")
+        } catch {
+            print("删除 Person 模型数据时出错: \(error)")
+        }
+    }
     
     func updateMessage(msgModel:TSChatMessage){
         TSRMShared.writeThread {
@@ -95,7 +100,6 @@ extension TSDBAIChatList {
     
     static func getOneDB() -> TSDBAIChatList{
         let dbModel = TSDBAIChatList()
-//        TSRMShared.create(dbModel)
         return dbModel
     }
     
@@ -113,13 +117,14 @@ extension TSDBAIChatList {
 extension TSDBAIChatList {
     
     func getTSDBChatMessage(chatMsg:TSChatMessage) -> TSDBChatMessage {
+        let keyValue = chatMsg.kind.keyValue
+        
         let dbModel = TSDBChatMessage()
-        dbModel.messageId = chatMsg.messageId
-        dbModel.sentDate = chatMsg.sentDate
-        dbModel.kind = chatMsg.kind.kindString
         dbModel.user = TSDBChatUser(senderId: chatMsg.user.senderId, displayName: chatMsg.user.displayName)
         dbModel.messageId = chatMsg.messageId
-        
+        dbModel.sentDate = chatMsg.sentDate
+        dbModel.kindKey = keyValue.0
+        dbModel.kindValue = keyValue.1
         return dbModel
     }
     
@@ -128,27 +133,28 @@ extension TSDBAIChatList {
 
 //MARK: extension
 extension MessageKind {
-    static func fromString(_ string: String) -> MessageKind {
+
+    static func fromKeyValue(key string: String,value:String) -> MessageKind {
         if string == "text" {
-            return .text("")
+            return .text(value)
         } else if string == "attributedText" {
-            return .attributedText(NSAttributedString())
-        } else if string == "emoji" {
-            return .emoji("")
+            return .attributedText(kMDAttributedString(text: value))
         } else {
             return .custom(nil)
         }
     }
     
-    
-    var kindString:String{
+    var keyValue:(String,String){
         switch self {
-            case .text(let text), .emoji(let text): return text
-            default: return ""
+        case .text(let text):
+            return ("text",text)
+        case .attributedText(let attributedString):
+            return ("attributedText",attributedString.string)
+        default:
+            return ("default","")
         }
     }
     
-    
     static func textKind(_ string: String) -> MessageKind {
         return .text(string)
     }
@@ -160,15 +166,16 @@ extension MessageKind {
 class TSDBChatMessage: Object {
     @objc dynamic var messageId: String = UUID().uuidString
     @objc dynamic var sentDate: Date = Date()
-    @objc dynamic var kind: String = ""
     @objc dynamic var user: TSDBChatUser? = TSDBChatUser(senderId: "", displayName: "")
-
+    @objc dynamic var kindKey: String = ""
+    @objc dynamic var kindValue: String = ""
+    
     override static func primaryKey() -> String? {
         return "messageId"
     }
     
     func getTSChatMessage() -> TSChatMessage {
-        let model = TSChatMessage(kind: .textKind(kind), user: user!.getTSChatUser(), messageId: messageId, date: sentDate)
+        let model = TSChatMessage(kind: .fromKeyValue(key: kindKey, value: kindValue), user: user!.getTSChatUser(), messageId: messageId, date: sentDate)
         return model
     }
     

+ 76 - 0
AIEmoji/Business/AIChat/TSChatViewController/Models/TSLayoutSizeCalculator.swift

@@ -0,0 +1,76 @@
+//
+//  CustomLayoutSizeCalculator.swift
+//  ChatExample
+//
+//  Created by Vignesh J on 01/05/21.
+//  Copyright © 2021 MessageKit. All rights reserved.
+//
+
+import MessageKit
+import UIKit
+
+class TSLayoutSizeCalculator: MessageSizeCalculator {//CellSizeCalculator {
+    
+//    override init(layout: MessagesCollectionViewFlowLayout? = nil) {
+//        super.init()
+//        self.layout = layout
+//    }
+
+//    var messagesLayout: MessagesCollectionViewFlowLayout {
+//        layout as! MessagesCollectionViewFlowLayout
+//    }
+    
+    static let topSpaceH = 0.0
+    static let bottomSpaceH = 0.0
+    
+
+    static var avatarSize = CGSizeMake(40, 40)
+    //MARK: ###################################左边头像###################################
+    static var leadingAvatarEdge:UIEdgeInsets = UIEdgeInsets(top: topSpaceH, left: 16, bottom: 0, right: 0)
+    static var leadingAvatarSize = CGSizeMake(40, 40)
+    //MARK: ###################################右边头像###################################
+    static var traingAvatarEdge:UIEdgeInsets = UIEdgeInsets(top: topSpaceH, left: 0, bottom: 0, right: 0)
+    static var traingAvatarSize = CGSizeMake(0, 0)
+    
+    //MARK: ###################################消息容器###################################
+    //消息容器的 UIEdgeInsets
+    static var cellMessageContainerLeadingEdge:UIEdgeInsets = UIEdgeInsets(top: topSpaceH, left: 8, bottom: 0, right: 16)
+    static var cellMessageContainerTraingEdge:UIEdgeInsets = UIEdgeInsets(top: topSpaceH, left: 16, bottom: 0, right: 16)
+    static var cellMessageContainerMinSize:CGSize = CGSizeMake(48, 48)
+    
+    //消息容器最大宽度
+    lazy var messageContainerLeadingMaxWidth: CGFloat = {
+        //左头像
+        var cellMessageContainerMaxWidth = messagesLayout.itemWidth - Self.leadingAvatarEdge.left - Self.leadingAvatarSize.width
+        //容器 填充
+        cellMessageContainerMaxWidth = cellMessageContainerMaxWidth - Self.cellMessageContainerLeadingEdge.left - Self.cellMessageContainerLeadingEdge.right
+        return cellMessageContainerMaxWidth
+    }()
+
+    lazy var messageContainerTraingMaxWidth: CGFloat = {
+        //右头像
+        var cellMessageContainerMaxWidth = messagesLayout.itemWidth - Self.traingAvatarEdge.right - Self.traingAvatarSize.width
+        //容器 填充
+        cellMessageContainerMaxWidth = cellMessageContainerMaxWidth - Self.cellMessageContainerTraingEdge.left - Self.cellMessageContainerTraingEdge.right
+        return cellMessageContainerMaxWidth
+    }()
+
+
+    var messagesDataSource: MessagesDataSource {
+        self.messagesLayout.messagesDataSource
+    }
+    
+    //cell itemSize
+    override func sizeForItem(at indexPath: IndexPath) -> CGSize {
+        let dataSource = messagesDataSource
+        let message = dataSource.messageForItem(at: indexPath,in: messagesLayout.messagesCollectionView)
+        let itemHeight = cellContentHeight(for: message,at: indexPath,fromCurrentSender:dataSource.isFromCurrentSender(message: message))
+        return CGSize(width: messagesLayout.itemWidth,height: itemHeight)
+    }
+    
+    //消息容器的高度
+    func cellContentHeight(for message: MessageType,at indexPath: IndexPath,fromCurrentSender: Bool)-> CGFloat {
+        return Self.cellMessageContainerMinSize.height
+    }
+    
+}

+ 72 - 0
AIEmoji/Business/AIChat/TSChatViewController/Models/TSTextLayoutSizeCalculator.swift

@@ -0,0 +1,72 @@
+//
+//  CustomTextMessageSizeCalculator.swift
+//  ChatExample
+//
+//  Created by Vignesh J on 30/04/21.
+//  Copyright © 2021 MessageKit. All rights reserved.
+//
+
+import MessageKit
+import UIKit
+
+class TSTextLayoutSizeCalculator: TSLayoutSizeCalculator {
+    
+    
+    //MARK: ###################################消息label###################################
+    //消息容器的 UIEdgeInsets
+    static var cellMessagelabelEdge:UIEdgeInsets = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
+    static var messageLabelFont = UIFont.font(size: 16.0)
+    static var cellMessagelabelMinSize:CGSize = CGSizeMake(24, 24)
+    
+    
+    var cellMessagelabelLeadingMaxWidth:CGFloat{
+        messageContainerLeadingMaxWidth - Self.cellMessagelabelEdge.left - Self.cellMessagelabelEdge.right
+    }
+    
+    var cellMessagelabelTraingMaxWidth:CGFloat{
+        messageContainerTraingMaxWidth - Self.cellMessagelabelEdge.left - Self.cellMessagelabelEdge.right
+    }
+    
+    //消息容器的高度
+    override func cellContentHeight(for message: MessageType,at indexPath: IndexPath, fromCurrentSender: Bool)-> CGFloat {
+        let superH = super.cellContentHeight(for: message, at: indexPath, fromCurrentSender: fromCurrentSender)
+        let size = messageLabelSize(for: message, at: indexPath, fromCurrentSender: fromCurrentSender)
+        var height = size.height + Self.cellMessagelabelEdge.top + Self.cellMessagelabelEdge.bottom
+
+        if height < superH {
+            height = superH
+        }
+
+        return height
+    }
+    
+    func messageLabelSize(for message: MessageType,at _: IndexPath, fromCurrentSender: Bool) -> CGSize{
+        let attributedText: NSAttributedString
+//        var sendText = ""
+        let textMessageKind = message.kind
+        switch textMessageKind {
+        case .attributedText(let text):
+            attributedText = text
+//            sendText = text.string
+        case .text(let text), .emoji(let text):
+            attributedText = NSAttributedString(string: text, attributes: [.font: Self.messageLabelFont])
+//            sendText = text
+        default:
+            fatalError("messageLabelSize received unhandled MessageDataType: \(message.kind)")
+        }
+        let maxWidth = fromCurrentSender ? cellMessagelabelTraingMaxWidth: cellMessagelabelLeadingMaxWidth
+//        let textSize = sendText.height(ofFont: Self.messageLabelFont, maxWidth: maxWidth)
+        var size = attributedText.size(consideringWidth: maxWidth)
+        size.width = size.width + 10
+        let minSize = Self.cellMessagelabelMinSize
+        if size.width < minSize.width {
+            size.width = minSize.width
+        }
+        if size.height < minSize.height {
+            size.height = minSize.height
+        }
+        
+        return size
+    }
+    
+}

+ 162 - 0
AIEmoji/Business/AIChat/TSChatViewController/TSChatInputBarVC/TSChatInputBarVC.swift

@@ -0,0 +1,162 @@
+//
+//  TSChatInputBarVC.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/14.
+//
+
+import UIKit
+
+class TSChatInputBarVC: TSBaseVC, UITextViewDelegate {
+    
+    var sendComplete:(([Any])->Void)?
+    
+    lazy var InputBarView:UIView = {
+        let InputBarView = UIView()
+        InputBarView.backgroundColor = "#222222".uiColor
+        InputBarView.cornerRadius = 16.0
+        return InputBarView
+    }()
+    
+    lazy var sendBtn: UIButton = {
+        let sendBtn = UIButton.createButton(image: UIImage(named: "chat_send")) { [weak self]  in
+            guard let self = self else { return }
+            if let string = textView.text {
+                sendComplete?([string])
+            }
+            
+            textView.text = ""
+        }
+        return sendBtn
+    }()
+    
+    lazy var magnifyBtn: UIButton = {
+        let magnifyBtn = UIButton.createButton(image: UIImage(named: "chat_send_magnify")) { [weak self]  in
+            guard let self = self else { return }
+            let vc = TSChatInputFullScreenVC()
+            vc.text = textView.text
+            vc.sendComplete = { [weak self] date in
+                guard let self = self else { return }
+                sendComplete?(date)
+            }
+            
+            vc.closeComplete = { [weak self] text in
+                guard let self = self else { return }
+                textView.text = text
+                scrollTextViewToBottom()
+            }
+            
+            kPresentModalVC(target: self, modelVC: vc)
+        }
+        magnifyBtn.isHidden = true
+        return magnifyBtn
+    }()
+    
+    private let minHeight: CGFloat = 24
+    private let maxHeight: CGFloat = 154
+    lazy var textView: UITextView = {
+        let textView = UITextView()
+        textView.backgroundColor = .clear
+        textView.textColor = .white
+        textView.delegate = self
+        textView.font = .font(size: 16)
+        textView.clipsToBounds = true
+        textView.isScrollEnabled = false
+        textView.tintColor = .themeColor
+        textView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
+        return textView
+    }()
+
+  
+    override func createView() {
+        setNavBarViewHidden(true)
+        
+        view.backgroundColor = .clear
+        
+        contentView.addSubview(InputBarView)
+        InputBarView.addSubview(textView)
+        InputBarView.addSubview(magnifyBtn)
+        InputBarView.addSubview(sendBtn)
+        
+        InputBarView.snp.makeConstraints { make in
+            make.leading.equalTo(16)
+            make.trailing.equalTo(-16)
+            make.top.equalTo(12)
+            make.bottom.equalTo(-12)
+        }
+        
+        textView.snp.makeConstraints { make in
+            make.leading.equalTo(16)
+            make.trailing.equalTo(-60)
+            make.top.equalTo(18)
+            make.bottom.equalTo(-14)
+            make.height.equalTo(minHeight)
+        }
+        
+        sendBtn.snp.makeConstraints { make in
+            make.trailing.equalTo(-16)
+            make.bottom.equalTo(-16)
+            make.width.equalTo(24)
+            make.height.equalTo(24)
+        }
+        
+        magnifyBtn.snp.makeConstraints { make in
+            make.trailing.equalTo(-16)
+            make.top.equalTo(16)
+            make.width.equalTo(24)
+            make.height.equalTo(24)
+        }
+
+    }
+    
+    
+    override func dealThings() {
+        // 监听文本变化事件
+        NotificationCenter.default.addObserver(self, selector: #selector(textDidChange), name: UITextView.textDidChangeNotification, object: textView)
+    }
+
+    
+    func sendEnabled(enabled:Bool){
+        sendBtn.isEnabled = enabled
+    }
+
+    
+    deinit {
+        // 移除通知监听
+        NotificationCenter.default.removeObserver(self, name: UITextView.textDidChangeNotification, object: textView)
+    }
+}
+
+
+extension TSChatInputBarVC {
+
+    @objc private func textDidChange() {
+        // 计算输入框的内容高度
+        let sizeToFit = CGSize(width: textView.bounds.width, height: .greatestFiniteMagnitude)
+        let estimatedSize = textView.sizeThatFits(sizeToFit)
+
+        // 根据内容高度调整输入框的高度
+        var newHeight = estimatedSize.height
+        if newHeight < minHeight {
+            newHeight = minHeight
+        } else if newHeight > maxHeight {
+            newHeight = maxHeight
+            textView.isScrollEnabled = true
+            magnifyBtn.isHidden = false
+        } else {
+            textView.isScrollEnabled = false
+            magnifyBtn.isHidden = true
+        }
+
+        // 更新输入框的高度
+        textView.snp.updateConstraints { make in
+            make.height.equalTo(newHeight)
+        }
+    }
+
+    private func scrollTextViewToBottom() {
+        let bottomOffset = CGPoint(x: 0, y: textView.contentSize.height - textView.bounds.size.height)
+        textView.setContentOffset(bottomOffset, animated: true)
+    }
+    
+}

+ 135 - 0
AIEmoji/Business/AIChat/TSChatViewController/TSChatInputBarVC/TSChatInputFullScreenVC.swift

@@ -0,0 +1,135 @@
+//
+//  TSChatInputFullScreenVC.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/14.
+//
+
+import UIKit
+
+class TSChatInputFullScreenVC: TSBaseVC, UITextViewDelegate {
+    
+    var sendComplete:(([Any])->Void)?
+    var closeComplete:((String)->Void)?
+    var text:String?
+    
+    lazy var InputBarView:UIView = {
+        let InputBarView = UIView()
+        InputBarView.backgroundColor = "#222222".uiColor
+        InputBarView.cornerRadius = 16.0
+        return InputBarView
+    }()
+    
+    lazy var sendBtn: UIButton = {
+        let sendBtn = UIButton.createButton(image: UIImage(named: "chat_send")) { [weak self]  in
+            guard let self = self else { return }
+            if let string = textView.text {
+                sendComplete?([string])
+            }
+            textView.text = ""
+        }
+        return sendBtn
+    }()
+    
+    lazy var minificationBtn: UIButton = {
+        let minificationBtn = UIButton.createButton(image: UIImage(named: "chat_send_minify")) { [weak self]  in
+            guard let self = self else { return }
+            closeComplete?(textView.text)
+            self.dismiss(animated: true)
+        }
+        return minificationBtn
+    }()
+    
+    private let minHeight: CGFloat = 24
+    private let maxHeight: CGFloat = 154
+    lazy var textView: UITextView = {
+        let textView = UITextView()
+        textView.backgroundColor = .clear
+        textView.textColor = .white
+        textView.delegate = self
+        textView.font = .font(size: 16)
+        textView.clipsToBounds = true
+        textView.isScrollEnabled = true
+        textView.tintColor = .themeColor
+        textView.textContainerInset = UIEdgeInsets(top: 5, left: 0, bottom: 5, right: 0)
+        return textView
+    }()
+
+  
+    override func createView() {
+        setNavBarViewHidden(true)
+        edgesForExtendedLayout = []
+        
+        contentView.addSubview(InputBarView)
+        InputBarView.addSubview(textView)
+        InputBarView.addSubview(minificationBtn)
+        InputBarView.addSubview(sendBtn)
+        
+        InputBarView.snp.makeConstraints { make in
+            make.leading.equalTo(16)
+            make.trailing.equalTo(-16)
+            make.top.equalTo(12+k_Height_NavBar)
+            make.bottom.equalTo(-12-k_Height_safeAreaInsetsBottom())
+        }
+        
+        textView.snp.makeConstraints { make in
+            make.leading.equalTo(16)
+            make.trailing.equalTo(-60)
+            make.top.equalTo(16)
+            make.bottom.equalTo(-16)
+            make.height.equalTo(minHeight)
+        }
+        
+        sendBtn.snp.makeConstraints { make in
+            make.trailing.equalTo(-16)
+            make.bottom.equalTo(-16)
+            make.width.equalTo(24)
+            make.height.equalTo(24)
+        }
+        
+        minificationBtn.snp.makeConstraints { make in
+            make.trailing.equalTo(-16)
+            make.top.equalTo(16)
+            make.width.equalTo(24)
+            make.height.equalTo(24)
+        }
+
+    }
+    
+    
+    override func dealThings() {
+        // 监听文本变化事件
+//        NotificationCenter.default.addObserver(self, selector: #selector(textDidChange), name: UITextView.textDidChangeNotification, object: textView)
+        textView.text = text
+    }
+//
+//
+//    @objc private func textDidChange() {
+//        // 计算输入框的内容高度
+//        let sizeToFit = CGSize(width: textView.bounds.width, height: .greatestFiniteMagnitude)
+//        let estimatedSize = textView.sizeThatFits(sizeToFit)
+//
+//        // 根据内容高度调整输入框的高度
+//        var newHeight = estimatedSize.height
+//        if newHeight < minHeight {
+//            newHeight = minHeight
+//        } else if newHeight > maxHeight {
+//            newHeight = maxHeight
+//            textView.isScrollEnabled = true
+//        } else {
+//            textView.isScrollEnabled = false
+//        }
+//
+//        // 更新输入框的高度
+//        textView.snp.updateConstraints { make in
+//            make.height.equalTo(newHeight)
+//        }
+//    }
+
+    deinit {
+        // 移除通知监听
+        NotificationCenter.default.removeObserver(self, name: UITextView.textDidChangeNotification, object: textView)
+    }
+    
+
+}

+ 188 - 209
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController+Ex.swift

@@ -12,233 +12,212 @@ import MapKit
 // MARK: MessagesLayoutDelegate
 
 extension TSChatViewController: MessagesLayoutDelegate {
-  func cellTopLabelHeight(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CGFloat {
-    18
-  }
-
-  func cellBottomLabelHeight(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CGFloat {
-    17
-  }
-
-  func messageTopLabelHeight(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CGFloat {
-    20
-  }
-
-  func messageBottomLabelHeight(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CGFloat {
-    16
-  }
-    
-    
-    func textCellSizeCalculator(
-     for _: MessageType,
-     at _: IndexPath,
-     in _: MessagesCollectionView)
-     -> CellSizeCalculator?
-   {
-       self.textMessageSizeCalculator
-   }
+    func customCellSizeCalculator(for message: any MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator {
+        self.textMessageSizeCalculator
+    }
 }
 
 
 // MARK: MessagesDisplayDelegate
 
 extension TSChatViewController: MessagesDisplayDelegate {
-  // MARK: - Text Messages
-
-  func textColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
-    isFromCurrentSender(message: message) ? .white : .darkText
-  }
-
-  func detectorAttributes(for detector: DetectorType, and _: MessageType, at _: IndexPath) -> [NSAttributedString.Key: Any] {
-    switch detector {
-    case .hashtag, .mention: return [.foregroundColor: UIColor.blue]
-    default: return MessageLabel.defaultAttributes
+    // MARK: - Text Messages
+    
+    func textColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
+        return UIColor.mainBg
+        //      isFromCurrentSender(message: message) ? UIColor.mainBg : .white
     }
-  }
-
-  func enabledDetectors(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> [DetectorType] {
-    [.url, .address, .phoneNumber, .date, .transitInformation, .mention, .hashtag]
-  }
-
-  // MARK: - All Messages
-
-  func backgroundColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
-      isFromCurrentSender(message: message) ? .themeColor : UIColor(red: 230 / 255, green: 230 / 255, blue: 230 / 255, alpha: 1)
-  }
-
-  func messageStyle(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> MessageStyle {
-    let tail: MessageStyle.TailCorner = isFromCurrentSender(message: message) ? .bottomRight : .bottomLeft
-    if let image = UIImage(named: "bobbly") {
-        return .customImageTail(image, tail)
-    } else {
-        return .bubbleTail(tail, .curved)
-    }
-  }
-
-  func configureAvatarView(_ avatarView: AvatarView, for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) {
-//    let avatar = SampleData.shared.getAvatarFor(sender: message.sender)
-    //设置聊天头像
-      avatarView.set(avatar: Avatar(image: UIImage(named: "App-Icon")))
-  }
-
-  func configureMediaMessageImageView(
-    _ imageView: UIImageView,
-    for message: MessageType,
-    at _: IndexPath,
-    in _: MessagesCollectionView)
-  {
-    if case MessageKind.photo(let media) = message.kind, let imageURL = media.url {
-      imageView.kf.setImage(with: imageURL)
-    } else {
-      imageView.kf.cancelDownloadTask()
-    }
-  }
-
-  // MARK: - Location Messages
-
-  func annotationViewForLocation(message _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> MKAnnotationView? {
-    let annotationView = MKAnnotationView(annotation: nil, reuseIdentifier: nil)
-    let pinImage = #imageLiteral(resourceName: "ic_map_marker")
-    annotationView.image = pinImage
-    annotationView.centerOffset = CGPoint(x: 0, y: -pinImage.size.height / 2)
-    return annotationView
-  }
-
-  func animationBlockForLocation(
-    message _: MessageType,
-    at _: IndexPath,
-    in _: MessagesCollectionView) -> ((UIImageView) -> Void)?
-  {
-    { view in
-      view.layer.transform = CATransform3DMakeScale(2, 2, 2)
-      UIView.animate(
-        withDuration: 0.6,
-        delay: 0,
-        usingSpringWithDamping: 0.9,
-        initialSpringVelocity: 0,
-        options: [],
-        animations: {
-          view.layer.transform = CATransform3DIdentity
-        },
-        completion: nil)
-    }
-  }
-
-  func snapshotOptionsForLocation(
-    message _: MessageType,
-    at _: IndexPath,
-    in _: MessagesCollectionView)
+    
+    func detectorAttributes(for detector: DetectorType, and _: MessageType, at _: IndexPath) -> [NSAttributedString.Key: Any] {
+        switch detector {
+        case .hashtag, .mention: return [.foregroundColor: UIColor.blue]
+        default: return MessageLabel.defaultAttributes
+        }
+    }
+    
+    func enabledDetectors(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> [DetectorType] {
+        [.url, .address, .phoneNumber, .date, .transitInformation, .mention, .hashtag]
+    }
+    
+    // MARK: - All Messages
+    
+    func backgroundColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
+        isFromCurrentSender(message: message) ? .themeColor : "#333333".uiColor
+    }
+    
+    func messageStyle(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> MessageStyle {
+        let tail: MessageStyle.TailCorner = isFromCurrentSender(message: message) ? .bottomRight : .bottomLeft
+        if let image = UIImage(named: "bobbly") {
+            return .customImageTail(image, tail)
+        } else {
+            return .bubbleTail(tail, .curved)
+        }
+    }
+    
+    func configureAvatarView(_ avatarView: AvatarView, for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) {
+        //    let avatar = SampleData.shared.getAvatarFor(sender: message.sender)
+        //设置聊天头像
+        avatarView.set(avatar: Avatar(image: UIImage(named: "App-Icon")))
+    }
+    
+    func configureMediaMessageImageView(
+        _ imageView: UIImageView,
+        for message: MessageType,
+        at _: IndexPath,
+        in _: MessagesCollectionView)
+    {
+        if case MessageKind.photo(let media) = message.kind, let imageURL = media.url {
+            imageView.kf.setImage(with: imageURL)
+        } else {
+            imageView.kf.cancelDownloadTask()
+        }
+    }
+    
+    // MARK: - Location Messages
+    
+    func annotationViewForLocation(message _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> MKAnnotationView? {
+        let annotationView = MKAnnotationView(annotation: nil, reuseIdentifier: nil)
+        let pinImage = #imageLiteral(resourceName: "ic_map_marker")
+        annotationView.image = pinImage
+        annotationView.centerOffset = CGPoint(x: 0, y: -pinImage.size.height / 2)
+        return annotationView
+    }
+    
+    func animationBlockForLocation(
+        message _: MessageType,
+        at _: IndexPath,
+        in _: MessagesCollectionView) -> ((UIImageView) -> Void)?
+    {
+        { view in
+            view.layer.transform = CATransform3DMakeScale(2, 2, 2)
+            UIView.animate(
+                withDuration: 0.6,
+                delay: 0,
+                usingSpringWithDamping: 0.9,
+                initialSpringVelocity: 0,
+                options: [],
+                animations: {
+                    view.layer.transform = CATransform3DIdentity
+                },
+                completion: nil)
+        }
+    }
+    
+    func snapshotOptionsForLocation(
+        message _: MessageType,
+        at _: IndexPath,
+        in _: MessagesCollectionView)
     -> LocationMessageSnapshotOptions
-  {
-    LocationMessageSnapshotOptions(
-      showsBuildings: true,
-      showsPointsOfInterest: true,
-      span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10))
-  }
-
-  // MARK: - Audio Messages
-
-  func audioTintColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
-    isFromCurrentSender(message: message) ? .white : UIColor(red: 15 / 255, green: 135 / 255, blue: 255 / 255, alpha: 1.0)
-  }
-
-  func configureAudioCell(_ cell: AudioMessageCell, message: MessageType) {
-      
-  }
+    {
+        LocationMessageSnapshotOptions(
+            showsBuildings: true,
+            showsPointsOfInterest: true,
+            span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10))
+    }
+    
+    // MARK: - Audio Messages
+    
+    func audioTintColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
+        isFromCurrentSender(message: message) ? .white : UIColor(red: 15 / 255, green: 135 / 255, blue: 255 / 255, alpha: 1.0)
+    }
+    
+    func configureAudioCell(_ cell: AudioMessageCell, message: MessageType) {
+        
+    }
 }
 
 
 // MARK: MessageCellDelegate
 
 extension TSChatViewController: MessageCellDelegate {
-  func didTapAvatar(in _: MessageCollectionViewCell) {
-    print("Avatar tapped")
-  }
-
-  func didTapMessage(in _: MessageCollectionViewCell) {
-    print("Message tapped")
-  }
-
-  func didTapImage(in _: MessageCollectionViewCell) {
-    print("Image tapped")
-  }
-
-  func didTapCellTopLabel(in _: MessageCollectionViewCell) {
-    print("Top cell label tapped")
-  }
-
-  func didTapCellBottomLabel(in _: MessageCollectionViewCell) {
-    print("Bottom cell label tapped")
-  }
-
-  func didTapMessageTopLabel(in _: MessageCollectionViewCell) {
-    print("Top message label tapped")
-  }
-
-  func didTapMessageBottomLabel(in _: MessageCollectionViewCell) {
-    print("Bottom label tapped")
-  }
-
-  func didTapPlayButton(in cell: AudioMessageCell) {
-    guard
-      let indexPath = messagesCollectionView.indexPath(for: cell),
-      let message = messagesCollectionView.messagesDataSource?.messageForItem(at: indexPath, in: messagesCollectionView)
-    else {
-      print("Failed to identify message when audio cell receive tap gesture")
-      return
-    }
-  }
-
-  func didStartAudio(in _: AudioMessageCell) {
-    print("Did start playing audio sound")
-  }
-
-  func didPauseAudio(in _: AudioMessageCell) {
-    print("Did pause audio sound")
-  }
-
-  func didStopAudio(in _: AudioMessageCell) {
-    print("Did stop audio sound")
-  }
-
-  func didTapAccessoryView(in _: MessageCollectionViewCell) {
-    print("Accessory view tapped")
-  }
+    func didTapAvatar(in _: MessageCollectionViewCell) {
+        print("Avatar tapped")
+    }
+    
+    func didTapMessage(in _: MessageCollectionViewCell) {
+        print("Message tapped")
+    }
+    
+    func didTapImage(in _: MessageCollectionViewCell) {
+        print("Image tapped")
+    }
+    
+    func didTapCellTopLabel(in _: MessageCollectionViewCell) {
+        print("Top cell label tapped")
+    }
+    
+    func didTapCellBottomLabel(in _: MessageCollectionViewCell) {
+        print("Bottom cell label tapped")
+    }
+    
+    func didTapMessageTopLabel(in _: MessageCollectionViewCell) {
+        print("Top message label tapped")
+    }
+    
+    func didTapMessageBottomLabel(in _: MessageCollectionViewCell) {
+        print("Bottom label tapped")
+    }
+    
+    func didTapPlayButton(in cell: AudioMessageCell) {
+        guard
+            let indexPath = messagesCollectionView.indexPath(for: cell),
+            let message = messagesCollectionView.messagesDataSource?.messageForItem(at: indexPath, in: messagesCollectionView)
+        else {
+            print("Failed to identify message when audio cell receive tap gesture")
+            return
+        }
+    }
+    
+    func didStartAudio(in _: AudioMessageCell) {
+        print("Did start playing audio sound")
+    }
+    
+    func didPauseAudio(in _: AudioMessageCell) {
+        print("Did pause audio sound")
+    }
+    
+    func didStopAudio(in _: AudioMessageCell) {
+        print("Did stop audio sound")
+    }
+    
+    func didTapAccessoryView(in _: MessageCollectionViewCell) {
+        print("Accessory view tapped")
+    }
 }
 
 
 // MARK: MessageLabelDelegate
 
 extension TSChatViewController: MessageLabelDelegate {
-  func didSelectAddress(_ addressComponents: [String: String]) {
-    print("Address Selected: \(addressComponents)")
-  }
-
-  func didSelectDate(_ date: Date) {
-    print("Date Selected: \(date)")
-  }
-
-  func didSelectPhoneNumber(_ phoneNumber: String) {
-    print("Phone Number Selected: \(phoneNumber)")
-  }
-
-  func didSelectURL(_ url: URL) {
-    print("URL Selected: \(url)")
-  }
-
-  func didSelectTransitInformation(_ transitInformation: [String: String]) {
-    print("TransitInformation Selected: \(transitInformation)")
-  }
-
-  func didSelectHashtag(_ hashtag: String) {
-    print("Hashtag selected: \(hashtag)")
-  }
-
-  func didSelectMention(_ mention: String) {
-    print("Mention selected: \(mention)")
-  }
-
-  func didSelectCustom(_ pattern: String, match _: String?) {
-    print("Custom data detector patter selected: \(pattern)")
-  }
+    func didSelectAddress(_ addressComponents: [String: String]) {
+        print("Address Selected: \(addressComponents)")
+    }
+    
+    func didSelectDate(_ date: Date) {
+        print("Date Selected: \(date)")
+    }
+    
+    func didSelectPhoneNumber(_ phoneNumber: String) {
+        print("Phone Number Selected: \(phoneNumber)")
+    }
+    
+    func didSelectURL(_ url: URL) {
+        print("URL Selected: \(url)")
+    }
+    
+    func didSelectTransitInformation(_ transitInformation: [String: String]) {
+        print("TransitInformation Selected: \(transitInformation)")
+    }
+    
+    func didSelectHashtag(_ hashtag: String) {
+        print("Hashtag selected: \(hashtag)")
+    }
+    
+    func didSelectMention(_ mention: String) {
+        print("Mention selected: \(mention)")
+    }
+    
+    func didSelectCustom(_ pattern: String, match _: String?) {
+        print("Custom data detector patter selected: \(pattern)")
+    }
 }

+ 72 - 40
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController.swift

@@ -43,7 +43,26 @@ class TSChatViewController: MessagesViewController, MessagesDataSource {
     }()
     
     // MARK: Private
-    lazy var textMessageSizeCalculator = CustomTextLayoutSizeCalculator(layout: self.messagesCollectionView.messagesCollectionViewFlowLayout)
+    lazy var textMessageSizeCalculator = TSTextLayoutSizeCalculator(layout: self.messagesCollectionView.messagesCollectionViewFlowLayout)
+    
+    
+    lazy var inputBarVC: TSChatInputBarVC = {
+        let inputBarVC = TSChatInputBarVC()
+        inputBarVC.sendComplete = { [weak self] components in
+            guard let self = self else { return }
+            sendMessages(components)
+            messagesCollectionView.scrollToLastItem(animated: true)
+        }
+        return inputBarVC
+    }()
+    
+    
+    
+    lazy var text: UILabel = {
+        let textLabel = UILabel.createLabel(text: "Remaining 3 free times")
+        return textLabel
+    }()
+    
     
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -87,13 +106,14 @@ class TSChatViewController: MessagesViewController, MessagesDataSource {
     }
     
     func configureMessageCollectionView() {
-        if let flowLayout = messagesCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
-            flowLayout.sectionInset = UIEdgeInsets(top: 4, left: 0, bottom: 4, right: 0)
-        }
-        
-        messagesCollectionView.backgroundColor = .clear
         view.backgroundColor = .clear
-        messagesCollectionView.register(CustomTextMessageContentCell.self)
+        //设置自定义FlowLayout,itemsize等,都在这里控制
+        let flowLayout = CustomMessagesFlowLayout()
+        flowLayout.sectionInset = UIEdgeInsets(top: 4, left: 0, bottom: 4, right: 0)
+    
+        messagesCollectionView.collectionViewLayout = flowLayout
+        messagesCollectionView.backgroundColor = .clear
+        messagesCollectionView.register(TSTextMessageContentCell.self)
         messagesCollectionView.messagesLayoutDelegate = self
         messagesCollectionView.messagesDisplayDelegate = self
         messagesCollectionView.messagesDataSource = self
@@ -102,17 +122,40 @@ class TSChatViewController: MessagesViewController, MessagesDataSource {
         scrollsToLastItemOnKeyboardBeginsEditing = true // default false
         maintainPositionOnInputBarHeightChanged = true // default false
         showMessageTimestampOnSwipeLeft = false // default false
-        
         //        messagesCollectionView.refreshControl = refreshControl
         messagesCollectionView.reloadData()
     }
     
     func configureMessageInputBar() {
-        messageInputBar.delegate = self
-        messageInputBar.inputTextView.tintColor = .themeColor
-        messageInputBar.sendButton.setTitleColor(.themeColor, for: .normal)
-        messageInputBar.sendButton.setTitleColor(
-            UIColor.themeColor.withAlphaComponent(0.3),for: .highlighted)
+        let clearView = UIView()
+        clearView.backgroundColor = .clear
+
+        if viewModel.uiStyle == .chat {
+            clearView.addSubview(inputBarVC.view)
+            inputBarVC.view.snp.makeConstraints { make in
+                make.leading.equalTo(0)
+                make.trailing.equalTo(0)
+                make.top.equalTo(0)
+                make.bottom.equalTo(0)
+            }
+        }
+        inputBarType = .custom(clearView)
+        
+        
+        let redView = UIView()
+        redView.backgroundColor = .red
+        view.addSubview(redView)
+        redView.snp.makeConstraints { make in
+            make.leading.equalTo(20)
+            make.trailing.equalTo(-20)
+            make.height.equalTo(30)
+//            make.bottom.equalTo(-10)
+            make.bottom.equalTo(inputContainerView.snp.top).offset(-10)
+            
+        }
+        
+        
+        
     }
     
     // MARK: - Helpers
@@ -122,6 +165,7 @@ class TSChatViewController: MessagesViewController, MessagesDataSource {
         }
         return IndexPath(item: messageList.count - 1, section: 0)
     }
+    
     func insertMessage(_ message: TSChatMessage) {
         messageList.append(message)
         messagesCollectionView.performBatchUpdates({
@@ -141,7 +185,6 @@ class TSChatViewController: MessagesViewController, MessagesDataSource {
         return messagesCollectionView.indexPathsForVisibleItems.contains(lastIndexPath)
     }
     
-    
     private let formatter: DateFormatter = {
         let formatter = DateFormatter()
         formatter.dateStyle = .medium
@@ -211,7 +254,7 @@ extension TSChatViewController {
     -> UICollectionViewCell?
     {
         let cell = messagesCollectionView.dequeueReusableCell(
-            CustomTextMessageContentCell.self,
+            TSTextMessageContentCell.self,
             for: indexPath)
         cell.configure(
             with: message,
@@ -287,47 +330,36 @@ extension TSChatViewController: InputBarAccessoryViewDelegate {
             return
         }
         
-        
-        let message = TSChatMessage(text: "...", user: viewModel.kAIUser, messageId: UUID().uuidString, date: Date())
+        let message = TSChatMessage(attributedText: kMDAttributedString(text: ""), user: viewModel.kAIUser, messageId: UUID().uuidString, date: Date())
         message.sendState = .start
         insertMessage(message)
         
-        
+        inputBarVC.sendEnabled(enabled: false)
         viewModel.sendChatMessage(message: messageString) {[weak self] string in
-            print("\(string)")
-            
-            self?.updataAIChatCell(string: string)
+            guard let self = self else { return }
+            debugPrint("viewModel.AiMDString=\(viewModel.AiMDString)")
+            message.kind = .attributedText(kMDAttributedString(text: viewModel.AiMDString))
+            message.sendState = .progress(0.5)
+            updataAIChatCellUI()
             
         } completion: {[weak self] data, error in
-            
             guard let self = self else { return }
             if let netData = data {
                 message.sendState = .success("netData")
-                
                 //保存这条消息到本地数据库
             }else {
-                message.kind = .text(kAIErrorString)
+                message.kind = .attributedText(kMDAttributedString(text: kAIErrorString))
                 message.sendState = .failed(kAIErrorString)
                 //保存这条消息到本地数据库
             }
-            
             updataAIChatCellUI()
+            
+            kExecuteOnMainThread {
+                self.inputBarVC.sendEnabled(enabled: true)
+            }
         }
     }
     
-    func updataAIChatCell(string:String){
-        
-        guard let generativeAIMessage = messageList.last else { return }
-        var message = ""
-        if case let .text(msg) = generativeAIMessage.kind {
-            message = msg + string
-        }
-        generativeAIMessage.kind = .text(message)
-        generativeAIMessage.sendState = .progress(0.5)
-        
-        updataAIChatCellUI()
-    }
-    
     func updataAIChatCellUI(){
         kExecuteOnMainThread {
             if self.messageList.count >= 2 {
@@ -338,9 +370,9 @@ extension TSChatViewController: InputBarAccessoryViewDelegate {
                 self.messagesCollectionView.reloadData()
             }
             
-            if self.isLastSectionVisible() == true {
+//            if self.isLastSectionVisible() == true {
                 self.messagesCollectionView.scrollToLastItem(animated: false)
-            }
+//            }
         }
     }
     

+ 31 - 3
AIEmoji/Business/AIChat/TSChatViewController/ViewModel/TSAIChatVM.swift

@@ -25,6 +25,11 @@ class TSAIChatVM {
     
     let kAIUser = TSChatUser(senderId: "000", displayName: "AI")
     let kUserSender = TSChatUser(senderId: "001", displayName: "001")
+    
+    
+    //ai markDown 回答的string
+    var AiMDString:String = ""
+
 }
 
 
@@ -40,7 +45,11 @@ extension TSAIChatVM {
             "sessionId": dbAIChatList.sessionId,
             "message": message
         ]
-        streamRequest = TSNetworkShared.postStream(urlType: .chat,parameters: parameters) { string in
+        
+        AiMDString = ""
+        streamRequest = TSNetworkShared.postStream(urlType: .chat,parameters: parameters) {[weak self] string in
+            guard let self = self else { return }
+            AiMDString = AiMDString + string
             streamHandler(string)
         } completion: { result in
             switch result {
@@ -65,8 +74,7 @@ extension TSAIChatVM {
             return self.dbAIChatList.getMessageList()
         }else {
             let aiString = "I can tackle your questions, my skillset includes, but is not limited to:\n\n📧 Composing high-quality emails  \n🇺🇸 Facilitating language learning  \n📑 Assisting in your studies  \n💡 Brainstorming ideas and much more!"
-            let msg = TSChatMessage(kind: .text(aiString), user: kAIUser, messageId: "", date: Date())
-            
+            let msg = TSChatMessage(kind: .attributedText(kMDAttributedString(text: aiString)), user: kAIUser, messageId: "", date: Date())
             return [msg]
         }
     }
@@ -90,3 +98,23 @@ extension TSAIChatVM {
         
     }
 }
+
+
+
+import SwiftyMarkdown
+private var md: SwiftyMarkdown = {
+    let md = SwiftyMarkdown(string: "")
+    md.setFontColorForAllStyles(with: .white)
+    md.code.color = .red
+    
+    return md
+}()
+
+//字符串转成 markdown NSAttributedString
+func kMDAttributedString(text:String) -> NSAttributedString{
+    return md.attributedString(from: text)
+}
+
+
+
+

+ 170 - 0
AIEmoji/Business/AIChat/TSChatViewController/Views/TSMessageContentCell.swift

@@ -0,0 +1,170 @@
+//
+//  CustomMessageContentCell.swift
+//  ChatExample
+//
+//  Created by Vignesh J on 01/05/21.
+//  Copyright © 2021 MessageKit. All rights reserved.
+//
+
+import MessageKit
+import UIKit
+
+class TSMessageContentCell: MessageCollectionViewCell {
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+        setupSubviews()
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+        setupSubviews()
+    }
+    
+    /// The `MessageCellDelegate` for the cell.
+    weak var delegate: MessageCellDelegate?
+    
+    
+    /// 消息内容容器.frame由 子视图自动布局撑起大小
+    var messageContainerView: UIView = {
+        let containerView = UIView()
+        containerView.cornerRadius = 16
+        return containerView
+    }()
+
+    // 左头像
+    var leadingAvatarImageView: UIImageView = {
+        let imageView = UIImageView.createImageView(imageName: "aichat_avatar")
+        return imageView
+    }()
+    
+    // 右头像(预留)
+    var trailingAvatarImageView: UIImageView = {
+        let imageView = UIImageView.createImageView(imageName: "aichat_avatar")
+        imageView.isHidden = true
+        return imageView
+    }()
+    
+    
+    //初始化 cell
+    func setupSubviews() {
+        contentView.addSubview(leadingAvatarImageView)
+        contentView.addSubview(messageContainerView)
+        contentView.addSubview(trailingAvatarImageView)
+        
+        
+        let leadingAvatarEdge = TSLayoutSizeCalculator.leadingAvatarEdge
+        let leadingAvatarSize = TSLayoutSizeCalculator.leadingAvatarSize
+        leadingAvatarImageView.snp.makeConstraints { make in
+            make.leading.equalTo(leadingAvatarEdge.left)
+            make.top.equalTo(leadingAvatarEdge.top)
+            make.size.equalTo(leadingAvatarSize)
+        }
+        
+        //暂时没用,预留的,数值都是 0
+        let traingAvatarEdge = TSLayoutSizeCalculator.traingAvatarEdge
+        let trainggAvatarSize = TSLayoutSizeCalculator.traingAvatarSize
+        trailingAvatarImageView.snp.makeConstraints { make in
+            make.trailing.equalTo(traingAvatarEdge.right)
+            make.top.equalTo(traingAvatarEdge.top)
+            make.size.equalTo(trainggAvatarSize)
+        }
+        
+        let cellMessageContainerEdge = TSLayoutSizeCalculator.cellMessageContainerLeadingEdge
+        messageContainerView.snp.makeConstraints { make in
+            make.leading.equalTo(leadingAvatarImageView.snp.trailing).offset(cellMessageContainerEdge.left)
+            make.trailing.lessThanOrEqualTo(trailingAvatarImageView.snp.leading).offset(-16)
+            make.top.equalTo(cellMessageContainerEdge.top)
+            make.bottom.equalTo(-cellMessageContainerEdge.bottom)
+        }
+    }
+    
+
+    
+    override func prepareForReuse() {
+        super.prepareForReuse()
+    }
+    
+    // 点击触发事件
+    override func handleTapGesture(_ gesture: UIGestureRecognizer) {
+        let touchLocation = gesture.location(in: self)
+        
+        switch true {
+        case messageContainerView.frame
+                .contains(touchLocation) && !cellContentView(canHandle: convert(touchLocation, to: messageContainerView)):
+            delegate?.didTapMessage(in: self)
+//        case cellTopLabel.frame.contains(touchLocation):
+//            delegate?.didTapCellTopLabel(in: self)
+//    case cellDateLabel.frame.contains(touchLocation):
+//      delegate?.didTapMessageBottomLabel(in: self)
+        default:
+            delegate?.didTapBackground(in: self)
+        }
+    }
+    
+    /// Handle long press gesture, return true when gestureRecognizer's touch point in `messageContainerView`'s frame
+    override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
+        let touchPoint = gestureRecognizer.location(in: self)
+        guard gestureRecognizer.isKind(of: UILongPressGestureRecognizer.self) else { return false }
+        return messageContainerView.frame.contains(touchPoint)
+    }
+
+    //设置更新内容
+    func configure(
+        with message: MessageType,
+        at indexPath: IndexPath,
+        in messagesCollectionView: MessagesCollectionView,
+        dataSource: MessagesDataSource,
+        and sizeCalculator: TSLayoutSizeCalculator)
+    {
+        guard let displayDelegate = messagesCollectionView.messagesDisplayDelegate else {
+            return
+        }
+//        cellTopLabel.frame = sizeCalculator.cellTopLabelFrame(
+//            for: message,
+//            at: indexPath)
+//        
+//        cellTopLabel.attributedText = dataSource.cellTopLabelAttributedText(
+//            for: message,
+//            at: indexPath)
+        //    cellDateLabel.frame = sizeCalculator.cellMessageBottomLabelFrame(
+        //      for: message,
+        //      at: indexPath)
+        //      cellDateLabel.attributedText = dataSource.messageBottomLabelAttributedText(
+        //        for: message,
+        //        at: indexPath)
+        
+        
+        
+
+        //是否是当前用户发送的消息
+        let fromCurrentSender = dataSource.isFromCurrentSender(message: message)
+        if fromCurrentSender {
+            leadingAvatarImageView.isHidden = true
+            let cellMessageContainerEdge = TSLayoutSizeCalculator.cellMessageContainerTraingEdge
+            messageContainerView.snp.remakeConstraints { make in
+                make.leading.greaterThanOrEqualTo(cellMessageContainerEdge.left)
+                make.trailing.equalTo(-cellMessageContainerEdge.right)
+                make.top.equalTo(cellMessageContainerEdge.top)
+                make.bottom.equalTo(-cellMessageContainerEdge.bottom)
+            }
+        }else{
+            leadingAvatarImageView.isHidden = false
+            let cellMessageContainerEdge = TSLayoutSizeCalculator.cellMessageContainerLeadingEdge
+            messageContainerView.snp.remakeConstraints { make in
+                make.leading.equalTo(leadingAvatarImageView.snp.trailing).offset(cellMessageContainerEdge.left)
+                make.trailing.lessThanOrEqualTo(-cellMessageContainerEdge.right)
+                make.top.equalTo(cellMessageContainerEdge.top)
+                make.bottom.equalTo(-cellMessageContainerEdge.bottom)
+            }
+        }
+        
+        messageContainerView.backgroundColor = displayDelegate.backgroundColor(for: message,at: indexPath,in: messagesCollectionView)
+    }
+    
+    /// Handle `ContentView`'s tap gesture, return false when `ContentView` doesn't needs to handle gesture
+    func cellContentView(canHandle _: CGPoint) -> Bool {
+        false
+    }
+}

+ 97 - 0
AIEmoji/Business/AIChat/TSChatViewController/Views/TSTextMessageContentCell.swift

@@ -0,0 +1,97 @@
+//
+//  CustomTextMessageContentCell.swift
+//  ChatExample
+//
+//  Created by Vignesh J on 01/05/21.
+//  Copyright © 2021 MessageKit. All rights reserved.
+//
+
+import MessageKit
+import UIKit
+import SwiftyMarkdown
+class TSTextMessageContentCell: TSMessageContentCell {
+    var messageLabel: UILabel = {
+        let label = UILabel.createLabel(font: .font(size: 16),numberOfLines: 0)
+        label.lineBreakMode = .byWordWrapping
+        return label
+    }()
+    
+    override func prepareForReuse() {
+        super.prepareForReuse()
+    }
+    
+    override func setupSubviews() {
+        super.setupSubviews()
+        
+        let labelEdge = TSTextLayoutSizeCalculator.cellMessagelabelEdge
+        messageContainerView.addSubview(messageLabel)
+        messageLabel.snp.makeConstraints { make in
+            make.edges.equalTo(labelEdge)
+            make.width.height.equalTo(0)
+        }
+        
+        
+    }
+    
+    override func configure(
+        with message: MessageType,
+        at indexPath: IndexPath,
+        in messagesCollectionView: MessagesCollectionView,
+        dataSource: MessagesDataSource,
+        and sizeCalculator: TSLayoutSizeCalculator)
+    {
+        super.configure(
+            with: message,
+            at: indexPath,
+            in: messagesCollectionView,
+            dataSource: dataSource,
+            and: sizeCalculator)
+        
+        guard let displayDelegate = messagesCollectionView.messagesDisplayDelegate else {
+            return
+        }
+        
+        let calculator = sizeCalculator as? TSTextLayoutSizeCalculator
+        let labelFrame = calculator?.messageLabelSize(for: message, at: indexPath,fromCurrentSender: dataSource.isFromCurrentSender(message: message)) ?? TSTextLayoutSizeCalculator.cellMessagelabelMinSize
+        messageLabel.snp.updateConstraints { make in
+            make.width.equalTo(labelFrame.width)
+            make.height.equalTo(labelFrame.height)
+        }
+        
+        let textMessageKind = message.kind
+        switch textMessageKind {
+        case .text(let text), .emoji(let text):
+            let textColor = displayDelegate.textColor(for: message, at: indexPath, in: messagesCollectionView)
+            messageLabel.textColor = textColor
+            messageLabel.text = text
+            debugPrint("text赋值")
+        case .attributedText(let text):
+            messageLabel.attributedText = text
+//            debugPrint("attributedText赋值")
+        default:
+            break
+        }
+    }
+    
+    
+    func extractAndPrintSubstring(from sourceString: String, to targetString: String) {
+        // 查找 sourceString 在 targetString 中的结束位置
+        if let range = targetString.range(of: sourceString) {
+            // 获取 sourceString 在 targetString 中的结束位置
+            let startIdx = range.upperBound
+            // 提取从该位置到 targetString 结束的子字符串
+            let extractedSubstring = targetString[startIdx...]
+            
+            // 将提取的子字符串逐个字符输出
+            for character in extractedSubstring {
+                print(character)
+                messageLabel.text = (messageLabel.text ?? "") + String(character)
+                messageLabel.attributedText = SwiftyMarkdown(string: messageLabel.text ?? "").attributedString()
+            }
+        } else {
+            print("未找到 sourceString")
+            messageLabel.text = targetString
+            messageLabel.attributedText = SwiftyMarkdown(string: targetString).attributedString()
+        }
+    }
+}