聊天详情页面
|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "back_black@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "back_black@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/back_black.imageset/back_black@2x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 368 B | 
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/back_black.imageset/back_black@3x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 735 B | 
							
								
								
									
										22
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_back_white.imageset/Contents.json
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_back_white@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_back_white@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_back_white.imageset/role_back_white@2x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 377 B | 
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_back_white.imageset/role_back_white@3x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 768 B | 
							
								
								
									
										22
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_change_close.imageset/Contents.json
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_change_close@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_change_close@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_change_close.imageset/role_chat_change_close@2x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 2.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_change_close.imageset/role_chat_change_close@3x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 6.1 KiB | 
							
								
								
									
										22
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_change_normal.imageset/Contents.json
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_change_normal@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_change_normal@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
| After Width: | Height: | Size: 1.9 KiB | 
| After Width: | Height: | Size: 4.5 KiB | 
							
								
								
									
										22
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_change_open.imageset/Contents.json
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_change_open@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_change_open@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_change_open.imageset/role_chat_change_open@2x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 2.0 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_change_open.imageset/role_chat_change_open@3x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 4.8 KiB | 
							
								
								
									
										22
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_expand_close.imageset/Contents.json
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_expand_close@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_expand_close@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_expand_close.imageset/role_chat_expand_close@2x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 355 B | 
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_expand_close.imageset/role_chat_expand_close@3x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 764 B | 
							
								
								
									
										22
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_expand_open.imageset/Contents.json
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_expand_open@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_expand_open@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_expand_open.imageset/role_chat_expand_open@2x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 336 B | 
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_expand_open.imageset/role_chat_expand_open@3x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 731 B | 
							
								
								
									
										22
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_expand_phone.imageset/Contents.json
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_expand_phone@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_expand_phone@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_expand_phone.imageset/role_chat_expand_phone@2x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 2.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_expand_phone.imageset/role_chat_expand_phone@3x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 5.0 KiB | 
							
								
								
									
										22
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_keyboard.imageset/Contents.json
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_keyboard@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_keyboard@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_keyboard.imageset/role_chat_keyboard@2x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 1.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_keyboard.imageset/role_chat_keyboard@3x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 3.9 KiB | 
							
								
								
									
										22
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_phone_vip.imageset/Contents.json
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_phone_vip@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_phone_vip@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_phone_vip.imageset/role_chat_phone_vip@2x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 1017 B | 
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_phone_vip.imageset/role_chat_phone_vip@3x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 2.1 KiB | 
							
								
								
									
										22
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_remind_voice.imageset/Contents.json
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_remind_voice@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_remind_voice@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_remind_voice.imageset/role_chat_remind_voice@2x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 599 B | 
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_remind_voice.imageset/role_chat_remind_voice@3x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 1.1 KiB | 
|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_send@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_send@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_send.imageset/role_chat_send@2x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 12 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_send.imageset/role_chat_send@3x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 26 KiB | 
							
								
								
									
										22
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_setting.imageset/Contents.json
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_setting@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_setting@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_setting.imageset/role_chat_setting@2x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_setting.imageset/role_chat_setting@3x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 2.8 KiB | 
|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_vip@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_vip@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_vip.imageset/role_chat_vip@2x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 1.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_vip.imageset/role_chat_vip@3x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 2.4 KiB | 
							
								
								
									
										22
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_voice.imageset/Contents.json
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_voice@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "role_chat_voice@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_voice.imageset/role_chat_voice@2x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 994 B | 
							
								
								
									
										
											BIN
										
									
								
								Visual_Novel_iOS/Assets.xcassets/Role/role_chat_voice.imageset/role_chat_voice@3x.png
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 2.0 KiB | 
|  | @ -49,6 +49,20 @@ import UIKit | |||
|         return v | ||||
|     }() | ||||
|      | ||||
|     @objc lazy var centerStackH : UIStackView = { | ||||
|         let v = UIStackView() | ||||
|         v.spacing = 5 | ||||
|         v.alignment = .center | ||||
|         addSubview(v) | ||||
|         v.snp.makeConstraints { make in | ||||
|             make.centerY.equalTo(backButton) | ||||
|             make.centerX.equalToSuperview() | ||||
|             make.leading.greaterThanOrEqualTo(leftStackH.snp.trailing) | ||||
|             make.trailing.lessThanOrEqualTo(rightStackH.snp.leading) | ||||
|         } | ||||
|         return v | ||||
|     }() | ||||
|      | ||||
|     @objc lazy var backButton: UIButton = { | ||||
|         let backButton = UIButton(type: .custom) | ||||
|         leftStackH.addArrangedSubview(backButton) | ||||
|  | @ -69,17 +83,31 @@ import UIKit | |||
|         titleLabel.preferredMaxLayoutWidth = 120 | ||||
|         titleLabel.textAlignment = .center | ||||
|         titleLabel.font = .t.ttm//.fredokaOne(size: 18) | ||||
|         addSubview(titleLabel) | ||||
| //        addSubview(titleLabel) | ||||
|         centerStackH.addArrangedSubview(titleLabel) | ||||
| 
 | ||||
|         titleLabel.snp.makeConstraints { make in | ||||
|             make.centerY.equalTo(backButton) | ||||
|             make.centerX.equalToSuperview() | ||||
|             make.leading.greaterThanOrEqualTo(leftStackH.snp.trailing) | ||||
|             make.trailing.lessThanOrEqualTo(rightStackH.snp.leading) | ||||
|         } | ||||
| //        titleLabel.snp.makeConstraints { make in | ||||
| //            make.centerY.equalTo(backButton) | ||||
| //            make.centerX.equalToSuperview() | ||||
| //            make.leading.greaterThanOrEqualTo(leftStackH.snp.trailing) | ||||
| //            make.trailing.lessThanOrEqualTo(rightStackH.snp.leading) | ||||
| //        } | ||||
|         return titleLabel | ||||
|     }() | ||||
|      | ||||
|     @objc lazy var iconImgView: UIImageView = { | ||||
|         let icon = UIImageView() | ||||
|         icon.isHidden = true | ||||
|         icon.cornerRadius = 10.0 | ||||
|         icon.snp.makeConstraints { make in | ||||
|             make.width.height.equalTo(20) | ||||
|         } | ||||
|         centerStackH.addArrangedSubview(icon) | ||||
| 
 | ||||
|         return icon | ||||
|     }() | ||||
|      | ||||
| 
 | ||||
