私聊页面的自定义U
@@ -45,6 +45,10 @@
|
||||
E884E8662B6900C500ADE6EE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E884E8642B6900C500ADE6EE /* Main.storyboard */; };
|
||||
E884E8682B6900C600ADE6EE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E884E8672B6900C600ADE6EE /* Assets.xcassets */; };
|
||||
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 */; };
|
||||
E8D15A9F2B89AED600369467 /* AuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D15A9E2B89AED500369467 /* AuthManager.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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -202,8 +210,11 @@
|
||||
E8479E3E2B8DC624009AF878 /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E89F19462B9197090098E797 /* ChatTimeCell.swift */,
|
||||
E8E4AB262B902A750096D77C /* ChatList */,
|
||||
E8479E3C2B8DC61F009AF878 /* ChatBaseCell.swift */,
|
||||
E89F19482B919EB80098E797 /* ChatVoiceCell.swift */,
|
||||
E89F194A2B919ECB0098E797 /* ChatImageCell.swift */,
|
||||
E8479E472B8DD6E1009AF878 /* ChatTextCell.swift */,
|
||||
E8FF28B52B90ED6C005D2BE7 /* ChatNavView.swift */,
|
||||
);
|
||||
@@ -511,6 +522,7 @@
|
||||
E8E4AB202B901CD50096D77C /* ChatMoreMenuView.swift */,
|
||||
E8E4AB222B901E0C0096D77C /* ChatMoreMnueConfig.swift */,
|
||||
E8E4AB242B901E400096D77C /* ChatMoreMenuCell.swift */,
|
||||
E89F19442B917DBA0098E797 /* ChatSendVoiceView.swift */,
|
||||
);
|
||||
path = Keyboard;
|
||||
sourceTree = "<group>";
|
||||
@@ -670,6 +682,7 @@
|
||||
E86A43E82B884C5E0084C04D /* String+.swift in Sources */,
|
||||
E8D15AA32B89B03D00369467 /* Deserialized.swift in Sources */,
|
||||
E8E4AAB72B8F95CA0096D77C /* AuthAppleManager.swift in Sources */,
|
||||
E89F19452B917DBA0098E797 /* ChatSendVoiceView.swift in Sources */,
|
||||
E8D15AC22B8C703C00369467 /* UserFunctionView.swift in Sources */,
|
||||
E86A43DA2B877A840084C04D /* AppConfigObject.swift in Sources */,
|
||||
E8D15AA62B89B0C600369467 /* List+.swift in Sources */,
|
||||
@@ -692,10 +705,12 @@
|
||||
E86A43C82B8743EA0084C04D /* AuthFillDataVC.swift in Sources */,
|
||||
E86A43CD2B874C8E0084C04D /* BaseView.swift in Sources */,
|
||||
E8D15AE82B8CD47100369467 /* WebViewController.swift in Sources */,
|
||||
E89F194B2B919ECB0098E797 /* ChatImageCell.swift in Sources */,
|
||||
E8D15AA12B89AF4F00369467 /* UserTokenObject.swift in Sources */,
|
||||
233E515B2B8C849600582F9C /* PlanetStarClickItemView.swift in Sources */,
|
||||
E8FF28B42B90ADBE005D2BE7 /* AppKeys.swift in Sources */,
|
||||
2311D6A92B8F405F001C70AB /* HomeVoiceChooseTypeView.swift in Sources */,
|
||||
E89F19492B919EB80098E797 /* ChatVoiceCell.swift in Sources */,
|
||||
E8D15AB62B8B002700369467 /* ChatVC.swift in Sources */,
|
||||
E8E4AB182B9019E50096D77C /* ChatKeyboardView.swift in Sources */,
|
||||
E8E4AB232B901E0C0096D77C /* ChatMoreMnueConfig.swift in Sources */,
|
||||
@@ -717,6 +732,7 @@
|
||||
E8D15AB02B8AFFCE00369467 /* HomeVoiceVC.swift in Sources */,
|
||||
E8E4AB282B902A9C0096D77C /* ChatListCell.swift in Sources */,
|
||||
E86A43D52B8774B70084C04D /* AuthViewModel.swift in Sources */,
|
||||
E89F19472B9197090098E797 /* ChatTimeCell.swift in Sources */,
|
||||
E8D15AB32B8B000400369467 /* PlanetStarVC.swift in Sources */,
|
||||
E8FF28B62B90ED6C005D2BE7 /* ChatNavView.swift in Sources */,
|
||||
E8D15A9D2B899E1500369467 /* YMNetworkHelper.swift in Sources */,
|
||||
@@ -881,6 +897,10 @@
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "yinmeng-ios/Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "音萌";
|
||||
INFOPLIST_KEY_NSCameraUsageDescription = "“音萌”需要您的同意,才可以访问进行拍照,才可以在聊天中分享图片给他人查看,或上传图片以更新头像";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "\"音萌\"需要您的同意,才可以进行语音聊天";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "“音萌”需要您的同意,才可以访问相册并选择图片,然后在聊天中分享图片给他人查看,或上传图片以更新头像";
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
||||
@@ -916,6 +936,10 @@
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "yinmeng-ios/Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "音萌";
|
||||
INFOPLIST_KEY_NSCameraUsageDescription = "“音萌”需要您的同意,才可以访问进行拍照,才可以在聊天中分享图片给他人查看,或上传图片以更新头像";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "\"音萌\"需要您的同意,才可以进行语音聊天";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "“音萌”需要您的同意,才可以访问相册并选择图片,然后在聊天中分享图片给他人查看,或上传图片以更新头像";
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
||||
|
@@ -16,5 +16,21 @@
|
||||
stopOnStyle = "0">
|
||||
</BreakpointContent>
|
||||
</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>
|
||||
</Bucket>
|
||||
|
22
yinmeng-ios/Assets.xcassets/chat/chat_more_album.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
BIN
yinmeng-ios/Assets.xcassets/chat/chat_more_album.imageset/chat_more_album@2x.png
vendored
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
yinmeng-ios/Assets.xcassets/chat/chat_more_album.imageset/chat_more_album@3x.png
vendored
Normal file
After Width: | Height: | Size: 4.3 KiB |
22
yinmeng-ios/Assets.xcassets/chat/chat_voice.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
BIN
yinmeng-ios/Assets.xcassets/chat/chat_voice.imageset/chat_voice@2x.png
vendored
Normal file
After Width: | Height: | Size: 823 B |
BIN
yinmeng-ios/Assets.xcassets/chat/chat_voice.imageset/chat_voice@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.2 KiB |
6
yinmeng-ios/Assets.xcassets/chat/voice/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
22
yinmeng-ios/Assets.xcassets/chat/voice/chat_audio_playing_first.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 142 B |
After Width: | Height: | Size: 245 B |
22
yinmeng-ios/Assets.xcassets/chat/voice/chat_audio_playing_second.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 305 B |
After Width: | Height: | Size: 535 B |
22
yinmeng-ios/Assets.xcassets/chat/voice/chat_audio_playing_third.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 437 B |
After Width: | Height: | Size: 774 B |
22
yinmeng-ios/Assets.xcassets/chat/voice/chat_voice_record_cancel.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 2.2 KiB |
22
yinmeng-ios/Assets.xcassets/chat/voice/chat_voice_record_first.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 2.6 KiB |
22
yinmeng-ios/Assets.xcassets/chat/voice/chat_voice_record_second.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 2.7 KiB |
22
yinmeng-ios/Assets.xcassets/chat/voice/chat_voice_record_third.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.7 KiB |
22
yinmeng-ios/Assets.xcassets/public/public_avatar.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
BIN
yinmeng-ios/Assets.xcassets/public/public_avatar.imageset/public_avatar@2x.png
vendored
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
yinmeng-ios/Assets.xcassets/public/public_avatar.imageset/public_avatar@3x.png
vendored
Normal file
After Width: | Height: | Size: 26 KiB |
@@ -19,8 +19,7 @@ class ChatVC: BaseViewController, HiddenNavigationBarProtocol {
|
||||
public init(session: NIMSession) {
|
||||
vm = ChatViewModel(session: session)
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
// vm.delegate = self
|
||||
// NIMSDK.shared().mediaManager.add(self)
|
||||
vm.delegate = self
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@@ -33,9 +32,19 @@ class ChatVC: BaseViewController, HiddenNavigationBarProtocol {
|
||||
.darkContent
|
||||
}
|
||||
|
||||
override open func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
if NIMSDK.shared().mediaManager.isPlaying() {
|
||||
NIMSDK.shared().mediaManager.stopPlay()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.backgroundColor = ThemeColor(hexStr: "#F8F8FB")
|
||||
requestInfo()
|
||||
loadChatList()
|
||||
registerChatCell()
|
||||
let height = ScreenHeight - (ToolBarLastH + NavHeight)
|
||||
chatTableView.frame = CGRect(x: 0, y: NavHeight, width: ScreenWidth, height: height)
|
||||
navView.frame = CGRect(x: 0, y: 0, width: ScreenWidth, height: NavHeight)
|
||||
@@ -47,17 +56,16 @@ class ChatVC: BaseViewController, HiddenNavigationBarProtocol {
|
||||
///注册cell
|
||||
private func registerChatCell() {
|
||||
chatTableView.register(cellType: ChatTextCell.self)
|
||||
chatTableView.register(cellType: ChatTimeCell.self)
|
||||
}
|
||||
|
||||
|
||||
|
||||
private lazy var chatTableView: UITableView = {
|
||||
let tableView = UITableView(frame: .zero, style: .plain)
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
tableView.tableFooterView = UIView()
|
||||
tableView.separatorStyle = .none
|
||||
tableView.backgroundColor = ThemeColor(hexStr: "#F8F8FB")
|
||||
tableView.backgroundColor = .clear
|
||||
if #available(iOS 11.0, *) {
|
||||
tableView.contentInsetAdjustmentBehavior = .never
|
||||
}
|
||||
@@ -84,6 +92,12 @@ class ChatVC: BaseViewController, HiddenNavigationBarProtocol {
|
||||
// MARK: - ChatKeyboardViewDelegate
|
||||
extension ChatVC: ChatKeyboardViewDelegate {
|
||||
|
||||
func keyboard(_ keyboard: ChatKeyboardView, voiceDidFinish path: String) {
|
||||
vm.sendAudioMessage(filePath: path) { error in
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func keyboard(_ keyboard: ChatKeyboardView, DidFinish content: String) {
|
||||
///发送消息
|
||||
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 {
|
||||
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return vm.messageObjects.count
|
||||
@@ -117,6 +263,9 @@ extension ChatVC: UITableViewDelegate, UITableViewDataSource, UIScrollViewDelega
|
||||
if model?.type == .text {
|
||||
let cell = tableView.dequeueReusableCell(for: indexPath, cellType: ChatTextCell.self)
|
||||
cell.model = model
|
||||
} else if model?.type == .time {
|
||||
let cell = tableView.dequeueReusableCell(for: indexPath, cellType: ChatTimeCell.self)
|
||||
cell.model = model
|
||||
}
|
||||
return UITableViewCell()
|
||||
}
|
||||
|
@@ -77,7 +77,7 @@ class ChatGrowingTextView: UITextView {
|
||||
self.returnKeyType = .send
|
||||
|
||||
self.layer.cornerRadius = 4
|
||||
self.layer.borderWidth = 1
|
||||
self.layer.borderWidth = 0
|
||||
self.layer.borderColor = ThemeColor(hexStr: "#2C363E").cgColor
|
||||
self.layer.masksToBounds = true
|
||||
// 添加占位控件
|
||||
|
@@ -6,8 +6,10 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import NIMSDK
|
||||
protocol ChatKeyboardViewDelegate: NSObjectProtocol {
|
||||
/// 录音完成
|
||||
func keyboard(_ keyboard: ChatKeyboardView, voiceDidFinish path: String)
|
||||
/// 输入完消息
|
||||
func keyboard(_ keyboard: ChatKeyboardView, DidFinish content: String)
|
||||
/// 键盘收起/弹出
|
||||
@@ -30,14 +32,14 @@ class ChatKeyboardView: UIView {
|
||||
|
||||
private let kLeft: 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
|
||||
|
||||
// MARK: - var lazy
|
||||
|
||||
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 keyboardHeight: CGFloat = 0.0
|
||||
|
||||
@@ -48,14 +50,29 @@ class ChatKeyboardView: UIView {
|
||||
/// 是否弹出了系统键盘
|
||||
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 = {
|
||||
let button = UIButton(type: .custom)
|
||||
let x: CGFloat = self.kLeft
|
||||
button.frame = CGRect(x: x, y: self.kSpace, width: self.kViewWH, height: self.kViewWH)
|
||||
button.setImage(UIImage(named: "chat_more"), for: .normal)
|
||||
button.setImage(UIImage(named: "chat_more"), for: .highlighted)
|
||||
button.addTarget(self, action: #selector(moreDidAction(_:)), for: .touchUpInside)
|
||||
button.frame = CGRect(x: x, y: self.kSpace + 5, width: self.kViewWH, height: self.kViewWH)
|
||||
button.setImage(UIImage(named: "chat_input_text"), for: .normal)
|
||||
button.setImage(UIImage(named: "chat_voice"), for: .highlighted)
|
||||
button.addTarget(self, action: #selector(changeDidAction(_:)), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
||||
@@ -63,7 +80,7 @@ class ChatKeyboardView: UIView {
|
||||
fileprivate lazy var moreButton : UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
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: .highlighted)
|
||||
button.addTarget(self, action: #selector(moreDidAction(_:)), for: .touchUpInside)
|
||||
@@ -109,11 +126,17 @@ class ChatKeyboardView: UIView {
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var voiceView: ChatSendVoiceView = {
|
||||
let view = ChatSendVoiceView()
|
||||
view.backgroundColor = .clear
|
||||
return view
|
||||
}()
|
||||
|
||||
// MARK: - life cycle
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
NIMSDK.shared().mediaManager.add(self)
|
||||
setupKeyboardView()
|
||||
registerNotification()
|
||||
}
|
||||
@@ -128,15 +151,23 @@ class ChatKeyboardView: UIView {
|
||||
func setupKeyboardView() {
|
||||
self.backgroundColor = .clear
|
||||
self.isUserInteractionEnabled = true
|
||||
|
||||
addSendVoiceAction()
|
||||
addSubview(toolBarView)
|
||||
toolBarView.addSubview(moreButton)
|
||||
toolBarView.addSubview(changeButton)
|
||||
toolBarView.addSubview(chatTextView)
|
||||
toolBarView.addSubview(sendVoiceBtn)
|
||||
|
||||
addSubview(contentView)
|
||||
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: - 监听键盘通知
|
||||
private func registerNotification() {
|
||||
@@ -171,18 +202,30 @@ class ChatKeyboardView: UIView {
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
NIMSDK.shared().mediaManager.remove(self)
|
||||
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
|
||||
|
||||
extension ChatKeyboardView: ChatMoreMenuViewDelegate {
|
||||
|
||||
func menu(_ view: ChatMoreMenuView, DidSelected type: ChatMoreMenuType) {
|
||||
|
||||
delegate?.keyboard(self, DidMoreMenu: type)
|
||||
}
|
||||
}
|
||||
@@ -209,7 +252,6 @@ extension ChatKeyboardView: UITextViewDelegate {
|
||||
/// 发送消息内容
|
||||
private func sendChatMessage() {
|
||||
delegate?.keyboard(self, DidFinish: self.chatTextView.text ?? "")
|
||||
|
||||
changeKeyboardHeight(height: lastTextHeight)
|
||||
chatTextView.text = ""
|
||||
chatTextView.attributedText = NSAttributedString(string: "")
|
||||
@@ -240,12 +282,12 @@ extension ChatKeyboardView {
|
||||
|
||||
let option = userInfo["UIKeyboardAnimationCurveUserInfoKey"] as! Int
|
||||
|
||||
var changedY = y - self.toolBarHeight - NavHeight - contentHeight
|
||||
var changedY = y - self.toolBarHeight - contentHeight
|
||||
if (isShowMore) { //显示系统键盘
|
||||
isShowMore = false
|
||||
self.moreMenuView.isHidden = 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: {
|
||||
@@ -270,7 +312,7 @@ extension ChatKeyboardView {
|
||||
isShowKeyboard = false
|
||||
|
||||
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: {
|
||||
self.frame = CGRect(x: 0, y: changedY, width: ScreenWidth, height: self.toolBarHeight + self.contentHeight)
|
||||
}, completion: nil)
|
||||
@@ -293,10 +335,69 @@ extension ChatKeyboardView {
|
||||
// MARK: - Action
|
||||
|
||||
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) {
|
||||
// 如有弹出菜单
|
||||
if isShowMore {
|
||||
isShowMore = false
|
||||
contentView.isHidden = true
|
||||
moreMenuView.isHidden = true
|
||||
contentHeight = 0.0
|
||||
restToolbarContentHeight(true)
|
||||
delegate?.keyboard(self, DidBecome: false)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -314,13 +415,13 @@ extension ChatKeyboardView {
|
||||
|
||||
/// 更改容器高度
|
||||
func restToolbarContentHeight(_ isRest: Bool = false) {
|
||||
var changedY = ScreenHeight - self.toolBarHeight - NavHeight - contentHeight
|
||||
var changedY = ScreenHeight - self.toolBarHeight - contentHeight
|
||||
if (isRest) {
|
||||
if (isShowMore) {
|
||||
isShowMore = false
|
||||
}
|
||||
|
||||
changedY = ScreenHeight - self.toolBarHeight - NavHeight - contentHeight - SafeAraeBottomHeight
|
||||
changedY = ScreenHeight - self.toolBarHeight - contentHeight
|
||||
}
|
||||
|
||||
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut, animations: {
|
||||
@@ -355,10 +456,10 @@ extension ChatKeyboardView {
|
||||
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)
|
||||
}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)
|
||||
}
|
||||
|
||||
|
@@ -67,8 +67,7 @@ class ChatMoreMenuView: UIView {
|
||||
|
||||
lazy var dataSource: [ChatMoreMnueConfig] = {
|
||||
let configs = [
|
||||
ChatMoreMnueConfig(title: "图片", image: "ic_more_album", type: .album),
|
||||
ChatMoreMnueConfig(title: "拍照", image: "ic_more_camera", type: .camera)
|
||||
ChatMoreMnueConfig(title: "图片", image: "chat_more_album", type: .album),
|
||||
]
|
||||
return configs
|
||||
}()
|
||||
@@ -138,7 +137,7 @@ class ChatMoreMenuView: UIView {
|
||||
|
||||
UIView.animate(withDuration: 0.15, delay: 0, options: .curveEaseOut, animations: {
|
||||
self.pageControl.alpha = 1.0
|
||||
self.pageControl.isHidden = false
|
||||
self.pageControl.isHidden = self.dataSource.count > 8 ? false : true
|
||||
}, completion: nil)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
124
yinmeng-ios/Modules/Chat/Keyboard/ChatSendVoiceView.swift
Normal 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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -12,6 +12,7 @@ public enum SessionType: Int {
|
||||
case text = 1
|
||||
case image
|
||||
case time
|
||||
case voice
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +67,7 @@ class ChatTextObject: ChatBaseObject {
|
||||
|
||||
let style = NSMutableParagraphStyle()
|
||||
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
|
||||
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 {
|
||||
var text:String = ""
|
||||
public var attribute: NSMutableAttributedString?
|
||||
required init(msg: NIMMessage?) {
|
||||
super.init(msg: msg)
|
||||
type = .time
|
||||
let style = NSMutableParagraphStyle()
|
||||
style.lineSpacing = 6
|
||||
let attributeStr = NSMutableAttributedString(string: msg?.text ?? "", attributes: [NSAttributedString.Key.font: ChatUIConfig.ui.messageFont, NSAttributedString.Key.paragraphStyle: style])
|
||||
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)
|
||||
text = timestrToTimeSecond("\(msg?.timestamp ?? 0)")
|
||||
contentSize = CGSize(width: ScreenWidth, height: 30)
|
||||
height = Float(50)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@@ -46,9 +46,9 @@ public class ChatUIConfig {
|
||||
/// 头像大小
|
||||
public var avatarSize: CGFloat = 34.0
|
||||
/// 聊天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)
|
||||
/// 图片最大宽高
|
||||
|
@@ -406,8 +406,6 @@ public class ChatViewModel: NSObject,
|
||||
let timeMsg = NIMMessage()
|
||||
timeMsg.timestamp = message.timestamp
|
||||
let model = ChatTimeObject(msg: timeMsg)
|
||||
model.type = .time
|
||||
model.text = "2024-2-28"
|
||||
return model
|
||||
}
|
||||
|
||||
@@ -418,11 +416,12 @@ public class ChatViewModel: NSObject,
|
||||
switch message.messageType {
|
||||
case .text:
|
||||
model = ChatTextObject(msg: message)
|
||||
case .audio:
|
||||
model = ChatVoiceObject(msg: message)
|
||||
case .image:
|
||||
model = ChatImageObject(msg: message)
|
||||
default:
|
||||
return nil
|
||||
// 未识别的消息类型,默认为文本消息类型,text为未知消息
|
||||
// message.text = "未知消息"
|
||||
// model = MAIMessageContentModel(message: message)
|
||||
}
|
||||
|
||||
if let uid = message.from {
|
||||
|
@@ -7,6 +7,7 @@
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
import NIMSDK
|
||||
protocol ChatBaseCellProtocol: NSObjectProtocol {
|
||||
func cell(_ cell: ChatBaseCell, didTapAvatarAt model: ChatBaseObject)
|
||||
}
|
||||
@@ -15,7 +16,16 @@ class ChatBaseCell: UITableViewCell, Reusable{
|
||||
weak var delegate: ChatBaseCellProtocol?
|
||||
var model:ChatSessionProtocol? {
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -45,7 +55,7 @@ class ChatBaseCell: UITableViewCell, Reusable{
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
selectionStyle = .none
|
||||
backgroundColor = .clear
|
||||
backgroundColor = ThemeColor(hexStr: "#F8F8FB")
|
||||
contentView.addSubview(avatarImgView)
|
||||
contentView.addSubview(bubbleView)
|
||||
contentView.addSubview(activityIndicatorView)
|
||||
@@ -61,11 +71,15 @@ class ChatBaseCell: UITableViewCell, Reusable{
|
||||
imageView.isUserInteractionEnabled = true
|
||||
imageView.layer.masksToBounds = true
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
imageView.layer.cornerRadius = ChatUIConfig.layout.avatarSize / 2.0
|
||||
return imageView
|
||||
}()
|
||||
|
||||
lazy var bubbleView: UIImageView = {
|
||||
let imageView = UIImageView()
|
||||
imageView.backgroundColor = .white
|
||||
imageView.layer.masksToBounds = true
|
||||
imageView.layer.cornerRadius = 8
|
||||
return imageView
|
||||
}()
|
||||
|
||||
|
88
yinmeng-ios/Modules/Chat/View/ChatImageCell.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -97,7 +97,7 @@ class ChatListCell: UITableViewCell, Reusable {
|
||||
let user = users?[safe:0]
|
||||
if let url = user?.userInfo?.avatarUrl {
|
||||
self.avatarImgView.kf.setImage(with: URL(string: url),
|
||||
placeholder: nil)
|
||||
placeholder: UIImage(named: "public_avatar"))
|
||||
}
|
||||
|
||||
self.nameLb.text = user?.userInfo?.nickName
|
||||
|
@@ -9,6 +9,18 @@ import UIKit
|
||||
|
||||
class ChatNavView: BaseView {
|
||||
|
||||
var isLike:Bool? {
|
||||
didSet {
|
||||
self.likeBtn.isHidden = isLike ?? false
|
||||
}
|
||||
}
|
||||
|
||||
var name:String? {
|
||||
didSet {
|
||||
self.titleLb.text = name
|
||||
}
|
||||
}
|
||||
|
||||
override func loadSubViews() {
|
||||
addSubview(backBtn)
|
||||
addSubview(titleLb)
|
||||
@@ -57,10 +69,11 @@ class ChatNavView: BaseView {
|
||||
let button = UIButton(type: .custom)
|
||||
button.setTitle("关注", 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.cornerRadius = 26
|
||||
button.isEnabled = false
|
||||
button.layer.cornerRadius = 11
|
||||
button.layer.borderWidth = 0.5
|
||||
button.layer.borderColor = ThemeColor(hexStr: "#FFDA24").cgColor
|
||||
button.addTarget(self, action: #selector(likeClick), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
@@ -19,18 +19,19 @@ class ChatTextCell: ChatBaseCell {
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
initSubview()
|
||||
}
|
||||
|
||||
func initSubview() {
|
||||
bubbleView.addSubview(textLb)
|
||||
|
||||
avatarImgView.snp.remakeConstraints { (make) in
|
||||
avatarImgView.snp.makeConstraints { (make) in
|
||||
make.size.equalTo(ChatUIConfig.layout.avatarSize)
|
||||
make.left.equalToSuperview().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.bottom.equalTo(textLb.snp.bottom).offset(2)
|
||||
make.left.equalTo(avatarImgView.snp.right)
|
||||
@@ -38,13 +39,13 @@ class ChatTextCell: ChatBaseCell {
|
||||
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.left.equalTo(bubbleView).offset(ChatUIConfig.layout.textInsets.left);
|
||||
make.right.equalTo(bubbleView).offset(-ChatUIConfig.layout.textInsets.right);
|
||||
}
|
||||
|
||||
activityIndicatorView.snp.remakeConstraints { (make) in
|
||||
activityIndicatorView.snp.makeConstraints { (make) in
|
||||
make.centerY.equalTo(bubbleView)
|
||||
make.centerX.equalToSuperview()
|
||||
make.width.height.equalTo(30)
|
||||
@@ -78,7 +79,7 @@ extension ChatTextCell {
|
||||
make.top.equalToSuperview().offset(ChatUIConfig.layout.cellContentInsets.top)
|
||||
}
|
||||
|
||||
bubbleView.snp.makeConstraints { make in
|
||||
bubbleView.snp.remakeConstraints { make in
|
||||
make.size.equalTo(model.contentSize)
|
||||
make.top.equalTo(avatarImgView)
|
||||
make.right.equalTo(avatarImgView.snp.left).offset(-10)
|
||||
@@ -89,8 +90,8 @@ extension ChatTextCell {
|
||||
make.width.height.equalTo(30)
|
||||
}
|
||||
|
||||
textLb.snp.makeConstraints { make in
|
||||
make.edges.equalTo(ChatUIConfig.layout.textInsets)
|
||||
textLb.snp.remakeConstraints { make in
|
||||
make.edges.equalToSuperview().inset(ChatUIConfig.layout.textInsets)
|
||||
}
|
||||
|
||||
// start
|
||||
@@ -103,14 +104,14 @@ extension ChatTextCell {
|
||||
make.top.equalToSuperview().offset(ChatUIConfig.layout.cellContentInsets.top)
|
||||
}
|
||||
|
||||
bubbleView.snp.makeConstraints { make in
|
||||
bubbleView.snp.remakeConstraints { make in
|
||||
make.size.equalTo(model.contentSize)
|
||||
make.top.equalTo(avatarImgView)
|
||||
make.left.equalTo(avatarImgView.snp.right).offset(10)
|
||||
}
|
||||
|
||||
textLb.snp.makeConstraints { make in
|
||||
make.edges.equalTo(ChatUIConfig.layout.textInsets)
|
||||
textLb.snp.remakeConstraints { make in
|
||||
make.edges.equalToSuperview().inset(ChatUIConfig.layout.textInsets)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
43
yinmeng-ios/Modules/Chat/View/ChatTimeCell.swift
Normal 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
|
||||
}()
|
||||
|
||||
}
|
156
yinmeng-ios/Modules/Chat/View/ChatVoiceCell.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|