私聊页面的自定义U

This commit is contained in:
fengshuo
2024-03-01 14:22:06 +08:00
parent 678297f771
commit 41ad01515d
49 changed files with 1088 additions and 71 deletions

View File

@@ -45,6 +45,10 @@
E884E8662B6900C500ADE6EE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E884E8642B6900C500ADE6EE /* Main.storyboard */; }; E884E8662B6900C500ADE6EE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E884E8642B6900C500ADE6EE /* Main.storyboard */; };
E884E8682B6900C600ADE6EE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E884E8672B6900C600ADE6EE /* Assets.xcassets */; }; E884E8682B6900C600ADE6EE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E884E8672B6900C600ADE6EE /* Assets.xcassets */; };
E884E86B2B6900C600ADE6EE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E884E8692B6900C600ADE6EE /* LaunchScreen.storyboard */; }; E884E86B2B6900C600ADE6EE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E884E8692B6900C600ADE6EE /* LaunchScreen.storyboard */; };
E89F19452B917DBA0098E797 /* ChatSendVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89F19442B917DBA0098E797 /* ChatSendVoiceView.swift */; };
E89F19472B9197090098E797 /* ChatTimeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89F19462B9197090098E797 /* ChatTimeCell.swift */; };
E89F19492B919EB80098E797 /* ChatVoiceCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89F19482B919EB80098E797 /* ChatVoiceCell.swift */; };
E89F194B2B919ECB0098E797 /* ChatImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89F194A2B919ECB0098E797 /* ChatImageCell.swift */; };
E8D15A9D2B899E1500369467 /* YMNetworkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D15A9C2B899E1500369467 /* YMNetworkHelper.swift */; }; E8D15A9D2B899E1500369467 /* YMNetworkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D15A9C2B899E1500369467 /* YMNetworkHelper.swift */; };
E8D15A9F2B89AED600369467 /* AuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D15A9E2B89AED500369467 /* AuthManager.swift */; }; E8D15A9F2B89AED600369467 /* AuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D15A9E2B89AED500369467 /* AuthManager.swift */; };
E8D15AA12B89AF4F00369467 /* UserTokenObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D15AA02B89AF4F00369467 /* UserTokenObject.swift */; }; E8D15AA12B89AF4F00369467 /* UserTokenObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D15AA02B89AF4F00369467 /* UserTokenObject.swift */; };
@@ -122,6 +126,10 @@
E884E8672B6900C600ADE6EE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; E884E8672B6900C600ADE6EE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
E884E86A2B6900C600ADE6EE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; E884E86A2B6900C600ADE6EE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
E884E86C2B6900C600ADE6EE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; E884E86C2B6900C600ADE6EE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E89F19442B917DBA0098E797 /* ChatSendVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatSendVoiceView.swift; sourceTree = "<group>"; };
E89F19462B9197090098E797 /* ChatTimeCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTimeCell.swift; sourceTree = "<group>"; };
E89F19482B919EB80098E797 /* ChatVoiceCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatVoiceCell.swift; sourceTree = "<group>"; };
E89F194A2B919ECB0098E797 /* ChatImageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatImageCell.swift; sourceTree = "<group>"; };
E8D15A9C2B899E1500369467 /* YMNetworkHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YMNetworkHelper.swift; sourceTree = "<group>"; }; E8D15A9C2B899E1500369467 /* YMNetworkHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YMNetworkHelper.swift; sourceTree = "<group>"; };
E8D15A9E2B89AED500369467 /* AuthManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthManager.swift; sourceTree = "<group>"; }; E8D15A9E2B89AED500369467 /* AuthManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthManager.swift; sourceTree = "<group>"; };
E8D15AA02B89AF4F00369467 /* UserTokenObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserTokenObject.swift; sourceTree = "<group>"; }; E8D15AA02B89AF4F00369467 /* UserTokenObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserTokenObject.swift; sourceTree = "<group>"; };
@@ -202,8 +210,11 @@
E8479E3E2B8DC624009AF878 /* View */ = { E8479E3E2B8DC624009AF878 /* View */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E89F19462B9197090098E797 /* ChatTimeCell.swift */,
E8E4AB262B902A750096D77C /* ChatList */, E8E4AB262B902A750096D77C /* ChatList */,
E8479E3C2B8DC61F009AF878 /* ChatBaseCell.swift */, E8479E3C2B8DC61F009AF878 /* ChatBaseCell.swift */,
E89F19482B919EB80098E797 /* ChatVoiceCell.swift */,
E89F194A2B919ECB0098E797 /* ChatImageCell.swift */,
E8479E472B8DD6E1009AF878 /* ChatTextCell.swift */, E8479E472B8DD6E1009AF878 /* ChatTextCell.swift */,
E8FF28B52B90ED6C005D2BE7 /* ChatNavView.swift */, E8FF28B52B90ED6C005D2BE7 /* ChatNavView.swift */,
); );
@@ -511,6 +522,7 @@
E8E4AB202B901CD50096D77C /* ChatMoreMenuView.swift */, E8E4AB202B901CD50096D77C /* ChatMoreMenuView.swift */,
E8E4AB222B901E0C0096D77C /* ChatMoreMnueConfig.swift */, E8E4AB222B901E0C0096D77C /* ChatMoreMnueConfig.swift */,
E8E4AB242B901E400096D77C /* ChatMoreMenuCell.swift */, E8E4AB242B901E400096D77C /* ChatMoreMenuCell.swift */,
E89F19442B917DBA0098E797 /* ChatSendVoiceView.swift */,
); );
path = Keyboard; path = Keyboard;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -670,6 +682,7 @@
E86A43E82B884C5E0084C04D /* String+.swift in Sources */, E86A43E82B884C5E0084C04D /* String+.swift in Sources */,
E8D15AA32B89B03D00369467 /* Deserialized.swift in Sources */, E8D15AA32B89B03D00369467 /* Deserialized.swift in Sources */,
E8E4AAB72B8F95CA0096D77C /* AuthAppleManager.swift in Sources */, E8E4AAB72B8F95CA0096D77C /* AuthAppleManager.swift in Sources */,
E89F19452B917DBA0098E797 /* ChatSendVoiceView.swift in Sources */,
E8D15AC22B8C703C00369467 /* UserFunctionView.swift in Sources */, E8D15AC22B8C703C00369467 /* UserFunctionView.swift in Sources */,
E86A43DA2B877A840084C04D /* AppConfigObject.swift in Sources */, E86A43DA2B877A840084C04D /* AppConfigObject.swift in Sources */,
E8D15AA62B89B0C600369467 /* List+.swift in Sources */, E8D15AA62B89B0C600369467 /* List+.swift in Sources */,
@@ -692,10 +705,12 @@
E86A43C82B8743EA0084C04D /* AuthFillDataVC.swift in Sources */, E86A43C82B8743EA0084C04D /* AuthFillDataVC.swift in Sources */,
E86A43CD2B874C8E0084C04D /* BaseView.swift in Sources */, E86A43CD2B874C8E0084C04D /* BaseView.swift in Sources */,
E8D15AE82B8CD47100369467 /* WebViewController.swift in Sources */, E8D15AE82B8CD47100369467 /* WebViewController.swift in Sources */,
E89F194B2B919ECB0098E797 /* ChatImageCell.swift in Sources */,
E8D15AA12B89AF4F00369467 /* UserTokenObject.swift in Sources */, E8D15AA12B89AF4F00369467 /* UserTokenObject.swift in Sources */,
233E515B2B8C849600582F9C /* PlanetStarClickItemView.swift in Sources */, 233E515B2B8C849600582F9C /* PlanetStarClickItemView.swift in Sources */,
E8FF28B42B90ADBE005D2BE7 /* AppKeys.swift in Sources */, E8FF28B42B90ADBE005D2BE7 /* AppKeys.swift in Sources */,
2311D6A92B8F405F001C70AB /* HomeVoiceChooseTypeView.swift in Sources */, 2311D6A92B8F405F001C70AB /* HomeVoiceChooseTypeView.swift in Sources */,
E89F19492B919EB80098E797 /* ChatVoiceCell.swift in Sources */,
E8D15AB62B8B002700369467 /* ChatVC.swift in Sources */, E8D15AB62B8B002700369467 /* ChatVC.swift in Sources */,
E8E4AB182B9019E50096D77C /* ChatKeyboardView.swift in Sources */, E8E4AB182B9019E50096D77C /* ChatKeyboardView.swift in Sources */,
E8E4AB232B901E0C0096D77C /* ChatMoreMnueConfig.swift in Sources */, E8E4AB232B901E0C0096D77C /* ChatMoreMnueConfig.swift in Sources */,
@@ -717,6 +732,7 @@
E8D15AB02B8AFFCE00369467 /* HomeVoiceVC.swift in Sources */, E8D15AB02B8AFFCE00369467 /* HomeVoiceVC.swift in Sources */,
E8E4AB282B902A9C0096D77C /* ChatListCell.swift in Sources */, E8E4AB282B902A9C0096D77C /* ChatListCell.swift in Sources */,
E86A43D52B8774B70084C04D /* AuthViewModel.swift in Sources */, E86A43D52B8774B70084C04D /* AuthViewModel.swift in Sources */,
E89F19472B9197090098E797 /* ChatTimeCell.swift in Sources */,
E8D15AB32B8B000400369467 /* PlanetStarVC.swift in Sources */, E8D15AB32B8B000400369467 /* PlanetStarVC.swift in Sources */,
E8FF28B62B90ED6C005D2BE7 /* ChatNavView.swift in Sources */, E8FF28B62B90ED6C005D2BE7 /* ChatNavView.swift in Sources */,
E8D15A9D2B899E1500369467 /* YMNetworkHelper.swift in Sources */, E8D15A9D2B899E1500369467 /* YMNetworkHelper.swift in Sources */,
@@ -881,6 +897,10 @@
ENABLE_USER_SCRIPT_SANDBOXING = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "yinmeng-ios/Info.plist"; INFOPLIST_FILE = "yinmeng-ios/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "音萌";
INFOPLIST_KEY_NSCameraUsageDescription = "“音萌”需要您的同意,才可以访问进行拍照,才可以在聊天中分享图片给他人查看,或上传图片以更新头像";
INFOPLIST_KEY_NSMicrophoneUsageDescription = "\"音萌\"需要您的同意,才可以进行语音聊天";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "“音萌”需要您的同意,才可以访问相册并选择图片,然后在聊天中分享图片给他人查看,或上传图片以更新头像";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UIMainStoryboardFile = Main;
@@ -916,6 +936,10 @@
ENABLE_USER_SCRIPT_SANDBOXING = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "yinmeng-ios/Info.plist"; INFOPLIST_FILE = "yinmeng-ios/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "音萌";
INFOPLIST_KEY_NSCameraUsageDescription = "“音萌”需要您的同意,才可以访问进行拍照,才可以在聊天中分享图片给他人查看,或上传图片以更新头像";
INFOPLIST_KEY_NSMicrophoneUsageDescription = "\"音萌\"需要您的同意,才可以进行语音聊天";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "“音萌”需要您的同意,才可以访问相册并选择图片,然后在聊天中分享图片给他人查看,或上传图片以更新头像";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UIMainStoryboardFile = Main;