|     /// btn 44x44 is ok | ||||
|     @objc lazy var rightStackH: UIStackView = { | ||||
|         let rightStackH = UIStackView() | ||||
|  | @ -131,6 +159,8 @@ import UIKit | |||
|          | ||||
|         //backgroundColor = .c.cbd | ||||
|         backgroundColor = .clear | ||||
|          | ||||
|         iconImgView.backgroundColor = .blue | ||||
|     } | ||||
| 
 | ||||
|     @available(*, unavailable) | ||||
|  |  | |||
|  | @ -0,0 +1,34 @@ | |||
| // | ||||
| //  InputSafeView.swift | ||||
| //  Visual_Novel_iOS | ||||
| // | ||||
| //  Created by mh on 2025/10/21. | ||||
| // | ||||
| 
 | ||||
| import UIKit | ||||
| 
 | ||||
| class InputSafeView: UIView { | ||||
|      | ||||
|     override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { | ||||
|         // 1. 自身不可见/无交互 → 剪枝 | ||||
|         if alpha <= 0.01 || isHidden || !isUserInteractionEnabled { return nil } | ||||
|          | ||||
|         // 2. 自身命中(含子视图)→ 正常返回 | ||||
|         let hit = super.hitTest(point, with: event) | ||||
|         if hit != nil { return hit } | ||||
|          | ||||
|         // 3. 自身没命中,继续测**所有子视图(含超框)** | ||||
|         for sub in subviews.reversed() { | ||||
|             let converted = sub.convert(point, from: self) | ||||
|             if sub.bounds.contains(converted) { | ||||
|                 return sub.hitTest(converted, with: event) | ||||
|             } else { | ||||
|                 if sub is SessionPhoneExpandView { | ||||
|                     return sub.hitTest(converted, with: event) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return nil | ||||
|     } | ||||
|      | ||||
| } | ||||
|  | @ -0,0 +1,29 @@ | |||
| // | ||||
| //  InputStackView.swift | ||||
| //  Visual_Novel_iOS | ||||
| // | ||||
| //  Created by mh on 2025/10/21. | ||||
| // | ||||
| 
 | ||||
| import UIKit | ||||
| 
 | ||||
| class InputStackView: UIStackView { | ||||
|      | ||||
|     override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { | ||||
|         // 1. 自身不可见/无交互 → 剪枝 | ||||
|         if alpha <= 0.01 || isHidden || !isUserInteractionEnabled { return nil } | ||||
|          | ||||
|         // 2. 自身命中(含子视图)→ 正常返回 | ||||
|         let hit = super.hitTest(point, with: event) | ||||
|         if hit != nil { return hit } | ||||
|          | ||||
|         // 3. 自身没命中,继续测**所有子视图(含超框)** | ||||
|         for sub in subviews.reversed() { | ||||
|             if sub is SessionInputOperateView { | ||||
|                 let converted = sub.convert(point, from: self) | ||||
|                 return sub.hitTest(converted, with: event) | ||||
|             } | ||||
|         } | ||||
|         return nil | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,124 @@ | |||
| // | ||||
| //  SessionCoverView.swift | ||||
| //  Visual_Novel_iOS | ||||
| // | ||||
| //  Created by mh on 2025/10/21. | ||||
| // | ||||
| 
 | ||||
| import UIKit | ||||
| 
 | ||||
| class SessionCoverView: UIView { | ||||
|      | ||||
|     var aiAnswerBlock: (()->Void)? = nil | ||||
|      | ||||
|     lazy var expandBtn: UIButton = { | ||||
|         let btn = UIButton(type: .custom) | ||||
|         btn.setImage(UIImage(named: "role_chat_expand_open"), for: .normal) | ||||
|         btn.setImage(UIImage(named: "role_chat_expand_close"), for: .selected) | ||||
|         btn.backgroundColor = .clear | ||||
|         btn.isUserInteractionEnabled = false | ||||
|         return btn | ||||
|     }() | ||||
|      | ||||
|     lazy var lineView: UIView = { | ||||
|         let view = UIView() | ||||
| //        view.backgroundColor = UIColor.init(white: 1.0, alpha: 0.2) | ||||
|         view.backgroundColor = .red | ||||
|         view.cornerRadius = 0.5 | ||||
|         view.alpha = 0 | ||||
|         return view | ||||
|     }() | ||||
|      | ||||
|     lazy var phoneBtn: UIButton = { | ||||
|         let btn = UIButton(type: .custom) | ||||
|         btn.setImage(UIImage(named: "role_chat_expand_phone"), for: .normal) | ||||
|         btn.addTarget(self, action: #selector(phoneBtnClicked), for: .touchDown) | ||||
|         btn.alpha = 0 | ||||
|         return btn | ||||
|     }() | ||||
|      | ||||
|     lazy var emailBtn: UIButton = { | ||||
|         let btn = UIButton(type: .custom) | ||||
|         btn.setImage(UIImage(named: "role_chat_expand_phone"), for: .normal) | ||||
|         btn.addTarget(self, action: #selector(emailBtnClicked), for: .touchDown) | ||||
|         btn.alpha = 0 | ||||
|         return btn | ||||
|     }() | ||||
|      | ||||
|     lazy var bgview: UIView = { | ||||
|         let view = UIView() | ||||
|         view.backgroundColor = .clear | ||||
|         view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(bgviewTap))) | ||||
|         return view | ||||
|     }() | ||||
|      | ||||
|     override init(frame: CGRect) { | ||||
|         super.init(frame: frame) | ||||
|          | ||||
|         setupUI() | ||||
|     } | ||||
|      | ||||
|     required init?(coder: NSCoder) { | ||||
|         fatalError("init(coder:) has not been implemented") | ||||
|     } | ||||
|      | ||||
|     @objc func bgviewTap() { | ||||
|         print("bgviewTap-bgviewTap") | ||||
|     } | ||||
|      | ||||
|     @objc func phoneBtnClicked() { | ||||
|         self.aiAnswerBlock?() | ||||
|     } | ||||
|      | ||||
|     @objc func emailBtnClicked() { | ||||
|         self.aiAnswerBlock?() | ||||
|     } | ||||
|      | ||||
|     func expandStatusChanged(_ expand: Bool) { | ||||
|         expandBtn.isSelected = expand | ||||
|         self.backgroundColor = expand ? .orange : .blue | ||||
|         UIView.animate(withDuration: 0.25) { | ||||
|             self.phoneBtn.alpha = expand ? 1 : 0 | ||||
|             self.emailBtn.alpha = expand ? 1 : 0 | ||||
|             self.lineView.alpha = expand ? 1 : 0 | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     private func setupUI() { | ||||
|          | ||||
|         addSubview(expandBtn) | ||||
|         addSubview(lineView) | ||||
|         addSubview(bgview) | ||||
|         addSubview(phoneBtn) | ||||
|         addSubview(emailBtn) | ||||
|          | ||||
|         expandBtn.snp.makeConstraints { make in | ||||
|             make.bottom.left.right.equalToSuperview() | ||||
|             make.height.equalTo(44) | ||||
|         } | ||||
|          | ||||
|         lineView.snp.makeConstraints { make in | ||||
|             make.centerX.equalToSuperview() | ||||
|             make.height.equalTo(1.0) | ||||
|             make.width.equalTo(10) | ||||
|             make.bottom.equalTo(expandBtn.snp.top) | ||||
|         } | ||||
|          | ||||
|         phoneBtn.snp.makeConstraints { make in | ||||
|             make.centerX.equalToSuperview() | ||||
|             make.bottom.equalTo(lineView.snp.top).offset(-17) | ||||
|             make.width.height.equalTo(30) | ||||
|         } | ||||
|          | ||||
|         emailBtn.snp.makeConstraints { make in | ||||
|             make.centerX.equalToSuperview() | ||||
|             make.bottom.equalTo(phoneBtn.snp.top).offset(-20) | ||||
|             make.width.height.equalTo(30) | ||||
|         } | ||||
|          | ||||
|         bgview.snp.makeConstraints { make in | ||||
|             make.left.right.top.equalToSuperview() | ||||
|             make.bottom.equalTo(lineView.snp.top) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,4 +1,5 @@ | |||
| // | ||||
| // | ||||
| //  SessionInputOperateView.swift | ||||
| 
 | ||||
| // | ||||
|  | @ -23,19 +24,20 @@ enum InputOperateState { | |||
| class SessionInputOperateView: UIView { | ||||
|     weak var delegate: SessionInputOperateViewDelegate? | ||||
|      | ||||
|     var safeView: UIView! | ||||
|     var giftButton: EPIconPrimaryButton! | ||||
|     var safeView: InputSafeView! | ||||
|      | ||||
|     var expandView: SessionPhoneExpandView! | ||||
|      | ||||
|     var block: UIView! | ||||
|     var effectViewOnBlock: UIVisualEffectView! | ||||
|     var modeButton: UIButton! | ||||
|     var rightStackH: UIStackView! | ||||
|     var helpButton: UIButton! | ||||
|     var moreButton: UIButton! | ||||
|      | ||||
|     var fakeTextfield: UITextField! | ||||
|     var voiceHoldView: UIView! | ||||
|      | ||||
|     var inputTextView: CLTextView! | ||||
|     let minTextViewHeight = 40.0//56.0 | ||||
|     var sendBtn: UIButton! | ||||
| 
 | ||||
|     //var tapInputFieldAction: (() -> Void)? | ||||
|     //    var voiceHoldAction: ((_ onVoice: Bool) -> Void)? | ||||
|      | ||||
|  | @ -45,6 +47,9 @@ class SessionInputOperateView: UIView { | |||
|      | ||||
|     override init(frame: CGRect) { | ||||
|         super.init(frame: frame) | ||||
|         self.clipsToBounds = false   // 允许子视图绘制到框外 | ||||
|         self.isUserInteractionEnabled = true | ||||
|          | ||||
|         setupViews() | ||||
|         setupData() | ||||
|         setupEvent() | ||||
|  | @ -54,28 +59,47 @@ class SessionInputOperateView: UIView { | |||
|         fatalError("init(coder:) has not been implemented") | ||||
|     } | ||||
|      | ||||
|     override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { | ||||
|         // 1. 自身不可见/无交互 → 剪枝 | ||||
|         if alpha <= 0.01 || isHidden || !isUserInteractionEnabled { return nil } | ||||
|          | ||||
|         // 2. 自身命中(含子视图)→ 正常返回 | ||||
|         let hit = super.hitTest(point, with: event) | ||||
|         if hit != nil { return hit } | ||||
|          | ||||
|         // 3. 自身没命中,继续测**所有子视图(含超框)** | ||||
|         for sub in subviews.reversed() { | ||||
|             let converted = sub.convert(point, from: self) | ||||
|             return sub.hitTest(converted, with: event) | ||||
|         } | ||||
|         return nil | ||||
|     } | ||||
|      | ||||
|     private func setupViews() { | ||||
|         safeView = { | ||||
|             let v = UIView() | ||||
|             v.backgroundColor = .clear | ||||
|             let v = InputSafeView() | ||||
| //            v.backgroundColor = .orange | ||||
|             addSubview(v) | ||||
|             v.snp.makeConstraints { make in | ||||
|                 make.leading.top.trailing.equalToSuperview() | ||||
|                 make.left.top.right.equalToSuperview() | ||||
|                 // make.bottom.equalToSuperview().offset(-UIWindow.safeAreaBottom*0.5-16) | ||||
|                 make.bottom.equalToSuperview() | ||||
|             } | ||||
|             return v | ||||
|         }() | ||||
|          | ||||
|         giftButton = { | ||||
|             let v = EPIconPrimaryButton(radius: .round, iconSize: .large, iconCode: .giftBorder) | ||||
|             v.addTarget(self, action: #selector(tapGiftButton), for: .touchUpInside) | ||||
|         expandView = { | ||||
|             let v = SessionPhoneExpandView() | ||||
|             safeView.clipsToBounds = false | ||||
|             safeView.isUserInteractionEnabled = true | ||||
| 
 | ||||
|             safeView.addSubview(v) | ||||
| //            v.backgroundColor = .blue | ||||
|             v.snp.makeConstraints { make in | ||||
|                 make.leading.equalToSuperview().offset(24) | ||||
|                 make.top.equalToSuperview().offset(16) | ||||
|                 make.bottom.equalToSuperview().offset(-16) | ||||
|                 make.size.equalTo(v.bgImageSize()) | ||||
|                 make.left.equalToSuperview().offset(24) | ||||
|                 make.top.equalToSuperview().inset(16).priority(999) | ||||
|                 make.bottom.equalToSuperview().inset(16) | ||||
|                 make.width.height.equalTo(44) | ||||
|             } | ||||
|             return v | ||||
|         }() | ||||
|  | @ -83,13 +107,13 @@ class SessionInputOperateView: UIView { | |||
|         block = { | ||||
|             let v = UIView() | ||||
|             v.backgroundColor = .c.cseln | ||||
|             v.layer.cornerRadius = 24 | ||||
|             v.layer.masksToBounds = true | ||||
|             v.cornerRadius = 24 | ||||
|             safeView.addSubview(v) | ||||
|             v.snp.makeConstraints { make in | ||||
|                 make.leading.equalTo(giftButton.snp.trailing).offset(12) | ||||
|                 make.trailing.equalToSuperview().offset(-24) | ||||
|                 make.height.equalTo(48) | ||||
|                 make.left.equalTo(expandView.snp.right).offset(12) | ||||
|                 make.right.equalToSuperview().offset(-24) | ||||
| //                make.height.equalTo(48) | ||||
|                 make.top.equalToSuperview().inset(16) | ||||
|                 make.centerY.equalToSuperview() | ||||
|             } | ||||
|             return v | ||||
|  | @ -108,75 +132,51 @@ class SessionInputOperateView: UIView { | |||
|         modeButton = { | ||||
|             let v = UIButton() | ||||
|             block.addSubview(v) | ||||
|             let size = CGSize(width: 20, height: 20) | ||||
|             let image = MWIconFont.image(fromIcon: .voiceMsg, size: size, color: .white) | ||||
|             let size = CGSize(width: 26, height: 26) | ||||
| //            let image = MWIconFont.image(fromIcon: .voiceMsg, size: size, color: .white) | ||||
|             let image = UIImage(named: "role_chat_voice") | ||||
|             v.setImage(image, for: .normal) | ||||
|             v.touchAreaInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) | ||||
|             v.addTarget(self, action: #selector(tapModeButton), for: .touchUpInside) | ||||
|             v.snp.makeConstraints { make in | ||||
|                 make.size.equalTo(size) | ||||
|                 make.leading.equalToSuperview().offset(16) | ||||
|                 make.centerY.equalToSuperview() | ||||
|                 make.left.equalToSuperview().inset(12) | ||||
|                 make.bottom.equalToSuperview().inset(11) | ||||
|             } | ||||
|             return v | ||||
|         }() | ||||
|          | ||||
|         rightStackH = { | ||||
|             let v = UIStackView() | ||||
|             v.spacing = 20 | ||||
|             v.alignment = .center | ||||
|         sendBtn = { | ||||
|             let btn = UIButton() | ||||
|             block.addSubview(btn) | ||||
|             btn.setImage(UIImage(named: "role_chat_send"), for: .normal) | ||||
|             btn.addTarget(self, action: #selector(tapSendButton), for: .touchUpInside) | ||||
|             btn.snp.makeConstraints { make in | ||||
|                 make.bottom.equalToSuperview().inset(2) | ||||
|                 make.right.equalToSuperview().inset(3) | ||||
|                 make.width.equalTo(60) | ||||
|                 make.height.equalTo(44) | ||||
|             } | ||||
|             return btn | ||||
|         }() | ||||
|          | ||||
|         inputTextView = { | ||||
|             let v = CLTextView() | ||||
|             block.addSubview(v) | ||||
|             v.snp.makeConstraints { make in | ||||
|                 make.trailing.equalToSuperview().offset(-16) | ||||
|                 make.centerY.equalToSuperview() | ||||
|             } | ||||
|             return v | ||||
|         }() | ||||
|          | ||||
|         helpButton = { | ||||
|             let v = UIButton() | ||||
|             rightStackH.addArrangedSubview(v) | ||||
|             let size = CGSize(width: 20, height: 20) | ||||
|             let image = MWIconFont.image(fromIcon: .prompt, size: size, color: .white) | ||||
|             v.setImage(image, for: .normal) | ||||
|             v.touchAreaInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) | ||||
|             v.addTarget(self, action: #selector(tapHelpButton), for: .touchUpInside) | ||||
|             v.snp.makeConstraints { make in | ||||
|                 make.size.equalTo(size) | ||||
|             } | ||||
|             return v | ||||
|         }() | ||||
|          | ||||
|         moreButton = { | ||||
|             let v = UIButton() | ||||
|             rightStackH.addArrangedSubview(v) | ||||
|             let size = CGSize(width: 20, height: 20) | ||||
|             let image = MWIconFont.image(fromIcon: .add, size: size, color: .white) | ||||
|             v.setImage(image, for: .normal) | ||||
|             v.touchAreaInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) | ||||
|             v.addTarget(self, action: #selector(tapMoreButton), for: .touchUpInside) | ||||
|             v.snp.makeConstraints { make in | ||||
|                 make.size.equalTo(size) | ||||
|             } | ||||
|             return v | ||||
|         }() | ||||
|          | ||||
|         fakeTextfield = { | ||||
|             let v = UITextField() | ||||
|             v.placeholder = "Type a message..." | ||||
|             v.placeholderTextColor = UIColor.init(white: 1, alpha: 0.4) | ||||
|             v.backgroundColor = .clear | ||||
|             v.limit.maxCharacterNumber = 500 | ||||
|             v.font = .t.tll | ||||
|             v.textColor = .c.ctpn | ||||
|             block.addSubview(v) | ||||
|             v.textColor = UIColor.white | ||||
|             v.font = UIFont.systemFont(ofSize: 14) | ||||
|             v.snp.makeConstraints { make in | ||||
|                 make.left.equalTo(modeButton.snp.right).offset(10) | ||||
|                 make.right.equalTo(sendBtn.snp.left).offset(-10) | ||||
|                 make.height.equalTo(minTextViewHeight) | ||||
|                 make.centerY.equalToSuperview() | ||||
|                 make.leading.equalTo(modeButton.snp.trailing).offset(16) | ||||
|                 make.trailing.equalTo(rightStackH.snp.leading).offset(-16) | ||||
|                 make.top.equalToSuperview().inset(4) | ||||
|             } | ||||
| //            v.text = "Chat" | ||||
|             v.attributedPlaceholder = "Chat".withAttributes([ | ||||
|                 .font(.t.tll), | ||||
|                 .textColor(UIColor.c.ctsn), | ||||
|             ]) | ||||
|             v.delegate = self | ||||
|             return v | ||||
|         }() | ||||
|          | ||||
|  | @ -185,19 +185,26 @@ class SessionInputOperateView: UIView { | |||
|             block.addSubview(v) | ||||
|             v.snp.makeConstraints { make in | ||||
|                 make.top.bottom.equalToSuperview() | ||||
|                 make.leading.trailing.equalTo(fakeTextfield) | ||||
|                 make.left.right.equalTo(inputTextView) | ||||
|             } | ||||
|              | ||||
|             let voiceImgView = UIImageView(image: UIImage(named: "role_chat_remind_voice")) | ||||
|              | ||||
|             let label = UILabel() | ||||
|             label.font = .t.tll | ||||
|             label.textColor = .white | ||||
|             label.textAlignment = .center | ||||
|             label.text = "Hold to Talk" | ||||
|             v.addSubview(label) | ||||
|             label.snp.makeConstraints { make in | ||||
|                 make.leading.trailing.equalToSuperview() | ||||
|                 make.centerY.equalToSuperview() | ||||
|              | ||||
|             let stackView = UIStackView(arrangedSubviews: [voiceImgView, label]) | ||||
|             stackView.spacing = 3 | ||||
|             stackView.alignment = .center | ||||
|             stackView.distribution = .fill | ||||
|             v.addSubview(stackView) | ||||
|             stackView.snp.makeConstraints { make in | ||||
|                 make.centerX.centerY.equalToSuperview() | ||||
|             } | ||||
|              | ||||
|             return v | ||||
|         }() | ||||
|     } | ||||
|  | @ -207,6 +214,11 @@ class SessionInputOperateView: UIView { | |||
|     } | ||||
|      | ||||
|     private func setupEvent() { | ||||
|          | ||||
|         inputTextView.textPublisher.sink {[weak self] str in | ||||
|             self?.fixTextViewHeight() | ||||
|         }.store(in: &cancellables) | ||||
|          | ||||
|         // 添加长按手势 | ||||
|         let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:))) | ||||
|         longPress.minimumPressDuration = 0.5 // 长按时间,默认 0.5 秒 | ||||
|  | @ -222,22 +234,37 @@ class SessionInputOperateView: UIView { | |||
|     // MARK: - Helper | ||||
|      | ||||
|     private func refreshByState(stateOf: InputOperateState) { | ||||
|         let size = CGSize(width: 20, height: 20) | ||||
| //        let size = CGSize(width: 26, height: 26) | ||||
|         switch stateOf { | ||||
|         case .text: | ||||
|             fakeTextfield.isHidden = false | ||||
| //            fakeTextfield.isHidden = false | ||||
|             inputTextView.isHidden = false | ||||
|             voiceHoldView.isHidden = true | ||||
|             let image = MWIconFont.image(fromIcon: .voiceMsg, size: size, color: .white) | ||||
| //            let image = MWIconFont.image(fromIcon:4 .voiceMsg, size: size, color: .white) | ||||
|             let image = UIImage(named: "role_chat_voice") | ||||
|             modeButton.setImage(image, for: .normal) | ||||
|         case .voice: | ||||
|             fakeTextfield.isHidden = true | ||||
| //            fakeTextfield.isHidden = true | ||||
|             inputTextView.isHidden = true | ||||
|             voiceHoldView.isHidden = false | ||||
|             let image = MWIconFont.image(fromIcon: .iconKeyboard, size: size, color: .white) | ||||
|             let image = UIImage(named: "role_chat_keyboard") | ||||
|             modeButton.setImage(image, for: .normal) | ||||
|             AudioRecordTool.audioAuth() | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     private func fixTextViewHeight(){ | ||||
|         if inputTextView.contentSize.height > minTextViewHeight { | ||||
|             inputTextView.snp.updateConstraints { make in | ||||
|                 make.height.equalTo(max(inputTextView.contentSize.height, 48)) // 73 | ||||
|             } | ||||
|         }else{ | ||||
|             inputTextView.snp.updateConstraints { make in | ||||
|                 make.height.equalTo(inputTextView.contentSize.height) //minTextViewHeight | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     // MARK: - Action | ||||
|      | ||||
|     @objc private func tapGiftButton() { | ||||
|  | @ -253,6 +280,10 @@ class SessionInputOperateView: UIView { | |||
|         } | ||||
|     } | ||||
|      | ||||
|     @objc private func tapSendButton() { | ||||
|          | ||||
|     } | ||||
|      | ||||
|     @objc private func tapMoreButton() { | ||||
|         delegate?.operateTapMoreAction() | ||||
|     } | ||||
|  |  | |||
|  | @ -0,0 +1,101 @@ | |||
| // | ||||
| //  SessionPhoneExpandView.swift | ||||
| //  Visual_Novel_iOS | ||||
| // | ||||
| //  Created by mh on 2025/10/20. | ||||
| // | ||||
| 
 | ||||
| import Foundation | ||||
| 
 | ||||
| class SessionPhoneExpandView: UIView { | ||||
|      | ||||
|     lazy var expandView: SessionCoverView = { | ||||
|         let view = SessionCoverView() | ||||
| //        view.backgroundColor = UIColor.init(white: 0, alpha: 0.4) | ||||
|         view.backgroundColor = .blue | ||||
|         view.cornerRadius = 22.0 | ||||
|         view.aiAnswerBlock = { [weak self] in | ||||
|             self?.tapClicked() | ||||
|         } | ||||
|         let tap = UITapGestureRecognizer(target: self, action: #selector(tapClicked)) | ||||
|         view.addGestureRecognizer(tap) | ||||
|         return view | ||||
|     }() | ||||
|      | ||||
|     var expand: Bool = false | ||||
|      | ||||
|     override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { | ||||
|         // 1. 自身不可见/无交互 → 剪枝 | ||||
|         if alpha <= 0.01 || isHidden || !isUserInteractionEnabled { return nil } | ||||
|          | ||||
|         // 2. 自身命中(含子视图)→ 正常返回 | ||||
|         let hit = super.hitTest(point, with: event) | ||||
|         if hit != nil { return hit } | ||||
|          | ||||
|         // 3. 自身没命中,继续测**所有子视图(含超框)** | ||||
|         for sub in subviews.reversed() { | ||||
|             let converted = sub.convert(point, from: self) | ||||
|             if sub.bounds.contains(converted) { | ||||
|                 return sub.hitTest(converted, with: event) | ||||
|             } | ||||
|         } | ||||
|         return nil | ||||
|     } | ||||
|      | ||||
|     override init(frame: CGRect) { | ||||
|         super.init(frame: frame) | ||||
|         self.clipsToBounds = false   // 允许子视图绘制到框外 | ||||
|         setupViews() | ||||
|         setupData() | ||||
|         setupEvent() | ||||
|     } | ||||
|      | ||||
|     required init?(coder: NSCoder) { | ||||
|         fatalError("init(coder:) has not been implemented") | ||||
|     } | ||||
|      | ||||
|     @objc func tapClicked() { | ||||
|         print("tapClicked 被调用 - expand: \(expand)") | ||||
|         self.expandView.expandStatusChanged(!self.expand) | ||||
|         UIView.animate(withDuration: 0.25) { | ||||
|             // 同时调整自身和expandView的高度 | ||||
|             self.expandView.snp.updateConstraints { make in | ||||
|                 make.height.equalTo(self.expand ? 44 : 152) | ||||
|             } | ||||
|              | ||||
|             self.layoutIfNeeded() | ||||
|         } completion: { finish in | ||||
|             if finish { | ||||
|                 self.expand = !self.expand | ||||
|                  | ||||
|                 print("动画完成 - 新的 expand 状态: \(self.expand)") | ||||
|             } | ||||
|         } | ||||
|          | ||||
|     } | ||||
|      | ||||
|     private func setupViews() { | ||||
|          | ||||
|         addSubview(expandView) | ||||
|          | ||||
|         expandView.snp.makeConstraints { make in | ||||
|             make.height.width.equalTo(44.0) | ||||
|             make.bottom.equalToSuperview() | ||||
|             make.centerX.equalToSuperview() | ||||
|         } | ||||
|          | ||||
|         // 设置自身的高度约束,允许动态调整 | ||||
|         self.snp.makeConstraints { make in | ||||
|             make.height.equalTo(44.0) | ||||
|         } | ||||
|          | ||||
|     } | ||||
|      | ||||
|     private func setupData() { | ||||
|          | ||||
|     } | ||||
|      | ||||
|     private func setupEvent() { | ||||
|          | ||||
|     } | ||||
| } | ||||
|  | @ -14,8 +14,9 @@ extension SessionController { | |||
|     func setupInputView() { | ||||
|          | ||||
|         bottomViewsStackV = { | ||||
|             let v = UIStackView() | ||||
|             let v = InputStackView() | ||||
|             v.axis = .vertical | ||||
|             v.clipsToBounds = false  // 允许子视图绘制到框外,支持超框点击 | ||||
|             view.addSubview(v) | ||||
|             v.snp.makeConstraints { make in | ||||
|                 make.leading.trailing.equalToSuperview() | ||||
|  | @ -97,7 +98,7 @@ extension SessionController { | |||
|         } | ||||
|          | ||||
|         inputBar.textView.textPublisher.sink {[weak self] string in | ||||
|             self?.inputEntrance.fakeTextfield.text = string | ||||
|             self?.inputEntrance.inputTextView.text = string | ||||
|         }.store(in: &cancellables) | ||||
|     } | ||||
|      | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ class SessionController: CLBaseViewController { | |||
|     // var headView: SessionAIHeadView! | ||||
|      | ||||
|     // MARK: BottomViews | ||||
|     var bottomViewsStackV : UIStackView! | ||||
|     var bottomViewsStackV : InputStackView! | ||||
|     var inputEntrance: SessionInputOperateView! | ||||
|     var inputBar: SessionInputView! | ||||
|     var moreView: IMMoreItemView! | ||||
|  | @ -23,6 +23,7 @@ class SessionController: CLBaseViewController { | |||
|     var voiceHoldView: IMVoiceHoldView! | ||||
|     var pureBgOperateView:SessionPureBgOperateView! | ||||
| 
 | ||||
|     var titleStr: String? = nil | ||||
| 
 | ||||
|     // 长按菜单响应的cell | ||||
|     var menuCell: SessionCell? | ||||
|  | @ -78,9 +79,10 @@ class SessionController: CLBaseViewController { | |||
|         } | ||||
|     } | ||||
|      | ||||
|     convenience init(conversationId: String) { | ||||
|     convenience init(conversationId: String, title: String? = "") { | ||||
|         self.init() | ||||
|         self.conversationId = conversationId | ||||
|         self.titleStr = title | ||||
|         conversation = V2NIMConversation() | ||||
|          | ||||
|         let stings = conversationId.components(separatedBy: "|") | ||||
|  | @ -149,11 +151,14 @@ class SessionController: CLBaseViewController { | |||
| 
 | ||||
| extension SessionController { | ||||
|     func setupUI() { | ||||
|         view.clipsToBounds = true | ||||
|         view.clipsToBounds = false  // 允许子视图绘制到框外,支持超框点击 | ||||
|         navigationView.backgroundColor = .clear | ||||
|         navigationView.backButton.setImage(nil, for: .normal) | ||||
|          | ||||
|         sessionNavigationView = { | ||||
|             let v = SessionNavigationView() | ||||
|             v.navigationView.title = self.titleStr ?? "" | ||||
|             v.navigationView.iconImgView.isHidden = false | ||||
|             view.addSubview(v) | ||||
|             v.snp.makeConstraints { make in | ||||
|                 make.top.leading.trailing.equalToSuperview() | ||||
|  | @ -368,9 +373,9 @@ extension SessionController { | |||
|     } | ||||
|      | ||||
|     func doKeyboardShowActions(){ | ||||
|         self.inputBar.isHidden = false | ||||
|         self.inputBar.isHidden = true | ||||
| 
 | ||||
|         self.inputEntrance.isHidden = true | ||||
|         self.inputEntrance.isHidden = false | ||||
|         // self.moreView.isHidden = true | ||||
|         //showMoreItems(show: false) | ||||
|     } | ||||
|  |  | |||
|  | @ -180,11 +180,13 @@ class SessionNavigationView: UIView { | |||
|     private func setupViews() { | ||||
|         addNavigationView() | ||||
|         navigationView.bgView.alpha = 0 | ||||
|         navigationView.backButton.setImage(UIImage(named: "role_back_white"), for: .normal) | ||||
| 
 | ||||
|         navigationView.paddingRightForRightStack = 2 | ||||
|         likeView = { | ||||
|             let v = HeartLikeCountView(viewSize: .xl) | ||||
|             v.purIconStyle() | ||||
|             v.isHidden = true | ||||
|             navigationView.rightStackH.addArrangedSubview(v) | ||||
|             return v | ||||
|         }() | ||||
|  | @ -192,7 +194,10 @@ class SessionNavigationView: UIView { | |||
|         naviMoreButton = { | ||||
|             let v = EPIconGhostButton(radius: .none, iconSize: .medium, iconCode: .chatroomMore) | ||||
|             navigationView.rightStackH.addArrangedSubview(v) | ||||
|              | ||||
|             v.setBackgroundImage(nil, for: .highlighted) | ||||
|             v.setImage(UIImage(named: "role_chat_setting"), for: .normal) | ||||
|             v.setImage(UIImage(named: "role_chat_setting"), for: .selected) | ||||
|             v.setImage(UIImage(named: "role_chat_setting"), for: .highlighted) | ||||
|             v.snp.makeConstraints { make in | ||||
|                 make.size.equalTo(CGSize(width: 52, height: 44)) | ||||
|             } | ||||
|  |  | |||
|  | @ -122,7 +122,8 @@ extension RolesRootPageView: UICollectionViewDelegate, UICollectionViewDataSourc | |||
|     } | ||||
|      | ||||
|     func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { | ||||
|          | ||||
|         let sessionId = "439217670979585@r@t" | ||||
|         AppRouter.goChatVC(conversationId: sessionId, title: "Character · 18", complete: nil) | ||||
|     } | ||||
|      | ||||
|     func collectionView(_ collectionView: UICollectionView, | ||||
|  |  | |||
|  | @ -35,11 +35,11 @@ extension AppRouter{ | |||
|         complete?() | ||||
|     } | ||||
|      | ||||
|     static func goChatVC(conversationId: String?, complete: (() -> Void)? = nil) { | ||||
|     static func goChatVC(conversationId: String?, title: String? = nil, complete: (() -> Void)? = nil) { | ||||
|         guard UserCore.shared.checkUserLoginIfNotPushUserToLogin() else{return} | ||||
|          | ||||
|         guard let sessionId  = conversationId else{return} | ||||
|         let vc = SessionController(conversationId: sessionId) | ||||
|         let vc = SessionController(conversationId: sessionId, title: title) | ||||
|         let nvc = UIWindow.getTopViewController(base: UIWindow.applicationKey?.rootViewController)?.navigationController | ||||
|         nvc?.pushViewController(vc, animated: true) | ||||
|         complete?() | ||||
|  |  | |||