View File

@@ -16,5 +16,21 @@
stopOnStyle = "0"> stopOnStyle = "0">
</BreakpointContent> </BreakpointContent>
</BreakpointProxy> </BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "0F9AD5F9-E68D-4D59-AA9C-49EDA3BAE3A3"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "yinmeng-ios/Modules/Chat/Keyboard/ChatKeyboardView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "453"
endingLineNumber = "453"
landmarkName = "changeKeyboardHeight(_:height:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints> </Breakpoints>
</Bucket> </Bucket>

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 B

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -19,8 +19,7 @@ class ChatVC: BaseViewController, HiddenNavigationBarProtocol {
public init(session: NIMSession) { public init(session: NIMSession) {
vm = ChatViewModel(session: session) vm = ChatViewModel(session: session)
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
// vm.delegate = self vm.delegate = self
// NIMSDK.shared().mediaManager.add(self)
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
@@ -33,9 +32,19 @@ class ChatVC: BaseViewController, HiddenNavigationBarProtocol {
.darkContent .darkContent
} }
override open func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if NIMSDK.shared().mediaManager.isPlaying() {
NIMSDK.shared().mediaManager.stopPlay()
}
}
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
view.backgroundColor = ThemeColor(hexStr: "#F8F8FB")
requestInfo()
loadChatList()
registerChatCell()
let height = ScreenHeight - (ToolBarLastH + NavHeight) let height = ScreenHeight - (ToolBarLastH + NavHeight)
chatTableView.frame = CGRect(x: 0, y: NavHeight, width: ScreenWidth, height: height) chatTableView.frame = CGRect(x: 0, y: NavHeight, width: ScreenWidth, height: height)
navView.frame = CGRect(x: 0, y: 0, width: ScreenWidth, height: NavHeight) navView.frame = CGRect(x: 0, y: 0, width: ScreenWidth, height: NavHeight)
@@ -47,17 +56,16 @@ class ChatVC: BaseViewController, HiddenNavigationBarProtocol {
///cell ///cell
private func registerChatCell() { private func registerChatCell() {
chatTableView.register(cellType: ChatTextCell.self) chatTableView.register(cellType: ChatTextCell.self)
chatTableView.register(cellType: ChatTimeCell.self)
} }
private lazy var chatTableView: UITableView = { private lazy var chatTableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .plain) let tableView = UITableView(frame: .zero, style: .plain)
tableView.delegate = self tableView.delegate = self
tableView.dataSource = self tableView.dataSource = self
tableView.tableFooterView = UIView() tableView.tableFooterView = UIView()
tableView.separatorStyle = .none tableView.separatorStyle = .none
tableView.backgroundColor = ThemeColor(hexStr: "#F8F8FB") tableView.backgroundColor = .clear
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
tableView.contentInsetAdjustmentBehavior = .never tableView.contentInsetAdjustmentBehavior = .never
} }
@@ -84,6 +92,12 @@ class ChatVC: BaseViewController, HiddenNavigationBarProtocol {
// MARK: - ChatKeyboardViewDelegate // MARK: - ChatKeyboardViewDelegate
extension ChatVC: ChatKeyboardViewDelegate { extension ChatVC: ChatKeyboardViewDelegate {
func keyboard(_ keyboard: ChatKeyboardView, voiceDidFinish path: String) {
vm.sendAudioMessage(filePath: path) { error in
}
}
func keyboard(_ keyboard: ChatKeyboardView, DidFinish content: String) { func keyboard(_ keyboard: ChatKeyboardView, DidFinish content: String) {
/// ///
vm.sendTextMessage(text: content) { error in vm.sendTextMessage(text: content) { error in
@@ -106,6 +120,138 @@ extension ChatVC: ChatKeyboardViewDelegate {
} }
} }
extension ChatVC {
func requestInfo() {
let params = ["uid": vm.session.sessionId]
RequestGet(path: "user/get", parma: params) { data in
if let info = Deserialized<UserObject>.toModel(with: data) {
self.navView.name = info.nick
}
} fail: { code, msg in
}
let par:[String : Any] = ["uid": AuthManager.userUid, "isLikeUid": vm.session.sessionId]
RequestGet(path: "fans/isLike", parma: par) { data in
if let isLike = data as? Bool {
self.navView.isLike = isLike
}
} fail: { code, msg in
}
}
func loadChatList() {
weak var weakSelf = self
vm.queryRoamMsgHasMoreTime_v2 { error, historyEnd, newEnd, models, index in
if let ms = models, ms.count > 0 {
weakSelf?.chatTableView.reloadData()
if weakSelf?.vm.isHistoryAnchorChat == true {
let indexPath = IndexPath(row: index, section: 0)
weakSelf?.chatTableView.scrollToRow(at: indexPath, at: .none, animated: false)
} else {
if let tempArrayJJom = weakSelf?.vm.messageObjects, tempArrayJJom.count > 0 {
DispatchQueue.main.async {
weakSelf?.chatTableView.scrollToRow(
at: IndexPath(row: tempArrayJJom.count - 1, section: 0),
at: .bottom,
animated: false
)
}
}
}
} else if let err = error {
HUDTool.show(with: err.localizedDescription)
}
}
}
private func insertRows() {
let oldRows = chatTableView.numberOfRows(inSection: 0)
if oldRows == 0 {
chatTableView.reloadData()
return
}
if oldRows == vm.messageObjects.count {
chatTableView.reloadData()
return
}
var indexs = [IndexPath]()
for (i, _) in vm.messageObjects.enumerated() {
if i >= oldRows {
indexs.append(IndexPath(row: i, section: 0))
}
}
if !indexs.isEmpty {
chatTableView.insertRows(at: indexs, with: .none)
chatTableView.scrollToRow(
at: IndexPath(row: vm.messageObjects.count - 1, section: 0),
at: .bottom,
animated: false
)
}
}
public func tableViewDeleteIndexs(_ indexs: [IndexPath]) {
chatTableView.beginUpdates()
chatTableView.deleteRows(at: indexs, with: .none)
chatTableView.endUpdates()
}
public func tableViewReloadIndexs(_ indexs: [IndexPath]) {
chatTableView.beginUpdates()
chatTableView.reloadRows(at: indexs, with: .none)
chatTableView.endUpdates()
}
}
extension ChatVC: ChatViewModelDelegate{
public func onRecvMessages(_ messages: [NIMMessage]) {
insertRows()
vm.markRead(messages: messages) { error in
}
}
public func willSend(_ message: NIMMessage) {
}
public func didAppend(_ message: NIMMessage) {
}
public func send(_ message: NIMMessage, progress: Float) {}
public func send(_ message: NIMMessage, didCompleteWithError error: Error?) {
}
private func indexPathsWithMessags(_ messages: [NIMMessage]) -> [IndexPath] {
var indexPaths = [IndexPath]()
for messageModel in messages {
for (i, model) in vm.messageObjects.enumerated() {
if model.msg?.messageId == messageModel.messageId {
indexPaths.append(IndexPath(row: i, section: 0))
}
}
}
return indexPaths
}
public func onDeleteMessage(_ message: NIMMessage, atIndexs: [IndexPath]) {
if atIndexs.isEmpty {
return
}
tableViewDeleteIndexs(atIndexs)
}
}
extension ChatVC: UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate { extension ChatVC: UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate {
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return vm.messageObjects.count return vm.messageObjects.count
@@ -117,6 +263,9 @@ extension ChatVC: UITableViewDelegate, UITableViewDataSource, UIScrollViewDelega
if model?.type == .text { if model?.type == .text {
let cell = tableView.dequeueReusableCell(for: indexPath, cellType: ChatTextCell.self) let cell = tableView.dequeueReusableCell(for: indexPath, cellType: ChatTextCell.self)
cell.model = model cell.model = model
} else if model?.type == .time {
let cell = tableView.dequeueReusableCell(for: indexPath, cellType: ChatTimeCell.self)
cell.model = model
} }
return UITableViewCell() return UITableViewCell()
} }

View File

@@ -77,7 +77,7 @@ class ChatGrowingTextView: UITextView {
self.returnKeyType = .send self.returnKeyType = .send
self.layer.cornerRadius = 4 self.layer.cornerRadius = 4
self.layer.borderWidth = 1 self.layer.borderWidth = 0
self.layer.borderColor = ThemeColor(hexStr: "#2C363E").cgColor self.layer.borderColor = ThemeColor(hexStr: "#2C363E").cgColor
self.layer.masksToBounds = true self.layer.masksToBounds = true
// //

View File

@@ -6,8 +6,10 @@
// //
import UIKit import UIKit
import NIMSDK
protocol ChatKeyboardViewDelegate: NSObjectProtocol { protocol ChatKeyboardViewDelegate: NSObjectProtocol {
///
func keyboard(_ keyboard: ChatKeyboardView, voiceDidFinish path: String)
/// ///
func keyboard(_ keyboard: ChatKeyboardView, DidFinish content: String) func keyboard(_ keyboard: ChatKeyboardView, DidFinish content: String)
/// / /// /
@@ -30,14 +32,14 @@ class ChatKeyboardView: UIView {
private let kLeft: CGFloat = 12.0 private let kLeft: CGFloat = 12.0
private let kSpace: CGFloat = 12.0 private let kSpace: CGFloat = 12.0
private let kViewWH: CGFloat = 25.0 private let kViewWH: CGFloat = 26.0
private let kLineHeight: CGFloat = 0.75 private let kLineHeight: CGFloat = 0.75
// MARK: - var lazy // MARK: - var lazy
weak var delegate: ChatKeyboardViewDelegate? weak var delegate: ChatKeyboardViewDelegate?
fileprivate var toolBarHeight: CGFloat = 52.0 fileprivate var toolBarHeight: CGFloat = (52.0 + SafeAraeBottomHeight)
fileprivate var lastTextHeight: CGFloat = 34.0 fileprivate var lastTextHeight: CGFloat = 34.0
fileprivate var keyboardHeight: CGFloat = 0.0 fileprivate var keyboardHeight: CGFloat = 0.0
@@ -48,14 +50,29 @@ class ChatKeyboardView: UIView {
/// ///
fileprivate var isShowKeyboard = false fileprivate var isShowKeyboard = false
/// private lazy var sendVoiceBtn: UIButton = {
let button = UIButton(type: .custom)
let w: CGFloat = ScreenWidth - self.kViewWH * 2 - self.kSpace * 3 - self.kSpace
button.frame = CGRect(x: self.kSpace + self.kLeft + self.kViewWH, y: self.kSpace, width: w, height: 36)
button.setTitle("按住说话", for: .normal)
button.setTitle("松开结束", for: .normal)
button.backgroundColor = ThemeColor(hexStr: "F1F1FA")
button.setTitleColor(ThemeColor(hexStr: "#282828"), for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .medium)
button.layer.masksToBounds = true
button.layer.cornerRadius = 4
button.isHidden = true
return button
}()
///
fileprivate lazy var changeButton : UIButton = { fileprivate lazy var changeButton : UIButton = {
let button = UIButton(type: .custom) let button = UIButton(type: .custom)
let x: CGFloat = self.kLeft let x: CGFloat = self.kLeft
button.frame = CGRect(x: x, y: self.kSpace, width: self.kViewWH, height: self.kViewWH) button.frame = CGRect(x: x, y: self.kSpace + 5, width: self.kViewWH, height: self.kViewWH)
button.setImage(UIImage(named: "chat_more"), for: .normal) button.setImage(UIImage(named: "chat_input_text"), for: .normal)
button.setImage(UIImage(named: "chat_more"), for: .highlighted) button.setImage(UIImage(named: "chat_voice"), for: .highlighted)
button.addTarget(self, action: #selector(moreDidAction(_:)), for: .touchUpInside) button.addTarget(self, action: #selector(changeDidAction(_:)), for: .touchUpInside)
return button return button
}() }()
@@ -63,7 +80,7 @@ class ChatKeyboardView: UIView {
fileprivate lazy var moreButton : UIButton = { fileprivate lazy var moreButton : UIButton = {
let button = UIButton(type: .custom) let button = UIButton(type: .custom)
let x: CGFloat = ScreenWidth - self.kViewWH - self.kSpace let x: CGFloat = ScreenWidth - self.kViewWH - self.kSpace
button.frame = CGRect(x: x, y: self.kSpace, width: self.kViewWH, height: self.kViewWH) button.frame = CGRect(x: x, y: self.kSpace + 5, width: self.kViewWH, height: self.kViewWH)
button.setImage(UIImage(named: "chat_more"), for: .normal) button.setImage(UIImage(named: "chat_more"), for: .normal)
button.setImage(UIImage(named: "chat_more"), for: .highlighted) button.setImage(UIImage(named: "chat_more"), for: .highlighted)
button.addTarget(self, action: #selector(moreDidAction(_:)), for: .touchUpInside) button.addTarget(self, action: #selector(moreDidAction(_:)), for: .touchUpInside)
@@ -109,11 +126,17 @@ class ChatKeyboardView: UIView {
return view return view
}() }()
private lazy var voiceView: ChatSendVoiceView = {
let view = ChatSendVoiceView()
view.backgroundColor = .clear
return view
}()
// MARK: - life cycle // MARK: - life cycle
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
NIMSDK.shared().mediaManager.add(self)
setupKeyboardView() setupKeyboardView()
registerNotification() registerNotification()
} }
@@ -128,15 +151,23 @@ class ChatKeyboardView: UIView {
func setupKeyboardView() { func setupKeyboardView() {
self.backgroundColor = .clear self.backgroundColor = .clear
self.isUserInteractionEnabled = true self.isUserInteractionEnabled = true
addSendVoiceAction()
addSubview(toolBarView) addSubview(toolBarView)
toolBarView.addSubview(moreButton) toolBarView.addSubview(moreButton)
toolBarView.addSubview(changeButton) toolBarView.addSubview(changeButton)
toolBarView.addSubview(chatTextView) toolBarView.addSubview(chatTextView)
toolBarView.addSubview(sendVoiceBtn)
addSubview(contentView) addSubview(contentView)
contentView.addSubview(moreMenuView) contentView.addSubview(moreMenuView)
} }
private func addSendVoiceAction() {
sendVoiceBtn.addTarget(self, action: #selector(audioTouchDownAction), for: .touchDown)
sendVoiceBtn.addTarget(self, action: #selector(audioTouchUpOutsideAction), for: .touchUpOutside)
sendVoiceBtn.addTarget(self, action: #selector(audioTouchUpInsideAction), for: .touchUpInside)
sendVoiceBtn.addTarget(self, action: #selector(audioTouchDragEnterAction), for: .touchDragEnter)
sendVoiceBtn.addTarget(self, action: #selector(audioTouchDragExitAction), for: .touchDragExit)
}
// MARK: - // MARK: -
private func registerNotification() { private func registerNotification() {
@@ -171,18 +202,30 @@ class ChatKeyboardView: UIView {
} }
deinit { deinit {
NIMSDK.shared().mediaManager.remove(self)
self.removeObserver(self, forKeyPath: "frame") self.removeObserver(self, forKeyPath: "frame")
} }
} }
extension ChatKeyboardView: NIMMediaManagerDelegate {
func recordAudioInterruptionBegin() {
voiceView.cancelAudioRecord()
}
func recordAudioProgress(_ currentTime: TimeInterval) {
voiceView.updateAudioRecordProgress(recordTime: currentTime)
}
func recordAudio(_ filePath: String?, didCompletedWithError error: Error?) {
if let path = filePath {
delegate?.keyboard(self, voiceDidFinish: path)
}
}
}
// MARK: - ChatMoreMenuViewDelegate // MARK: - ChatMoreMenuViewDelegate
extension ChatKeyboardView: ChatMoreMenuViewDelegate { extension ChatKeyboardView: ChatMoreMenuViewDelegate {
func menu(_ view: ChatMoreMenuView, DidSelected type: ChatMoreMenuType) { func menu(_ view: ChatMoreMenuView, DidSelected type: ChatMoreMenuType) {
delegate?.keyboard(self, DidMoreMenu: type) delegate?.keyboard(self, DidMoreMenu: type)
} }
} }
@@ -209,7 +252,6 @@ extension ChatKeyboardView: UITextViewDelegate {
/// ///
private func sendChatMessage() { private func sendChatMessage() {
delegate?.keyboard(self, DidFinish: self.chatTextView.text ?? "") delegate?.keyboard(self, DidFinish: self.chatTextView.text ?? "")
changeKeyboardHeight(height: lastTextHeight) changeKeyboardHeight(height: lastTextHeight)
chatTextView.text = "" chatTextView.text = ""
chatTextView.attributedText = NSAttributedString(string: "") chatTextView.attributedText = NSAttributedString(string: "")
@@ -240,12 +282,12 @@ extension ChatKeyboardView {
let option = userInfo["UIKeyboardAnimationCurveUserInfoKey"] as! Int let option = userInfo["UIKeyboardAnimationCurveUserInfoKey"] as! Int
var changedY = y - self.toolBarHeight - NavHeight - contentHeight var changedY = y - self.toolBarHeight - contentHeight
if (isShowMore) { // if (isShowMore) { //
isShowMore = false isShowMore = false
self.moreMenuView.isHidden = true self.moreMenuView.isHidden = true
self.moreMenuView.hidePageController = true self.moreMenuView.hidePageController = true
changedY = y - self.toolBarHeight - NavHeight changedY = y - self.toolBarHeight
} }
UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: UIView.AnimationOptions.RawValue(option)), animations: { UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: UIView.AnimationOptions.RawValue(option)), animations: {
@@ -270,7 +312,7 @@ extension ChatKeyboardView {
isShowKeyboard = false isShowKeyboard = false
let option = userInfo["UIKeyboardAnimationCurveUserInfoKey"] as! Int let option = userInfo["UIKeyboardAnimationCurveUserInfoKey"] as! Int
let changedY = ScreenHeight - NavHeight - self.toolBarHeight - SafeAraeBottomHeight - self.contentHeight let changedY = ScreenHeight - self.toolBarHeight - self.contentHeight
UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: UIView.AnimationOptions.RawValue(option)), animations: { UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: UIView.AnimationOptions.RawValue(option)), animations: {
self.frame = CGRect(x: 0, y: changedY, width: ScreenWidth, height: self.toolBarHeight + self.contentHeight) self.frame = CGRect(x: 0, y: changedY, width: ScreenWidth, height: self.toolBarHeight + self.contentHeight)
}, completion: nil) }, completion: nil)
@@ -293,10 +335,69 @@ extension ChatKeyboardView {
// MARK: - Action // MARK: - Action
extension ChatKeyboardView { extension ChatKeyboardView {
@objc func audioTouchDownAction() {
sendVoiceBtn.isSelected = true
//
if voiceView.superview == nil {
UIApplication.shared.keyWindow?.addSubview(voiceView)
voiceView.translatesAutoresizingMaskIntoConstraints = false
voiceView.snp.makeConstraints { make in
make.center.equalToSuperview()
}
voiceView.configAudioRecord(imageName: "chat_voice_record_first", title: "手指上滑,取消发送", isAnimation: true)
voiceView.beginAudioRecord()
}
}
@objc func audioTouchUpOutsideAction() {
sendVoiceBtn.isSelected = false
voiceView.cancelAudioRecord()
voiceView.removeFromSuperview()
}
@objc func audioTouchUpInsideAction() {
sendVoiceBtn.isSelected = false
voiceView.finishAudioRecord()
voiceView.removeFromSuperview()
}
@objc func audioTouchDragEnterAction() {
voiceView.configAudioRecord(imageName: "chat_voice_record_first", title: "手指上滑,取消发送", isAnimation: true)
}
@objc func audioTouchDragExitAction() {
voiceView.configAudioRecord(imageName: "chat_voice_record_cancel", title: "松开手指,取消发送", isAnimation: false)
}
@objc func changeDidAction(_ button: UIButton) {
button.isSelected = !button.isSelected
isShowMore = false
contentView.isHidden = true
moreMenuView.isHidden = true
contentHeight = 0.0
restToolbarContentHeight(true)
if button.isSelected {
chatTextView.resignFirstResponder()
sendVoiceBtn.isHidden = false
chatTextView.isHidden = true
} else {
chatTextView.becomeFirstResponder()
sendVoiceBtn.isHidden = true
chatTextView.isHidden = false
}
delegate?.keyboard(self, DidBecome: false)
}
/// ///
@objc func moreDidAction(_ button: UIButton) { @objc func moreDidAction(_ button: UIButton) {
// //
if isShowMore { if isShowMore {
isShowMore = false
contentView.isHidden = true
moreMenuView.isHidden = true
contentHeight = 0.0
restToolbarContentHeight(true)
delegate?.keyboard(self, DidBecome: false)
return return
} }
@@ -314,13 +415,13 @@ extension ChatKeyboardView {
/// ///
func restToolbarContentHeight(_ isRest: Bool = false) { func restToolbarContentHeight(_ isRest: Bool = false) {
var changedY = ScreenHeight - self.toolBarHeight - NavHeight - contentHeight var changedY = ScreenHeight - self.toolBarHeight - contentHeight
if (isRest) { if (isRest) {
if (isShowMore) { if (isShowMore) {
isShowMore = false isShowMore = false
} }
changedY = ScreenHeight - self.toolBarHeight - NavHeight - contentHeight - SafeAraeBottomHeight changedY = ScreenHeight - self.toolBarHeight - contentHeight
} }
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut, animations: { UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut, animations: {
@@ -355,10 +456,10 @@ extension ChatKeyboardView {
isShowMore = false isShowMore = false
} }
let changedY = ScreenHeight - keyboardHeight - toolBarHeight - NavHeight let changedY = ScreenHeight - keyboardHeight - toolBarHeight
self.frame = CGRect(x: 0, y: changedY, width: ScreenWidth, height: toolBarView.height + contentView.height) self.frame = CGRect(x: 0, y: changedY, width: ScreenWidth, height: toolBarView.height + contentView.height)
}else { }else {
let changedY = ScreenHeight - NavHeight - (toolBarView.height + contentView.height) let changedY = ScreenHeight - (toolBarView.height + contentView.height)
self.frame = CGRect(x: 0, y: changedY, width: ScreenWidth, height: toolBarView.height + contentView.height) self.frame = CGRect(x: 0, y: changedY, width: ScreenWidth, height: toolBarView.height + contentView.height)
} }

View File

@@ -67,8 +67,7 @@ class ChatMoreMenuView: UIView {
lazy var dataSource: [ChatMoreMnueConfig] = { lazy var dataSource: [ChatMoreMnueConfig] = {
let configs = [ let configs = [
ChatMoreMnueConfig(title: "图片", image: "ic_more_album", type: .album), ChatMoreMnueConfig(title: "图片", image: "chat_more_album", type: .album),
ChatMoreMnueConfig(title: "拍照", image: "ic_more_camera", type: .camera)
] ]
return configs return configs
}() }()
@@ -138,7 +137,7 @@ class ChatMoreMenuView: UIView {
UIView.animate(withDuration: 0.15, delay: 0, options: .curveEaseOut, animations: { UIView.animate(withDuration: 0.15, delay: 0, options: .curveEaseOut, animations: {
self.pageControl.alpha = 1.0 self.pageControl.alpha = 1.0
self.pageControl.isHidden = false self.pageControl.isHidden = self.dataSource.count > 8 ? false : true
}, completion: nil) }, completion: nil)
DispatchQueue.main.async { DispatchQueue.main.async {

View File

@@ -0,0 +1,124 @@
//
// ChatSendVoiceView.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/3/1.
//
import UIKit
import NIMSDK
class ChatSendVoiceView: UIView {
private var backView: UIView = {
let view = UIView()
view.backgroundColor = .black
view.layer.masksToBounds = true
view.layer.cornerRadius = 10
return view
}()
private var logoImageView: UIImageView = {
let imageView = UIImageView()
imageView.isUserInteractionEnabled = true
let firstImage = UIImage(named: "chat_voice_record_first")!
let secondImage = UIImage(named: "chat_voice_record_second")!
let thirdImage = UIImage(named: "chat_voice_record_third")!
imageView.animationImages = [firstImage, secondImage, thirdImage]
imageView.animationDuration = 1
imageView.animationRepeatCount = .max
return imageView
}()
private var titleLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 15)
label.textColor = .white
label.textAlignment = .center
return label
}()
private var timeLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 17, weight: .medium)
label.textColor = .white
label.textAlignment = .center
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
loadSubViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configAudioRecord(imageName: String, title: String, isAnimation: Bool) {
logoImageView.image = UIImage(named: imageName)
if isAnimation {
logoImageView.startAnimating()
} else {
logoImageView.stopAnimating()
}
titleLabel.text = title
}
func beginAudioRecord() {
timeLabel.text = "00:00"
NIMSDK.shared().mediaManager.record(forDuration: 60)
}
func cancelAudioRecord() {
timeLabel.text = "00:00"
NIMSDK.shared().mediaManager.cancelRecord()
}
func finishAudioRecord() {
timeLabel.text = "00:00"
NIMSDK.shared().mediaManager.stopRecord()
}
func updateAudioRecordProgress(recordTime: TimeInterval) {
let minutes = Int(recordTime) / 60
let seconds = Int(recordTime) % 60
timeLabel.text = String(format: "%02d:%02d", minutes, seconds)
}
// Private Method
private func loadSubViews() {
addSubview(backView)
backView.addSubview(logoImageView)
backView.addSubview(titleLabel)
backView.addSubview(timeLabel)
translatesAutoresizingMaskIntoConstraints = false
self.snp.makeConstraints { make in
make.size.equalTo(CGSize(width: 250, height: 250))
}
timeLabel.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalToSuperview().offset(25)
}
backView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
logoImageView.snp.makeConstraints { make in
make.size.equalTo(CGSize(width: 90, height: 72))
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(50)
}
titleLabel.snp.makeConstraints { make in
make.left.right.equalTo(backView).inset(0)
make.top.equalTo(logoImageView.snp.bottom).offset(25)
}
}
}

View File

@@ -12,6 +12,7 @@ public enum SessionType: Int {
case text = 1 case text = 1
case image case image
case time case time
case voice
} }
@@ -66,7 +67,7 @@ class ChatTextObject: ChatBaseObject {
let style = NSMutableParagraphStyle() let style = NSMutableParagraphStyle()
style.lineSpacing = 6 style.lineSpacing = 6
let attributeStr = NSMutableAttributedString(string: msg?.text ?? "", attributes: [NSAttributedString.Key.font: ChatUIConfig.ui.messageFont, NSAttributedString.Key.paragraphStyle: style]) let attributeStr = NSMutableAttributedString(string: msg?.text ?? "", attributes: [NSAttributedString.Key.font: ChatUIConfig.ui.messageFont, NSAttributedString.Key.foregroundColor:ThemeColor(hexStr: "#2B2D33"), NSAttributedString.Key.paragraphStyle: style])
attribute = attributeStr attribute = attributeStr
let textSize = ChatAttributeTool.boundingRect(attribute: attributeStr, font: ChatUIConfig.ui.messageFont, maxSize: CGSize(width: ChatUIConfig.layout.contentMaxWidth, height: CGFloat.greatestFiniteMagnitude)) let textSize = ChatAttributeTool.boundingRect(attribute: attributeStr, font: ChatUIConfig.ui.messageFont, maxSize: CGSize(width: ChatUIConfig.layout.contentMaxWidth, height: CGFloat.greatestFiniteMagnitude))
@@ -83,23 +84,86 @@ class ChatTextObject: ChatBaseObject {
class ChatTimeObject: ChatBaseObject { class ChatTimeObject: ChatBaseObject {
var text:String = "" var text:String = ""
public var attribute: NSMutableAttributedString?
required init(msg: NIMMessage?) { required init(msg: NIMMessage?) {
super.init(msg: msg) super.init(msg: msg)
type = .time type = .time
let style = NSMutableParagraphStyle() text = timestrToTimeSecond("\(msg?.timestamp ?? 0)")
style.lineSpacing = 6 contentSize = CGSize(width: ScreenWidth, height: 30)
let attributeStr = NSMutableAttributedString(string: msg?.text ?? "", attributes: [NSAttributedString.Key.font: ChatUIConfig.ui.messageFont, NSAttributedString.Key.paragraphStyle: style]) height = Float(50)
attribute = attributeStr
let textSize = ChatAttributeTool.boundingRect(attribute: attributeStr, font: ChatUIConfig.ui.messageFont, maxSize: CGSize(width: ChatUIConfig.layout.contentMaxWidth, height: CGFloat.greatestFiniteMagnitude))
var h = ChatUIConfig.layout.bubbleMinHeight
h = textSize.height + 1 + ChatUIConfig.layout.textInsets.top + ChatUIConfig.layout.textInsets.bottom
if h < 36 {
h = 36
}
contentSize = CGSize(width: textSize.width + ChatUIConfig.layout.textInsets.left + ChatUIConfig.layout.textInsets.right + 1, height: h)
height = Float(contentSize.height + ChatUIConfig.layout.cellContentInsets.bottom + ChatUIConfig.layout.cellContentInsets.top)
} }
} }
class ChatImageObject: ChatBaseObject {
var url:String = ""
required init(msg: NIMMessage?) {
super.init(msg: msg)
type = .image
if let content = msg?.messageObject as? NIMImageObject {
url = content.url ?? content.thumbUrl ?? ""
}
contentSize = CGSize(width: 200 , height: 200)
height = Float(200 + ChatUIConfig.layout.cellContentInsets.bottom + ChatUIConfig.layout.cellContentInsets.top)
}
}
class ChatVoiceObject: ChatBaseObject {
var filPath:String = ""
var duartion:Int = 0
required init(msg: NIMMessage?) {
super.init(msg: msg)
type = .voice
let voiceHeight = 30.0
if let content = msg?.messageObject as? NIMAudioObject {
let scale = 2*atan((Double(content.duration)/1000.0-1)/10.0)/M_PI;
let low = (ChatUIConfig.layout.contentMaxWidth - 180)
let max = (ChatUIConfig.layout.contentMaxWidth - 100)
let width = (max - low) * scale + low
if let path = content.path {
filPath = path
}
duartion = content.duration
contentSize = CGSize(width: width, height: voiceHeight)
height = Float(voiceHeight + ChatUIConfig.layout.cellContentInsets.bottom + ChatUIConfig.layout.cellContentInsets.top)
} else {
let low = (ChatUIConfig.layout.contentMaxWidth - 180)
contentSize = CGSize(width: low , height: voiceHeight)
height = Float(voiceHeight + ChatUIConfig.layout.cellContentInsets.bottom + ChatUIConfig.layout.cellContentInsets.top)
}
}
}
func timestrToTimeSecond(_ timeStr: String) -> String {
let interval = TimeInterval(timeStr) ?? 0
let date = Date(timeIntervalSince1970: interval)
return stringFromDate(date)
}
func stringFromDate(_ date: Date) -> String {
let currentFormatter = DateFormatter()
if isDateInToday(date) {
currentFormatter.dateFormat = "HH:mm"
} else {
if date.timeIntervalSince1970 > 0 {
currentFormatter.dateFormat = "MM月dd日 HH:mm"
} else {
currentFormatter.dateFormat = "yyyy年MM月dd日 HH:mm"
}
}
let dateString = currentFormatter.string(from: date)
return dateString
}
func isDateInToday(_ date: Date) -> Bool {
let secondsPerDay: TimeInterval = 24 * 60 * 60
let today = Date()
let tomorrow = today.addingTimeInterval(secondsPerDay)
let yesterday = today.addingTimeInterval(-secondsPerDay)
let todayString = String(describing: today).prefix(10)
let yesterdayString = String(describing: yesterday).prefix(10)
let tomorrowString = String(describing: tomorrow).prefix(10)
let dateString = String(describing: date).prefix(10)
return dateString == todayString
}

View File

@@ -46,9 +46,9 @@ public class ChatUIConfig {
/// ///
public var avatarSize: CGFloat = 34.0 public var avatarSize: CGFloat = 34.0
/// cell /// cell
public var cellContentInsets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) public var cellContentInsets = UIEdgeInsets(top: 10, left: 16, bottom: 8, right: 16)
/// ///
public var textInsets = UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15) public var textInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
/// ///
public var contentMaxWidth: CGFloat = (UIScreen.main.bounds.size.width - 170) public var contentMaxWidth: CGFloat = (UIScreen.main.bounds.size.width - 170)
/// ///

View File

@@ -406,8 +406,6 @@ public class ChatViewModel: NSObject,
let timeMsg = NIMMessage() let timeMsg = NIMMessage()
timeMsg.timestamp = message.timestamp timeMsg.timestamp = message.timestamp
let model = ChatTimeObject(msg: timeMsg) let model = ChatTimeObject(msg: timeMsg)
model.type = .time
model.text = "2024-2-28"
return model return model
} }
@@ -418,11 +416,12 @@ public class ChatViewModel: NSObject,
switch message.messageType { switch message.messageType {
case .text: case .text:
model = ChatTextObject(msg: message) model = ChatTextObject(msg: message)
case .audio:
model = ChatVoiceObject(msg: message)
case .image:
model = ChatImageObject(msg: message)
default: default:
return nil return nil
// text
// message.text = ""
// model = MAIMessageContentModel(message: message)
} }
if let uid = message.from { if let uid = message.from {

View File

@@ -7,6 +7,7 @@
import UIKit import UIKit
import Reusable import Reusable
import NIMSDK
protocol ChatBaseCellProtocol: NSObjectProtocol { protocol ChatBaseCellProtocol: NSObjectProtocol {
func cell(_ cell: ChatBaseCell, didTapAvatarAt model: ChatBaseObject) func cell(_ cell: ChatBaseCell, didTapAvatarAt model: ChatBaseObject)
} }
@@ -15,7 +16,16 @@ class ChatBaseCell: UITableViewCell, Reusable{
weak var delegate: ChatBaseCellProtocol? weak var delegate: ChatBaseCellProtocol?
var model:ChatSessionProtocol? { var model:ChatSessionProtocol? {
didSet { didSet {
guard let _ = model else {return} guard let model = model else {return}
if let sessionId = model.msg?.session?.sessionId {
let user = NIMSDK.shared().userManager.userInfo(sessionId)
if let url = user?.userInfo?.avatarUrl {
self.avatarImgView.kf.setImage(with: URL(string: url),
placeholder: UIImage(named: "public_avatar"))
} else {
}
}
layoutMessageCell() layoutMessageCell()
} }
} }
@@ -45,7 +55,7 @@ class ChatBaseCell: UITableViewCell, Reusable{
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none selectionStyle = .none
backgroundColor = .clear backgroundColor = ThemeColor(hexStr: "#F8F8FB")
contentView.addSubview(avatarImgView) contentView.addSubview(avatarImgView)
contentView.addSubview(bubbleView) contentView.addSubview(bubbleView)
contentView.addSubview(activityIndicatorView) contentView.addSubview(activityIndicatorView)
@@ -61,11 +71,15 @@ class ChatBaseCell: UITableViewCell, Reusable{
imageView.isUserInteractionEnabled = true imageView.isUserInteractionEnabled = true
imageView.layer.masksToBounds = true imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill imageView.contentMode = .scaleAspectFill
imageView.layer.cornerRadius = ChatUIConfig.layout.avatarSize / 2.0
return imageView return imageView
}() }()
lazy var bubbleView: UIImageView = { lazy var bubbleView: UIImageView = {
let imageView = UIImageView() let imageView = UIImageView()
imageView.backgroundColor = .white
imageView.layer.masksToBounds = true
imageView.layer.cornerRadius = 8
return imageView return imageView
}() }()

View File

@@ -0,0 +1,88 @@
//
// ChatImageCell.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/3/1.
//
import UIKit
class ChatImageCell: ChatBaseCell {
override var model: ChatSessionProtocol? {
didSet {
if let model = model as? ChatImageObject {
backImageView.kf.setImage(with: URL(string: model.url),
placeholder: UIImage(named: "public_avatar"))
}
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
bubbleView.addSubview(backImageView)
backImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private lazy var backImageView: UIImageView = {
let imageView = UIImageView()
imageView.isUserInteractionEnabled = true
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill
return imageView
}()
}
extension ChatImageCell {
func setupCellLayout() {
guard let model = model else {return}
if model.msg?.isOutgoingMsg == true { //
avatarImgView.snp.remakeConstraints { (make) in
make.size.equalTo(ChatUIConfig.layout.avatarSize)
make.right.equalToSuperview().offset(-10)
make.top.equalToSuperview().offset(ChatUIConfig.layout.cellContentInsets.top)
}
bubbleView.snp.remakeConstraints { make in
make.size.equalTo(model.contentSize)
make.top.equalTo(avatarImgView)
make.right.equalTo(avatarImgView.snp.left).offset(-10)
}
activityIndicatorView.snp.remakeConstraints { (make) in
make.centerY.equalTo(bubbleView)
make.right.equalTo(bubbleView.snp.left)
make.width.height.equalTo(30)
}
backImageView.snp.remakeConstraints { make in
make.edges.equalToSuperview()
}
// start
activityStartAnimating()
}else {
avatarImgView.snp.remakeConstraints { (make) in
make.size.equalTo(ChatUIConfig.layout.avatarSize)
make.left.equalToSuperview().offset(10)
make.top.equalToSuperview().offset(ChatUIConfig.layout.cellContentInsets.top)
}
bubbleView.snp.remakeConstraints { make in
make.size.equalTo(model.contentSize)
make.top.equalTo(avatarImgView)
make.left.equalTo(avatarImgView.snp.right).offset(10)
}
backImageView.snp.remakeConstraints { make in
make.edges.equalToSuperview()
}
}
}
}

View File

@@ -97,7 +97,7 @@ class ChatListCell: UITableViewCell, Reusable {
let user = users?[safe:0] let user = users?[safe:0]
if let url = user?.userInfo?.avatarUrl { if let url = user?.userInfo?.avatarUrl {
self.avatarImgView.kf.setImage(with: URL(string: url), self.avatarImgView.kf.setImage(with: URL(string: url),
placeholder: nil) placeholder: UIImage(named: "public_avatar"))
} }
self.nameLb.text = user?.userInfo?.nickName self.nameLb.text = user?.userInfo?.nickName

View File

@@ -9,6 +9,18 @@ import UIKit
class ChatNavView: BaseView { class ChatNavView: BaseView {
var isLike:Bool? {
didSet {
self.likeBtn.isHidden = isLike ?? false
}
}
var name:String? {
didSet {
self.titleLb.text = name
}
}
override func loadSubViews() { override func loadSubViews() {
addSubview(backBtn) addSubview(backBtn)
addSubview(titleLb) addSubview(titleLb)
@@ -57,10 +69,11 @@ class ChatNavView: BaseView {
let button = UIButton(type: .custom) let button = UIButton(type: .custom)
button.setTitle("关注", for: .normal) button.setTitle("关注", for: .normal)
button.setTitleColor(ThemeColor(hexStr: "#FFDA24"), for: .normal) button.setTitleColor(ThemeColor(hexStr: "#FFDA24"), for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium) button.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .medium)
button.layer.masksToBounds = true button.layer.masksToBounds = true
button.layer.cornerRadius = 26 button.layer.cornerRadius = 11
button.isEnabled = false button.layer.borderWidth = 0.5
button.layer.borderColor = ThemeColor(hexStr: "#FFDA24").cgColor
button.addTarget(self, action: #selector(likeClick), for: .touchUpInside) button.addTarget(self, action: #selector(likeClick), for: .touchUpInside)
return button return button
}() }()

View File

@@ -19,18 +19,19 @@ class ChatTextCell: ChatBaseCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
initSubview()
} }
func initSubview() { func initSubview() {
bubbleView.addSubview(textLb) bubbleView.addSubview(textLb)
avatarImgView.snp.remakeConstraints { (make) in avatarImgView.snp.makeConstraints { (make) in
make.size.equalTo(ChatUIConfig.layout.avatarSize) make.size.equalTo(ChatUIConfig.layout.avatarSize)
make.left.equalToSuperview().offset(10) make.left.equalToSuperview().offset(10)
make.top.equalTo(contentView).offset(10) make.top.equalTo(contentView).offset(10)
} }
bubbleView.snp.remakeConstraints { (make) in bubbleView.snp.makeConstraints { (make) in
make.top.equalTo(contentView.snp.top).offset(2) make.top.equalTo(contentView.snp.top).offset(2)
make.bottom.equalTo(textLb.snp.bottom).offset(2) make.bottom.equalTo(textLb.snp.bottom).offset(2)
make.left.equalTo(avatarImgView.snp.right) make.left.equalTo(avatarImgView.snp.right)
@@ -38,13 +39,13 @@ class ChatTextCell: ChatBaseCell {
make.height.equalTo(textLb).offset(26) make.height.equalTo(textLb).offset(26)
} }
textLb.snp.remakeConstraints { (make) in textLb.snp.makeConstraints { (make) in
make.top.equalTo(bubbleView).offset(ChatUIConfig.layout.textInsets.top); make.top.equalTo(bubbleView).offset(ChatUIConfig.layout.textInsets.top);
make.left.equalTo(bubbleView).offset(ChatUIConfig.layout.textInsets.left); make.left.equalTo(bubbleView).offset(ChatUIConfig.layout.textInsets.left);
make.right.equalTo(bubbleView).offset(-ChatUIConfig.layout.textInsets.right); make.right.equalTo(bubbleView).offset(-ChatUIConfig.layout.textInsets.right);
} }
activityIndicatorView.snp.remakeConstraints { (make) in activityIndicatorView.snp.makeConstraints { (make) in
make.centerY.equalTo(bubbleView) make.centerY.equalTo(bubbleView)
make.centerX.equalToSuperview() make.centerX.equalToSuperview()
make.width.height.equalTo(30) make.width.height.equalTo(30)
@@ -78,7 +79,7 @@ extension ChatTextCell {
make.top.equalToSuperview().offset(ChatUIConfig.layout.cellContentInsets.top) make.top.equalToSuperview().offset(ChatUIConfig.layout.cellContentInsets.top)
} }
bubbleView.snp.makeConstraints { make in bubbleView.snp.remakeConstraints { make in
make.size.equalTo(model.contentSize) make.size.equalTo(model.contentSize)
make.top.equalTo(avatarImgView) make.top.equalTo(avatarImgView)
make.right.equalTo(avatarImgView.snp.left).offset(-10) make.right.equalTo(avatarImgView.snp.left).offset(-10)
@@ -89,8 +90,8 @@ extension ChatTextCell {
make.width.height.equalTo(30) make.width.height.equalTo(30)
} }
textLb.snp.makeConstraints { make in textLb.snp.remakeConstraints { make in
make.edges.equalTo(ChatUIConfig.layout.textInsets) make.edges.equalToSuperview().inset(ChatUIConfig.layout.textInsets)
} }
// start // start
@@ -103,14 +104,14 @@ extension ChatTextCell {
make.top.equalToSuperview().offset(ChatUIConfig.layout.cellContentInsets.top) make.top.equalToSuperview().offset(ChatUIConfig.layout.cellContentInsets.top)
} }
bubbleView.snp.makeConstraints { make in bubbleView.snp.remakeConstraints { make in
make.size.equalTo(model.contentSize) make.size.equalTo(model.contentSize)
make.top.equalTo(avatarImgView) make.top.equalTo(avatarImgView)
make.left.equalTo(avatarImgView.snp.right).offset(10) make.left.equalTo(avatarImgView.snp.right).offset(10)
} }
textLb.snp.makeConstraints { make in textLb.snp.remakeConstraints { make in
make.edges.equalTo(ChatUIConfig.layout.textInsets) make.edges.equalToSuperview().inset(ChatUIConfig.layout.textInsets)
} }
} }
} }

View File

@@ -0,0 +1,43 @@
//
// ChatTimeCell.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/3/1.
//
import UIKit
import Reusable
class ChatTimeCell: UITableViewCell, Reusable {
var model:ChatSessionProtocol? {
didSet {
if let model = model as? ChatTimeObject {
self.timeLb.text = model.text
}
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none
backgroundColor = ThemeColor(hexStr: "#F8F8FB")
contentView.addSubview(timeLb)
timeLb.snp.makeConstraints { make in
make.edges.equalTo(contentView)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private lazy var timeLb: UILabel = {
let label = UILabel()
label.textColor = ThemeColor(hexStr: "#A2A7B8")
label.font = UIFont.systemFont(ofSize: 12)
label.textAlignment = .center
return label
}()
}

View File

@@ -0,0 +1,156 @@
//
// ChatVoiceCell.swift
// yinmeng-ios
//
// Created by MaiMang on 2024/3/1.
//
import UIKit
import NIMSDK
class ChatVoiceCell: ChatBaseCell, NIMMediaManagerDelegate {
override var model: ChatSessionProtocol? {
didSet {
if let model = model as? ChatVoiceObject {
timeLabel.text = "\(model.duartion / 1000)"
}
}
}
private lazy var timeLabel: UILabel = {
let label = UILabel()
label.textColor = ThemeColor(hexStr: "#2B2D33")
label.font = UIFont.systemFont(ofSize: 14, weight: .medium)
return label
}()
private lazy var voiceImageView: UIImageView = {
let imageView = UIImageView()
imageView.isUserInteractionEnabled = true
let firstImage = UIImage(named:"chat_audio_playing_first")!
let second = UIImage(named:"chat_audio_playing_first")!
let third = UIImage(named:"chat_audio_playing_first")!
imageView.animationImages = [firstImage, second, third];
imageView.animationDuration = 1
imageView.animationRepeatCount = 100
imageView.image = third
return imageView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
NIMSDK.shared().mediaManager.setNeedProximityMonitor(false)
NIMSDK.shared().mediaManager.add(self)
loadSubViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
NIMSDK.shared().mediaManager.remove(self)
}
func loadSubViews() {
bubbleView.addSubview(timeLabel)
bubbleView.addSubview(voiceImageView)
timeLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(10)
make.centerY.equalToSuperview()
}
voiceImageView.snp.makeConstraints { make in
make.size.equalTo(CGSize(width: 20, height: 20))
make.centerY.equalToSuperview()
make.right.equalToSuperview().offset(-10)
}
}
func playAudio(_ filePath: String, didBeganWithError error: Error?) {
if filePath.isEmpty || error != nil { return }
voiceImageView.startAnimating()
}
func playAudio(_ filePath: String, didCompletedWithError error: Error?) {
voiceImageView.stopAnimating()
let thirdImage = UIImage(named: "chat_audio_playing_third")
voiceImageView.image = thirdImage
}
@objc func didTapBackRecognizer() {
if NIMSDK.shared().mediaManager.isPlaying() {
NIMSDK.shared().mediaManager.stopPlay()
}
if let path = self.model as? ChatVoiceObject , path.filPath.count > 0{
NIMSDK.shared().mediaManager.play(path.filPath)
}
}
}
extension ChatVoiceCell {
func setupCellLayout() {
guard let model = model else {return}
if model.msg?.isOutgoingMsg == true { //
avatarImgView.snp.remakeConstraints { (make) in
make.size.equalTo(ChatUIConfig.layout.avatarSize)
make.right.equalToSuperview().offset(-10)
make.top.equalToSuperview().offset(ChatUIConfig.layout.cellContentInsets.top)
}
bubbleView.snp.remakeConstraints { make in
make.size.equalTo(model.contentSize)
make.top.equalTo(avatarImgView)
make.right.equalTo(avatarImgView.snp.left).offset(-10)
}
activityIndicatorView.snp.remakeConstraints { (make) in
make.centerY.equalTo(bubbleView)
make.right.equalTo(bubbleView.snp.left)
make.width.height.equalTo(30)
}
timeLabel.snp.remakeConstraints { make in
make.left.equalToSuperview().offset(10)
make.centerY.equalToSuperview()
}
voiceImageView.snp.remakeConstraints { make in
make.size.equalTo(CGSize(width: 20, height: 20))
make.centerY.equalToSuperview()
make.right.equalToSuperview().offset(-10)
}
// start
activityStartAnimating()
}else {
avatarImgView.snp.remakeConstraints { (make) in
make.size.equalTo(ChatUIConfig.layout.avatarSize)
make.left.equalToSuperview().offset(10)
make.top.equalToSuperview().offset(ChatUIConfig.layout.cellContentInsets.top)
}
bubbleView.snp.remakeConstraints { make in
make.size.equalTo(model.contentSize)
make.top.equalTo(avatarImgView)
make.left.equalTo(avatarImgView.snp.right).offset(10)
}
timeLabel.snp.remakeConstraints { make in
make.left.equalToSuperview().offset(10)
make.centerY.equalToSuperview()
}
voiceImageView.snp.remakeConstraints { make in
make.size.equalTo(CGSize(width: 20, height: 20))
make.centerY.equalToSuperview()
make.right.equalToSuperview().offset(-10)
}
}
}
}