diff --git a/yinmeng-ios.xcodeproj/project.pbxproj b/yinmeng-ios.xcodeproj/project.pbxproj index 474011d..cd86a3b 100644 --- a/yinmeng-ios.xcodeproj/project.pbxproj +++ b/yinmeng-ios.xcodeproj/project.pbxproj @@ -64,6 +64,15 @@ E8D15AEA2B8CD77800369467 /* H5Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D15AE92B8CD77800369467 /* H5Utils.swift */; }; E8E4AAB52B8F8E3A0096D77C /* AuthItmeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E4AAB42B8F8E3A0096D77C /* AuthItmeButton.swift */; }; E8E4AAB72B8F95CA0096D77C /* AuthAppleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E4AAB62B8F95CA0096D77C /* AuthAppleManager.swift */; }; + E8E4AB182B9019E50096D77C /* ChatKeyboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E4AB172B9019E50096D77C /* ChatKeyboardView.swift */; }; + E8E4AB1B2B901AF50096D77C /* UIView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E4AB1A2B901AF50096D77C /* UIView+.swift */; }; + E8E4AB1D2B901B9D0096D77C /* ChatKeyboard+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E4AB1C2B901B9D0096D77C /* ChatKeyboard+.swift */; }; + E8E4AB1F2B901BFC0096D77C /* ChatGrowingTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E4AB1E2B901BFC0096D77C /* ChatGrowingTextView.swift */; }; + E8E4AB212B901CD50096D77C /* ChatMoreMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E4AB202B901CD50096D77C /* ChatMoreMenuView.swift */; }; + E8E4AB232B901E0C0096D77C /* ChatMoreMnueConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E4AB222B901E0C0096D77C /* ChatMoreMnueConfig.swift */; }; + E8E4AB252B901E400096D77C /* ChatMoreMenuCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E4AB242B901E400096D77C /* ChatMoreMenuCell.swift */; }; + E8E4AB282B902A9C0096D77C /* ChatListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E4AB272B902A9C0096D77C /* ChatListCell.swift */; }; + E8FF28B42B90ADBE005D2BE7 /* AppKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FF28B32B90ADBE005D2BE7 /* AppKeys.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -132,6 +141,15 @@ E8E4AAB42B8F8E3A0096D77C /* AuthItmeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthItmeButton.swift; sourceTree = ""; }; E8E4AAB62B8F95CA0096D77C /* AuthAppleManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthAppleManager.swift; sourceTree = ""; }; E8E4AAB82B8F99B90096D77C /* yinmeng-ios.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "yinmeng-ios.entitlements"; sourceTree = ""; }; + E8E4AB172B9019E50096D77C /* ChatKeyboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatKeyboardView.swift; sourceTree = ""; }; + E8E4AB1A2B901AF50096D77C /* UIView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+.swift"; sourceTree = ""; }; + E8E4AB1C2B901B9D0096D77C /* ChatKeyboard+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChatKeyboard+.swift"; sourceTree = ""; }; + E8E4AB1E2B901BFC0096D77C /* ChatGrowingTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatGrowingTextView.swift; sourceTree = ""; }; + E8E4AB202B901CD50096D77C /* ChatMoreMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMoreMenuView.swift; sourceTree = ""; }; + E8E4AB222B901E0C0096D77C /* ChatMoreMnueConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMoreMnueConfig.swift; sourceTree = ""; }; + E8E4AB242B901E400096D77C /* ChatMoreMenuCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMoreMenuCell.swift; sourceTree = ""; }; + E8E4AB272B902A9C0096D77C /* ChatListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListCell.swift; sourceTree = ""; }; + E8FF28B32B90ADBE005D2BE7 /* AppKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppKeys.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -182,6 +200,7 @@ E8479E3E2B8DC624009AF878 /* View */ = { isa = PBXGroup; children = ( + E8E4AB262B902A750096D77C /* ChatList */, E8479E3C2B8DC61F009AF878 /* ChatBaseCell.swift */, E8479E472B8DD6E1009AF878 /* ChatTextCell.swift */, ); @@ -240,6 +259,7 @@ E86A43AB2B85DFC20084C04D /* Extension */ = { isa = PBXGroup; children = ( + E8E4AB192B901AEA0096D77C /* UIView */, E8479E442B8DD5B5009AF878 /* Date */, E8D15AA42B89B0BA00369467 /* List */, E86A43E62B884C520084C04D /* String */, @@ -308,6 +328,7 @@ E86A43BE2B8620C40084C04D /* Utils.swift */, E86A43D22B8773C90084C04D /* APPUtils.swift */, E8D15AE92B8CD77800369467 /* H5Utils.swift */, + E8FF28B32B90ADBE005D2BE7 /* AppKeys.swift */, ); path = Utils; sourceTree = ""; @@ -423,6 +444,7 @@ E8D15AB42B8B001900369467 /* Chat */ = { isa = PBXGroup; children = ( + E8E4AB162B9019CE0096D77C /* Keyboard */, E8479E492B8DDA3F009AF878 /* Tool */, E8479E3F2B8DC6A6009AF878 /* Model */, E8479E3E2B8DC624009AF878 /* View */, @@ -477,6 +499,35 @@ path = Web; sourceTree = ""; }; + E8E4AB162B9019CE0096D77C /* Keyboard */ = { + isa = PBXGroup; + children = ( + E8E4AB172B9019E50096D77C /* ChatKeyboardView.swift */, + E8E4AB1E2B901BFC0096D77C /* ChatGrowingTextView.swift */, + E8E4AB1C2B901B9D0096D77C /* ChatKeyboard+.swift */, + E8E4AB202B901CD50096D77C /* ChatMoreMenuView.swift */, + E8E4AB222B901E0C0096D77C /* ChatMoreMnueConfig.swift */, + E8E4AB242B901E400096D77C /* ChatMoreMenuCell.swift */, + ); + path = Keyboard; + sourceTree = ""; + }; + E8E4AB192B901AEA0096D77C /* UIView */ = { + isa = PBXGroup; + children = ( + E8E4AB1A2B901AF50096D77C /* UIView+.swift */, + ); + path = UIView; + sourceTree = ""; + }; + E8E4AB262B902A750096D77C /* ChatList */ = { + isa = PBXGroup; + children = ( + E8E4AB272B902A9C0096D77C /* ChatListCell.swift */, + ); + path = ChatList; + sourceTree = ""; + }; E8EE60802B8858A500D02F6E /* Security */ = { isa = PBXGroup; children = ( @@ -628,19 +679,25 @@ E8479E3B2B8DC5FA009AF878 /* ChatViewModel.swift in Sources */, E8479E412B8DC6BC009AF878 /* ChatBaseObject.swift in Sources */, E86A43AA2B85DFA90084C04D /* BaseViewController.swift in Sources */, + E8E4AB1F2B901BFC0096D77C /* ChatGrowingTextView.swift in Sources */, E884E85F2B6900C500ADE6EE /* AppDelegate.swift in Sources */, E8D15AEA2B8CD77800369467 /* H5Utils.swift in Sources */, E8D15AAA2B8ACC6B00369467 /* YMNetworkFun.swift in Sources */, E8D15AB82B8B003C00369467 /* UserInfoVC.swift in Sources */, E86A43B82B85F0B80084C04D /* AuthLaunchVC.swift in Sources */, + E8E4AB252B901E400096D77C /* ChatMoreMenuCell.swift in Sources */, E86A43C82B8743EA0084C04D /* AuthFillDataVC.swift in Sources */, E86A43CD2B874C8E0084C04D /* BaseView.swift in Sources */, E8D15AE82B8CD47100369467 /* WebViewController.swift in Sources */, E8D15AA12B89AF4F00369467 /* UserTokenObject.swift in Sources */, 233E515B2B8C849600582F9C /* PlanetStarClickItemView.swift in Sources */, + E8FF28B42B90ADBE005D2BE7 /* AppKeys.swift in Sources */, 2311D6A92B8F405F001C70AB /* HomeVoiceChooseTypeView.swift in Sources */, E8D15AB62B8B002700369467 /* ChatVC.swift in Sources */, + E8E4AB182B9019E50096D77C /* ChatKeyboardView.swift in Sources */, + E8E4AB232B901E0C0096D77C /* ChatMoreMnueConfig.swift in Sources */, E86A43C62B862CC70084C04D /* UIImage+.swift in Sources */, + E8E4AB212B901CD50096D77C /* ChatMoreMenuView.swift in Sources */, E86A43D32B8773C90084C04D /* APPUtils.swift in Sources */, E86A43CB2B874C6F0084C04D /* AuthPrivacyView.swift in Sources */, E8D15AC52B8C90D400369467 /* AboutUsVC.swift in Sources */, @@ -652,11 +709,14 @@ E8479E4D2B8DDBC5009AF878 /* ChatAttributeTool.swift in Sources */, E81A7BAF2B885B20009E736E /* MAIDESEncryptTool.m in Sources */, E8D15AA82B89B74700369467 /* YMRequestX.swift in Sources */, + E8E4AB1D2B901B9D0096D77C /* ChatKeyboard+.swift in Sources */, 2311D6A72B8F2CFA001C70AB /* HomeVoiceChooseItemVeiw.swift in Sources */, E8D15AB02B8AFFCE00369467 /* HomeVoiceVC.swift in Sources */, + E8E4AB282B902A9C0096D77C /* ChatListCell.swift in Sources */, E86A43D52B8774B70084C04D /* AuthViewModel.swift in Sources */, E8D15AB32B8B000400369467 /* PlanetStarVC.swift in Sources */, E8D15A9D2B899E1500369467 /* YMNetworkHelper.swift in Sources */, + E8E4AB1B2B901AF50096D77C /* UIView+.swift in Sources */, 2311D69D2B8DC311001C70AB /* PlanetStarModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/yinmeng-ios.xcworkspace/xcuserdata/fengshuo.xcuserdatad/UserInterfaceState.xcuserstate b/yinmeng-ios.xcworkspace/xcuserdata/fengshuo.xcuserdatad/UserInterfaceState.xcuserstate index bd3b69f..9992275 100644 Binary files a/yinmeng-ios.xcworkspace/xcuserdata/fengshuo.xcuserdatad/UserInterfaceState.xcuserstate and b/yinmeng-ios.xcworkspace/xcuserdata/fengshuo.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/yinmeng-ios/AppDelegate.swift b/yinmeng-ios/AppDelegate.swift index 0721299..3ff0f01 100644 --- a/yinmeng-ios/AppDelegate.swift +++ b/yinmeng-ios/AppDelegate.swift @@ -8,6 +8,7 @@ import UIKit import DeviceKit import NSObject_Rx +import NIMSDK @main class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? @@ -18,7 +19,9 @@ var window: UIWindow? self.window = UIWindow.init(frame: UIScreen.main.bounds) self.window?.backgroundColor = UIColor.white self.window?.rootViewController = BaseNavigationViewController(rootViewController:AuthLaunchVC()) + loadNIMSDK() loginStateListener() + return true } @@ -34,13 +37,36 @@ var window: UIWindow? if let uid = LoginTokenConfig.config.getAccountInfo()?.uid { UserViewModel.userVM.getUserInfo(uid: uid) } + if NIMSDK.shared().loginManager.isLogined() == false { + if let uid = LoginTokenConfig.config.getAccountInfo()?.uid, let token = LoginTokenConfig.config.getAccountInfo()?.netEaseToken { + NIMSDK.shared().loginManager.login("\(uid)", token: token) { error in + print("aaa") + } + } else { + ///去登录 + self.window?.rootViewController = BaseNavigationViewController(rootViewController:AuthLaunchVC()) + + } + } } else { ///去登录 self.window?.rootViewController = BaseNavigationViewController(rootViewController:AuthLaunchVC()) } }).disposed(by: rx.disposeBag) + UserViewModel.userVM.userInfo.subscribe(onNext: { result in + if result.nick?.count ?? 0 <= 0 || result.avatar?.count ?? 0 <= 0 { + let fillVC = AuthFillDataVC() + fillVC.modalPresentationStyle = .fullScreen + YMRequestX.topViewController()?.navigationController?.present(fillVC, animated: true) + } + }).disposed(by: rx.disposeBag) + } + private func loadNIMSDK() { + let opt = NIMSDKOption(appKey: AppKeys.nimAppid) + opt.apnsCername = "yinmeng_anps" + NIMSDK.shared().register(with: opt) } } diff --git a/yinmeng-ios/Base/Request/YMNetworkHelper.swift b/yinmeng-ios/Base/Request/YMNetworkHelper.swift index 5c0bd89..534cd24 100644 --- a/yinmeng-ios/Base/Request/YMNetworkHelper.swift +++ b/yinmeng-ios/Base/Request/YMNetworkHelper.swift @@ -2,7 +2,7 @@ // YMNetworkAPI.swift // yinmeng-ios // -// Created by MaiMang on 2024/2/24. +// Created by Mang on 2024/2/24. // import Foundation @@ -37,7 +37,7 @@ class YMNetworkHelper: NSObject { func requestSend(type:HTTPMethod,path:String,params:Dictionary, succeed:SessionCallSucceed?,fail:SessionCallFail?) -> Void { getHttpRequestHeaders() - requestSend(type: type,host: "https://api.ymlive.fun/", path: path, params: params, encoding: URLEncoding.queryString, header: headersSet, succeed: succeed, fail: fail) + requestSend(type: type,host: AppKeys.api, path: path, params: params, encoding: URLEncoding.queryString, header: headersSet, succeed: succeed, fail: fail) } func requestSend(type:HTTPMethod, @@ -74,50 +74,50 @@ class YMNetworkHelper: NSObject { func analyzeThe(response1:AFDataResponse, succeed2:SessionCallSucceed?, fail3: SessionCallFail?,uuid4:String) -> Void { - let maiUrlSss = response1.request?.url?.absoluteString ?? "unkown" + let UrlSss = response1.request?.url?.absoluteString ?? "unkown" switch response1.result { case .success: - let maiResponNk :Dictionary = response1.value as? Dictionary ?? Dictionary.init() - let maiResultMo = maiResponNk - if maiResultMo.keys.contains("code") { - let maicodeNum :Int = maiResultMo["code"] as? Int ?? 0 - if maicodeNum == 200 { - if maiResultMo.keys.contains("data") { - let maiDDD = maiResultMo["data"] as Any - succeed2?(maiDDD) + let ResponNk :Dictionary = response1.value as? Dictionary ?? Dictionary.init() + let ResultMo = ResponNk + if ResultMo.keys.contains("code") { + let codeNum :Int = ResultMo["code"] as? Int ?? 0 + if codeNum == 200 { + if ResultMo.keys.contains("data") { + let DDD = ResultMo["data"] as Any + succeed2?(DDD) }else{ succeed2?(Dictionary.init()) } }else{ - if maicodeNum == 401 && maiUrlSss.contains("auth-center/sso/logout") == false { - NotificationCenter.default.post(name: NSNotification.Name(rawValue: "MAISessionTickValid"), object: nil) + if codeNum == 401 && UrlSss.contains("auth-center/sso/logout") == false { + NotificationCenter.default.post(name: NSNotification.Name(rawValue: "SessionTickValid"), object: nil) sessionNetMana.cancelAllRequests() } var messageIn = response1.error.debugDescription - if maiResultMo.keys.contains("message") { messageIn = maiResultMo["message"] as? String ?? "" } - fail3?(maicodeNum,messageIn) + if ResultMo.keys.contains("message") { messageIn = ResultMo["message"] as? String ?? "" } + fail3?(codeNum,messageIn) } - } else if maiResultMo.keys.contains("errno") { - let maiCodeNum :Int = maiResultMo["errno"] as? Int ?? 0 - if maiCodeNum == 0 { - if maiResultMo.keys.contains("data") { - let dataSc = maiResultMo["data"] as Any + } else if ResultMo.keys.contains("errno") { + let CodeNum :Int = ResultMo["errno"] as? Int ?? 0 + if CodeNum == 0 { + if ResultMo.keys.contains("data") { + let dataSc = ResultMo["data"] as Any succeed2?(dataSc) }else{ succeed2?(Dictionary.init()) } }else{ var majmessageStr = response1.error.debugDescription - if maiResultMo.keys.contains("errmsg") { majmessageStr = maiResultMo["errmsg"] as? String ?? ""} - fail3?(maiCodeNum,majmessageStr) + if ResultMo.keys.contains("errmsg") { majmessageStr = ResultMo["errmsg"] as? String ?? ""} + fail3?(CodeNum,majmessageStr) } } else { fail3?(10000,"请求失败") } case let .failure(error): - var maiErrorMssg = response1.error?.errorDescription ?? "" - var maicodeNum = response1.error?.responseCode ?? 0 - fail3?(maicodeNum,maiErrorMssg) + var ErrorMssg = response1.error?.errorDescription ?? "" + var codeNum = response1.error?.responseCode ?? 0 + fail3?(codeNum,ErrorMssg) } } diff --git a/yinmeng-ios/Base/Utils/AppKeys.swift b/yinmeng-ios/Base/Utils/AppKeys.swift new file mode 100644 index 0000000..0a9ba97 --- /dev/null +++ b/yinmeng-ios/Base/Utils/AppKeys.swift @@ -0,0 +1,17 @@ +// +// AppKeys.swift +// yinmeng-ios +// +// Created by MaiMang on 2024/2/29. +// + +import Foundation +enum AppKeys { +#if DEBUG + static let nimAppid = "5d5a833a2d0ff1304a5d8bed53d2af5b" + static let api = "http://beta.api.ymlive.fun/" +#else + static let nimAppid = "5e76ec47632d86c30ce18eabfa332b6a" + static let api = "https://api.ymlive.fun/" +#endif +} diff --git a/yinmeng-ios/Base/Utils/H5Utils.swift b/yinmeng-ios/Base/Utils/H5Utils.swift index 2796b3b..13324b4 100644 --- a/yinmeng-ios/Base/Utils/H5Utils.swift +++ b/yinmeng-ios/Base/Utils/H5Utils.swift @@ -9,5 +9,6 @@ import Foundation enum H5Utils:String { case privacy = "modules/rule/privacy-wap.html" + case user = "modules/rule/protocol.html" case logoff = "modules/logout/index.html" } diff --git a/yinmeng-ios/Base/Utils/Utils.swift b/yinmeng-ios/Base/Utils/Utils.swift index 9856d44..d3cb729 100644 --- a/yinmeng-ios/Base/Utils/Utils.swift +++ b/yinmeng-ios/Base/Utils/Utils.swift @@ -31,6 +31,4 @@ let TabHeight = (49) let DesKey = "1ea53d260ecf11e7b56e00163e046a26" -let API_URL = "http://beta.api.ymlive.fun" - let H5_URL = "http://beta.h5.ymlive.fun" diff --git a/yinmeng-ios/Extension/UIView/UIView+.swift b/yinmeng-ios/Extension/UIView/UIView+.swift new file mode 100644 index 0000000..44e3265 --- /dev/null +++ b/yinmeng-ios/Extension/UIView/UIView+.swift @@ -0,0 +1,161 @@ +// +// UIView+.swift +// yinmeng-ios +// +// Created by MaiMang on 2024/2/29. +// + +import UIKit + +extension UIView { + /// x + public var x : CGFloat { + + get { + return self.frame.origin.x + } + set (x) { + var frame = self.frame + frame.origin.x = x + self.frame = frame + } + } + + + /// y + public var y : CGFloat { + + get { + return self.frame.origin.y + } + set (y) { + var frame = self.frame + frame.origin.y = y + self.frame = frame + } + } + + + /// maxX + public var maxX : CGFloat { + + get { + return self.frame.maxX + } + set(maxX) { + self.frame.origin.x = maxX - self.frame.size.width + } + } + + /// maxY + public var maxY : CGFloat { + + get { + return self.frame.maxY + } + set(maxY) { + self.frame.origin.y = maxY - self.frame.size.height + } + } + + + /// width + public var width : CGFloat { + + get { + return self.frame.size.width + } + set (width) { + var frame = self.frame + frame.size.width = width + self.frame = frame + } + } + + + /// height + public var height : CGFloat { + + get { + return self.frame.size.height + } + set (height) { + var frame = self.frame + frame.size.height = height + self.frame = frame + } + } + + + /// centerX + public var centerX : CGFloat { + + get { + return self.center.x + } + set (centerX) { + var center = self.center + center.x = centerX + self.center = center + } + } + + + /// centerY + public var centerY : CGFloat { + + get { + return self.center.y + } + set (centerY) { + var center = self.center + center.y = centerY + self.center = center + } + } + + + /// size + public var size : CGSize { + + get { + return self.frame.size + } + set (size) { + var newSize = self.frame.size + newSize = CGSize(width: size.width, height: size.height) + self.frame.size = newSize + } + } + + + /// origin + public var origin : CGPoint { + + get { + return self.frame.origin + } + set (origin) { + var newOrigin = self.frame.origin + newOrigin = CGPoint(x: origin.x, y: origin.y) + self.frame.origin = newOrigin + } + } + + /// borderWidth + public var borderWidth: CGFloat { + + get { + return self.layer.borderWidth + } + set (borderWidth){ + self.layer.borderWidth = borderWidth + + guard self.layer.masksToBounds else { + return + } + self.layer.masksToBounds = true + } + } +} + diff --git a/yinmeng-ios/Modules/Auth/VC/AuthFillDataVC.swift b/yinmeng-ios/Modules/Auth/VC/AuthFillDataVC.swift index 8119da0..e2bd8be 100644 --- a/yinmeng-ios/Modules/Auth/VC/AuthFillDataVC.swift +++ b/yinmeng-ios/Modules/Auth/VC/AuthFillDataVC.swift @@ -16,6 +16,7 @@ class AuthFillDataVC: BaseViewController, HiddenNavigationBarProtocol { } private func loadSubViews() { + randomNick() view.addSubview(backImgView) view.addSubview(backBtn) view.addSubview(titleLb) @@ -144,6 +145,16 @@ class AuthFillDataVC: BaseViewController, HiddenNavigationBarProtocol { return button }() + fileprivate func randomNick() { + RequestGet(path: "random/nick/get", parma: [:]) { text in + if let text = text as? String { + self.nickTextFiled.text = text + } + } fail: { code, msg in + + } + } + @objc func femaleBtnAction() { self.femaleBtn.isSelected = true self.maleBtn.isSelected = false @@ -157,14 +168,36 @@ class AuthFillDataVC: BaseViewController, HiddenNavigationBarProtocol { @objc func backBtnAction() { - self.navigationController?.popViewController(animated: true) + self.dismiss(animated: true, completion: nil) + AuthViewModel.authVM.logout() } @objc func nickTextFiledDidChange(_ textField: UITextField) { - + if let text = textField.text { + if text.count > 15 { + textField.text = text.substring(start: 0, 15) + } + } } + @objc func confirmBtnAction() { + if maleBtn.isSelected == true || femaleBtn.isSelected == true { + if let nick = nickTextFiled.text, nick.count > 0 { + let gender = self.maleBtn.isSelected ? "1" : "2" + let params:[String: Any] = ["avatar":"https://image.ymlive.fun/default_avatar.png", "nick": nick, "gender":gender, "uid": AuthManager.userUid, "ticket":AuthManager.ticket] + RequestPost(path: "user/v2/update", parma: params) { data in + HUDTool.show(with: "更新成功") + self.dismiss(animated: true, completion: nil) + } fail: { code, message in + HUDTool.show(with: message) + } + } else { + HUDTool.show(with: "请输入昵称") + } + } else { + HUDTool.show(with: "请选择性别") + } } } diff --git a/yinmeng-ios/Modules/Auth/VC/AuthLaunchVC.swift b/yinmeng-ios/Modules/Auth/VC/AuthLaunchVC.swift index aea8662..27a2318 100644 --- a/yinmeng-ios/Modules/Auth/VC/AuthLaunchVC.swift +++ b/yinmeng-ios/Modules/Auth/VC/AuthLaunchVC.swift @@ -281,7 +281,7 @@ class AuthLaunchVC: BaseViewController, HiddenNavigationBarProtocol { } @objc func userProtocolBtnAction() { - let web = WebViewController(url: "yinmeng/\(H5Utils.privacy.rawValue)") + let web = WebViewController(url: "yinmeng/\(H5Utils.user.rawValue)") self.navigationController?.pushViewController(web, animated: true) } diff --git a/yinmeng-ios/Modules/Auth/VM/AuthManager.swift b/yinmeng-ios/Modules/Auth/VM/AuthManager.swift index ff1c132..116b984 100644 --- a/yinmeng-ios/Modules/Auth/VM/AuthManager.swift +++ b/yinmeng-ios/Modules/Auth/VM/AuthManager.swift @@ -22,6 +22,7 @@ class AuthManager: NSObject { } } + class LoginTokenConfig: NSObject { public static let config = LoginTokenConfig.init() var tokenInfo: UserTokenObject? diff --git a/yinmeng-ios/Modules/Auth/VM/AuthViewModel.swift b/yinmeng-ios/Modules/Auth/VM/AuthViewModel.swift index e355345..4044217 100644 --- a/yinmeng-ios/Modules/Auth/VM/AuthViewModel.swift +++ b/yinmeng-ios/Modules/Auth/VM/AuthViewModel.swift @@ -7,6 +7,7 @@ import Foundation import RxSwift +import NIMSDK class AuthViewModel: NSObject { static let authVM = AuthViewModel.init() let data = PublishSubject() @@ -22,6 +23,24 @@ class AuthViewModel: NSObject { } } + func logout() { + if NIMSDK.shared().loginManager.isLogined() { + NIMSDK.shared().loginManager.logout() + } + let token = LoginTokenConfig.config.getAccountInfo()?.access_token + LoginTokenConfig.config.removeTicketFromLoaction() + LoginTokenConfig.config.removeTicketFromLoaction() + if let token = token { + let params = ["auaccess_token": token] + RequestPost(path: "acc/logout", parma: params) { data in + print("你好") + } fail: { code, msg in + print("de") + } + } + self.loginSuccess.onNext(false) + } + ///获取验证码 func getSmsCode(phone:String, type:Int) { if let phoneDes = phone.encrypt() { @@ -106,14 +125,11 @@ class AuthViewModel: NSObject { if let dic = data as? [String: Any], let tickets = dic["tickets"] as? [[String: Any]], let ticket1 = tickets[safe: 0], let ticket = ticket1["ticket"] as? String{ LoginTokenConfig.config.saveTicketToLoaction(ticket: ticket) self.loginSuccess.onNext(true) - self.loginSuccess.onCompleted() } else { self.loginSuccess.onNext(false) - self.loginSuccess.onCompleted() } } fail: { code, message in self.loginSuccess.onNext(false) - self.loginSuccess.onCompleted() } } diff --git a/yinmeng-ios/Modules/Chat/ChatListVC.swift b/yinmeng-ios/Modules/Chat/ChatListVC.swift index 52465e9..fa74a4d 100644 --- a/yinmeng-ios/Modules/Chat/ChatListVC.swift +++ b/yinmeng-ios/Modules/Chat/ChatListVC.swift @@ -6,13 +6,136 @@ // 回话列表 import UIKit - +import NIMSDK class ChatListVC: BaseViewController { + var list:NSMutableArray? + + deinit { + NIMSDK.shared().loginManager.remove(self) + NIMSDK.shared().conversationManager.remove(self) + } + override func viewDidLoad() { super.viewDidLoad() - - // Do any additional setup after loading the view. + NIMSDK.shared().conversationManager.add(self) + NIMSDK.shared().loginManager.add(self) + getList() } + private lazy var tableView: UITableView = { + let tableView = UITableView(frame: .zero, style: .plain) + tableView.delegate = self + tableView.dataSource = self + tableView.tableFooterView = UIView() + tableView.separatorStyle = .none + tableView.backgroundColor = .clear + if #available(iOS 11.0, *) { + tableView.contentInsetAdjustmentBehavior = .never + } + tableView.register(cellType: ChatListCell.self) + return tableView + }() + +} + +extension ChatListVC: UITableViewDelegate, UITableViewDataSource { + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(for: indexPath, cellType: ChatListCell.self) + return cell + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return list?.count ?? 0 + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 64 + } +} + +extension ChatListVC: NIMConversationManagerDelegate, NIMLoginManagerDelegate { + func onLogin(_ step: NIMLoginStep) { + if (step == .syncOK) { + getList() + } + } + + func didLoadAllRecentSessionCompletion() { + getList() + } + + func didAdd(_ recentSession: NIMRecentSession, totalUnreadCount: Int) { + var isInList = false + for index in 0..<(list?.count ?? 0) { + let recent = list?[index] as? NIMRecentSession + if recent?.session?.sessionId == recentSession.session?.sessionId{ + isInList = true + list?.replaceObject(at: index, with: recentSession) + break + } + } + if isInList == false { + list?.add(recentSession) + } + mai_sortMessages() + updateItemBadge() + } + + func didUpdate(_ recentSession: NIMRecentSession, totalUnreadCount: Int) { + var isInList = false + for index in 0..<(list?.count ?? 0) { + let session = list?[index] as? NIMRecentSession + if session?.session?.sessionId == recentSession.session?.sessionId{ + isInList = true + list?.replaceObject(at: index, with: recentSession) + break + } + } + if isInList == false { + list?.add(recentSession) + } + mai_sortMessages() + updateItemBadge() + } + + func didRemove(_ recentSession: NIMRecentSession, totalUnreadCount: Int) { + let arr = NSArray.init(object: recentSession.session as Any) + NIMSDK.shared().conversationManager.deleteRemoteSessions(arr as! [NIMSession], completion: nil) + tableView.reloadData() + } +} + +extension ChatListVC { + + fileprivate func getList() { + if let array = NIMSDK.shared().conversationManager.allRecentSessions() { + list = NSMutableArray.init(array: array) + mai_sortMessages() + } + } + + fileprivate func mai_sortMessages() -> Void { + var arr = list?.sortedArray(options: NSSortOptions.stable, usingComparator: { obj1, obj2 in + let item1 = obj1 as? NIMRecentSession + let item2 = obj2 as? NIMRecentSession + let time1 = (item1?.lastMessage?.timestamp ?? 0) as Double + let time2 = (item2?.lastMessage?.timestamp ?? 0) as Double + if time1 < time2{ + return .orderedDescending + } + return .orderedAscending + }) + + if let array = arr { + list = NSMutableArray(array: array) + } + tableView.reloadData() + } + + + private func updateItemBadge() { + let unreadCount = NIMSDK.shared().conversationManager.allUnreadCount() + self.tabBarItem.badgeValue = "\(unreadCount)" + } } diff --git a/yinmeng-ios/Modules/Chat/ChatVC.swift b/yinmeng-ios/Modules/Chat/ChatVC.swift index d555814..0014be9 100644 --- a/yinmeng-ios/Modules/Chat/ChatVC.swift +++ b/yinmeng-ios/Modules/Chat/ChatVC.swift @@ -9,25 +9,39 @@ import UIKit import NIMSDK class ChatVC: BaseViewController { + // 键盘收起/弹出 滚动的时候收起输入栏 + private var isBecome: Bool = false + /// 是否发送完成 + private var isSended: Bool = true + /// 输入栏默认的高度 + private let ToolBarLastH: CGFloat = 52 + public init(session: NIMSession) { vm = ChatViewModel(session: session) super.init(nibName: nil, bundle: nil) // vm.delegate = self // NIMSDK.shared().mediaManager.add(self) } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + var vm:ChatViewModel - override func viewDidLoad() { - super.viewDidLoad() - - view.backgroundColor = .orange - } + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = ThemeColor(hexStr: "#F6F6F6") + let height = ScreenHeight - (ToolBarLastH + SafeAraeBottomHeight + NavHeight) + chatTableView.frame = CGRect(x: 0, y: 0, width: ScreenWidth, height: height) + view.addSubview(chatTableView) + view.addSubview(keyboardView) + } + ///注册cell + private func registerChatCell() { + chatTableView.register(cellType: ChatTextCell.self) + } private lazy var chatTableView: UITableView = { let tableView = UITableView(frame: .zero, style: .plain) @@ -42,20 +56,57 @@ class ChatVC: BaseViewController { return tableView }() + private lazy var keyboardView: ChatKeyboardView = { + let toolBarY = ScreenHeight - NavHeight - ToolBarLastH - SafeAraeBottomHeight + let view = ChatKeyboardView(frame: CGRect(x: 0, y: toolBarY, width: ScreenWidth, height: ToolBarLastH)) + view.delegate = self + return view + }() + } -extension ChatVC: UITableViewDelegate, UITableViewDataSource { +// MARK: - ChatKeyboardViewDelegate +extension ChatVC: ChatKeyboardViewDelegate { + + func keyboard(_ keyboard: ChatKeyboardView, DidFinish content: String) { + ///发送消息 + vm.sendTextMessage(text: content) { error in + + } + } + + func keyboard(_ keyboard: ChatKeyboardView, DidBecome isBecome: Bool) { + self.isSended = true + self.isBecome = isBecome + } + + func keyboard(_ keyboard: ChatKeyboardView, DidMoreMenu type: ChatMoreMenuType) { + + //TODO: 点击更多 + } + + func keyboard(_ keyboard: ChatKeyboardView, DidObserver offsetY: CGFloat) { + + } +} + +extension ChatVC: UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate { public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return vm.messageObjects.count } public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let model = vm.messageObjects[safe: indexPath.row] + if model?.type == .text { + let cell = tableView.dequeueReusableCell(for: indexPath, cellType: ChatTextCell.self) + cell.model = model + } return UITableViewCell() } public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - + tableView.deselectRow(at: indexPath, animated: true) } public func tableView(_ tableView: UITableView, @@ -63,4 +114,22 @@ extension ChatVC: UITableViewDelegate, UITableViewDataSource { let m = vm.messageObjects[safe:indexPath.row] return CGFloat(m?.height ?? 0) } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + if isSended { + isSended = false + } + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + if isSended { + return + } + + if chatTableView.y <= 0 && isBecome { + DispatchQueue.main.async { + NotificationCenter.default.post(name: .kChatTextKeyboardNeedHide, object: nil) + } + } + } } diff --git a/yinmeng-ios/Modules/Chat/Keyboard/ChatGrowingTextView.swift b/yinmeng-ios/Modules/Chat/Keyboard/ChatGrowingTextView.swift new file mode 100644 index 0000000..6992cd5 --- /dev/null +++ b/yinmeng-ios/Modules/Chat/Keyboard/ChatGrowingTextView.swift @@ -0,0 +1,153 @@ +// +// ChatGrowingTextView.swift +// yinmeng-ios +// +// Created by MaiMang on 2024/2/29. +// + +import UIKit + +class ChatGrowingTextView: UITextView { + /// 行数部分间距调整 + fileprivate let kEdgeInset: CGFloat = 12 + /// 内容字体默认大小 + fileprivate let kDefultSize: CGFloat = 15.0 + + // MARK: - var lazy + + /// 默认3行的高度 + fileprivate var maxTextViewHeight: CGFloat = 80 + + var placeholder: String? = "" { + didSet { + self.placeholderLabel.text = placeholder + } + } + + var placeholderColor: UIColor? = .black { + didSet { + self.placeholderLabel.textColor = placeholderColor + } + } + + var maxNumberOfLines: Int = 3 { + didSet { + let numberOfLines = CGFloat(maxNumberOfLines) + maxTextViewHeight = CGFloat(ceilf(Float(self.font!.lineHeight * numberOfLines + self.textContainerInset.top + self.textContainerInset.bottom))) - kEdgeInset + } + } + + + + /// 输入框高度监听回调 + var didTextChangedHeightClosure : ((CGFloat)->Void)? + + /// 占位标签 + fileprivate lazy var placeholderLabel: UILabel = { + let frame = CGRect(x: 5, y: 7, width: ScreenWidth - 10, height: 21) + let label = UILabel(frame: frame) + label.numberOfLines = 1 + label.text = "请输入你要发送的消息" + label.textColor = ThemeColor(hexStr: "#F6F6F6") + label.font = UIFont.systemFont(ofSize: self.kDefultSize) + return label + }() + + // MARK: - life cycle + + override init(frame: CGRect, textContainer: NSTextContainer?) { + super.init(frame: frame, textContainer: textContainer) + + setup() + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + fileprivate func setup() { + backgroundColor = .white + + self.isScrollEnabled = false + self.scrollsToTop = false + //self.contentInset = UIEdgeInsets(top: 1, left: 0, bottom: 1, right: 0) + self.showsHorizontalScrollIndicator = false + self.enablesReturnKeyAutomatically = true + self.font = UIFont.systemFont(ofSize: kDefultSize) + self.returnKeyType = .send + + self.layer.cornerRadius = 4 + self.layer.borderWidth = 1 + self.layer.borderColor = ThemeColor(hexStr: "#2C363E").cgColor + self.layer.masksToBounds = true + // 添加占位控件 + addSubview(self.placeholderLabel) + + // register + registerChangeNotification() + } + + fileprivate func registerChangeNotification() { + // 实时监听输入值的改变 + NotificationCenter.default.addObserver(self, + selector: #selector(textDidChanged), + name: UITextView.textDidChangeNotification, + object: self) + + self.addObserver(self, forKeyPath: "attributedText", options: .new, context: nil) + } + + // MARK: - KVO监听 + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "attributedText" { + textDidChanged() + }else { + + super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) + } + } + + + deinit { + NotificationCenter.default.removeObserver(self) + self.removeObserver(self, forKeyPath: "attributedText") + } + +} + +// MARK: - Action + +extension ChatGrowingTextView { + + @objc func textDidChanged() { + placeholderLabel.isHidden = self.hasText + + // 计算高度 + let constrainSize = CGSize(width: self.frame.size.width, height: CGFloat(MAXFLOAT)) + var size = self.sizeThatFits(constrainSize) + + if size.height >= maxTextViewHeight { + self.isScrollEnabled = true + size.height = maxTextViewHeight + }else { + self.isScrollEnabled = false + if (didTextChangedHeightClosure != nil && !self.isScrollEnabled) { + didTextChangedHeightClosure?(size.height) + } + } + + let nowFrame = self.frame + frame.size.height = size.height + self.frame = nowFrame + + self.layoutIfNeeded() + + sendChangedNoti() + } + + func sendChangedNoti() { + + NotificationCenter.default.post(name: .kChatTextKeyboardChanged, object: self.text, userInfo: nil) + } +} diff --git a/yinmeng-ios/Modules/Chat/Keyboard/ChatKeyboard+.swift b/yinmeng-ios/Modules/Chat/Keyboard/ChatKeyboard+.swift new file mode 100644 index 0000000..c79639d --- /dev/null +++ b/yinmeng-ios/Modules/Chat/Keyboard/ChatKeyboard+.swift @@ -0,0 +1,29 @@ +// +// ChatKeyboard+.swift +// yinmeng-ios +// +// Created by MaiMang on 2024/2/29. +// + +import Foundation +// MARK: - NSNotificationName + +public extension NSNotification.Name { + /// 获取点击空白处回收键盘的处理通知 + static let kChatTextKeyboardNeedHide = Notification.Name("kChatTextKeyboardNeedHide") + /// 获取文本输入框值变化 + static let kChatTextKeyboardChanged = Notification.Name("kChatTextKeyboardChanged") +} + +protocol OptionalType { + associatedtype Wrapped + + var value: Wrapped? { get } +} + + +extension Optional: OptionalType { + var value: Wrapped? { + return self + } +} diff --git a/yinmeng-ios/Modules/Chat/Keyboard/ChatKeyboardView.swift b/yinmeng-ios/Modules/Chat/Keyboard/ChatKeyboardView.swift new file mode 100644 index 0000000..4893f78 --- /dev/null +++ b/yinmeng-ios/Modules/Chat/Keyboard/ChatKeyboardView.swift @@ -0,0 +1,371 @@ +// +// ChatKeyboardView.swift +// yinmeng-ios +// +// Created by MaiMang on 2024/2/29. +// + +import UIKit + +protocol ChatKeyboardViewDelegate: NSObjectProtocol { + /// 输入完消息 + func keyboard(_ keyboard: ChatKeyboardView, DidFinish content: String) + /// 键盘收起/弹出 + func keyboard(_ keyboard: ChatKeyboardView, DidBecome isBecome: Bool) + /// 键盘的y值 + func keyboard(_ keyboard: ChatKeyboardView, DidObserver offsetY: CGFloat) + /// 菜单栏点击 + func keyboard(_ keyboard: ChatKeyboardView, DidMoreMenu type: ChatMoreMenuType) +} + +extension ChatKeyboardViewDelegate { + func keyboard(_ keyboard: ChatKeyboardView, DidFinish content: String) {} + func keyboard(_ keyboard: ChatKeyboardView, DidBecome isBecome: Bool) {} + func keyboard(_ keyboard: ChatKeyboardView, DidObserver offsetY: CGFloat) {} + func keyboard(_ keyboard: ChatKeyboardView, DidMoreMenu type: ChatMoreMenuType) {} +} + + +class ChatKeyboardView: UIView { + + private let kSpace: CGFloat = 8.0 + private let kViewWH: CGFloat = 36.0 + private let kLineHeight: CGFloat = 0.75 + + // MARK: - var lazy + + weak var delegate: ChatKeyboardViewDelegate? + + fileprivate var toolBarHeight: CGFloat = 52.0 + fileprivate var lastTextHeight: CGFloat = 34.0 + fileprivate var keyboardHeight: CGFloat = 0.0 + + /// 底部菜单容器高度 + fileprivate var contentHeight: CGFloat = 0.0 + fileprivate var isShowMore = false + + /// 是否弹出了系统键盘 + fileprivate var isShowKeyboard = false + + /// 更多按钮 + 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.setImage(UIImage(named: "chat_more"), for: .normal) + button.setImage(UIImage(named: "chat_more"), for: .highlighted) + button.addTarget(self, action: #selector(moreDidAction(_:)), for: .touchUpInside) + return button + }() + + /// 文本输入框 + fileprivate lazy var chatTextView: ChatGrowingTextView = { + let w: CGFloat = ScreenWidth - self.kViewWH * 2 - self.kSpace * 3 - self.kSpace + let textView = ChatGrowingTextView(frame: CGRect(x: self.kSpace, y: self.kSpace, width: w, height: self.kViewWH)) + textView.placeholder = "请输入..." + textView.textColor = ThemeColor(hexStr: "#000000") + textView.maxNumberOfLines = 5 + textView.delegate = self + textView.didTextChangedHeightClosure = { [weak self] height in + self?.changeKeyboardHeight(height: height) + } + return textView + }() + + fileprivate lazy var topLineView: UIView = { + let lineView1 = UIView(frame: CGRect(x: 0, y: 0, width: ScreenWidth, height: kLineHeight)) + lineView1.backgroundColor = ThemeColor(hexStr: "#E5E5E5") + return lineView1 + }() + + fileprivate lazy var bottomLineView: UIView = { + let lineView2 = UIView(frame: CGRect(x: 0, y: self.toolBarHeight - kLineHeight, width: ScreenWidth, height: kLineHeight)) + lineView2.backgroundColor = ThemeColor(hexStr: "#E5E5E5") + return lineView2 + }() + + fileprivate lazy var toolBarView: UIView = { + let view = UIView(frame: CGRect(x: 0, y: 0, width: ScreenWidth, height: self.toolBarHeight)) + view.backgroundColor = ThemeColor(hexStr: "#F7F7F7") + return view + }() + + /// 底部背景容器 + fileprivate lazy var contentView: UIView = { + let y = self.toolBarView.maxY + let view = UIView(frame: CGRect(x: 0, y: y, width: ScreenWidth, height: self.contentHeight)) + view.backgroundColor = ThemeColor(hexStr: "#F8F8F8") + return view + }() + + /// 更多菜单 + fileprivate lazy var moreMenuView: ChatMoreMenuView = { + let view = ChatMoreMenuView(frame: self.contentView.bounds) + view.isHidden = true + view.delegate = self + return view + }() + + // MARK: - life cycle + + override init(frame: CGRect) { + super.init(frame: frame) + + setupKeyboardView() + registerNotification() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + + setupKeyboardView() + registerNotification() + } + + func setupKeyboardView() { + self.backgroundColor = ThemeColor(hexStr: "#F7F7F7") + self.isUserInteractionEnabled = true + + addSubview(toolBarView) + toolBarView.addSubview(moreButton) + toolBarView.addSubview(chatTextView) + toolBarView.addSubview(topLineView) + toolBarView.addSubview(bottomLineView) + + addSubview(contentView) + contentView.addSubview(moreMenuView) + } + + // MARK: - 监听键盘通知 + private func registerNotification() { + // 监听键盘弹出通知 + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), + name:UIResponder.keyboardWillShowNotification,object: nil) + // 监听键盘隐藏通知 + NotificationCenter.default.addObserver(self,selector: #selector(keyboardWillHide(_:)), + name: UIResponder.keyboardWillHideNotification, object: nil) + + // 主要是为了获取点击空白处回收键盘的处理 + NotificationCenter.default.addObserver(self,selector: #selector(keyboardNeedHide), + name: .kChatTextKeyboardNeedHide, object: nil) + // 添加KVO监听输入键盘y值 + addObserver(self, forKeyPath: "frame", options: [.new, .old], context: nil) + } + + // MARK: - KVO监听 + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "frame" && ((object as? UIView) != nil) { + if let changeObject = change.value { + if let newFrame = changeObject[.newKey] as? CGRect { + delegate?.keyboard(self, DidObserver: newFrame.origin.y) + print("y值发生改变\(newFrame.origin.y)") + } + } + }else { + + super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) + } + } + + deinit { + + self.removeObserver(self, forKeyPath: "frame") + } +} + + +// MARK: - ChatMoreMenuViewDelegate + +extension ChatKeyboardView: ChatMoreMenuViewDelegate { + + func menu(_ view: ChatMoreMenuView, DidSelected type: ChatMoreMenuType) { + + delegate?.keyboard(self, DidMoreMenu: type) + } +} + +// MARK: - UITextViewDelegate + +extension ChatKeyboardView: UITextViewDelegate { + + func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + // 发送键&回车键处理 + if (text == "\n") { + if (isShowKeyboard) { + isShowKeyboard = true + } + + sendChatMessage() + return false + } + + return true + } + + + /// 发送消息内容 + private func sendChatMessage() { + delegate?.keyboard(self, DidFinish: self.chatTextView.text ?? "") + + changeKeyboardHeight(height: lastTextHeight) + chatTextView.text = "" + chatTextView.attributedText = NSAttributedString(string: "") + } +} + + +// MARK: - KeyBoard Manager + +extension ChatKeyboardView { + + /// 键盘将要显示 + @objc func keyboardWillShow(_ noti: NSNotification) { + guard let userInfo = noti.userInfo else { + return + } + + contentHeight = 0 + delegate?.keyboard(self, DidBecome: true) + + let duration = userInfo["UIKeyboardAnimationDurationUserInfoKey"] as! Double + let endFrame = (userInfo["UIKeyboardFrameEndUserInfoKey"] as! NSValue).cgRectValue + let y = endFrame.origin.y + // 获取键盘的高度 + keyboardHeight = endFrame.height + // 键盘弹出状态 + isShowKeyboard = true + + let option = userInfo["UIKeyboardAnimationCurveUserInfoKey"] as! Int + + var changedY = y - self.toolBarHeight - NavHeight - contentHeight + if (isShowMore) { //显示系统键盘 + isShowMore = false + self.moreMenuView.isHidden = true + self.moreMenuView.hidePageController = true + changedY = y - self.toolBarHeight - NavHeight + } + + 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) + } + + /// 键盘将要消失 + @objc func keyboardWillHide(_ noti: NSNotification) { + guard let userInfo = noti.userInfo else { + return + } + + delegate?.keyboard(self, DidBecome: false) + + let duration = userInfo["UIKeyboardAnimationDurationUserInfoKey"] as! Double + let endFrame = (userInfo["UIKeyboardFrameEndUserInfoKey"] as! NSValue).cgRectValue + //let y = endFrame.origin.y + // 获取键盘的高度 + keyboardHeight = endFrame.height + // 键盘弹出状态 + isShowKeyboard = false + + let option = userInfo["UIKeyboardAnimationCurveUserInfoKey"] as! Int + let changedY = ScreenHeight - NavHeight - self.toolBarHeight - SafeAraeBottomHeight - 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) + } + + @objc func keyboardNeedHide(_ noti: NSNotification) { + + chatTextView.resignFirstResponder() + moreMenuView.hidePageController = true + + contentHeight = 0.0 + restToolbarContentHeight(true) + + delegate?.keyboard(self, DidBecome: false) + } +} + + + +// MARK: - Action + +extension ChatKeyboardView { + /// 更多按钮点击处理 + @objc func moreDidAction(_ button: UIButton) { + // 如有弹出菜单 + if isShowMore { + return + } + + isShowMore = true + contentHeight = 250 + contentView.isHidden = false + moreMenuView.isHidden = false + chatTextView.resignFirstResponder() + + restToolbarContentHeight() + moreMenuView.reloadData() + + delegate?.keyboard(self, DidBecome: true) + } + + /// 更改容器高度 + func restToolbarContentHeight(_ isRest: Bool = false) { + var changedY = ScreenHeight - self.toolBarHeight - NavHeight - contentHeight + if (isRest) { + if (isShowMore) { + isShowMore = false + } + + changedY = ScreenHeight - self.toolBarHeight - NavHeight - contentHeight - SafeAraeBottomHeight + } + + UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut, animations: { + self.contentView.frame = CGRect(x: 0, y: self.toolBarView.maxY, width: ScreenWidth, height: self.contentHeight) + self.moreMenuView.frame = self.contentView.bounds + self.frame = CGRect(x: 0, y: changedY, width: ScreenWidth, height: self.toolBarHeight + self.contentHeight) + }, completion: nil) + + self.layoutIfNeeded() + } + +} + +// MARK: - 改变输入框高度位置 + +extension ChatKeyboardView { + + func changeKeyboardHeight(_ isClear: Bool = false, height: CGFloat) { + let textHeight = height + + toolBarHeight = textHeight + kSpace * 2 + toolBarView.frame = CGRect(x: toolBarView.x, y: 0, width: toolBarView.width, height: toolBarHeight) + + let spaceY = toolBarView.height - kSpace - kViewWH + chatTextView.frame = CGRect(x: chatTextView.x, y: chatTextView.x, width: chatTextView.width, height: textHeight) + moreButton.frame = CGRect(x: moreButton.x, y: spaceY, width: moreButton.width, height: moreButton.height) + contentView.frame = CGRect(x: contentView.x, y: toolBarView.maxY, width: contentView.width, height: contentHeight) + + topLineView.frame = CGRect(x: 0, y: 0, width: ScreenWidth, height: kLineHeight) + bottomLineView.frame = CGRect(x: 0, y: toolBarView.height - kLineHeight, width: ScreenWidth, height: kLineHeight) + + + if (isShowKeyboard) { + if (isShowMore) { + isShowMore = false + } + + let changedY = ScreenHeight - keyboardHeight - toolBarHeight - NavHeight + self.frame = CGRect(x: 0, y: changedY, width: ScreenWidth, height: toolBarView.height + contentView.height) + }else { + let changedY = ScreenHeight - NavHeight - (toolBarView.height + contentView.height) + self.frame = CGRect(x: 0, y: changedY, width: ScreenWidth, height: toolBarView.height + contentView.height) + } + + self.setNeedsLayout() + print("self y === \(self.frame.origin.y)") + } +} + + diff --git a/yinmeng-ios/Modules/Chat/Keyboard/ChatMoreMenuCell.swift b/yinmeng-ios/Modules/Chat/Keyboard/ChatMoreMenuCell.swift new file mode 100644 index 0000000..74a317a --- /dev/null +++ b/yinmeng-ios/Modules/Chat/Keyboard/ChatMoreMenuCell.swift @@ -0,0 +1,67 @@ +// +// ChatMoreMenuCell.swift +// yinmeng-ios +// +// Created by MaiMang on 2024/2/29. +// + +import UIKit +import Reusable +class ChatMoreMenuCell: UICollectionViewCell, Reusable { + + // MARK: - lazy var + + var model: ChatMoreMnueConfig? { + didSet { + guard model != nil else { + return + } + + self.imageView.image = UIImage(named: model!.image!) + self.titleLabel.text = model?.title ?? "" + } + } + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.textAlignment = .center + label.textColor = .white + label.font = UIFont.systemFont(ofSize: 14) + return label + }() + + private lazy var imageView: UIImageView = { + let imageView = UIImageView() + return imageView + }() + + // MARK: - life cycle + + override init(frame: CGRect) { + super.init(frame: frame) + + contentView.addSubview(imageView) + contentView.addSubview(titleLabel) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + override func layoutSubviews() { + super.layoutSubviews() + + imageView.snp.makeConstraints { (make) in + make.centerX.equalToSuperview() + make.centerY.equalToSuperview().offset(-10) + make.width.height.equalTo(36) + } + + titleLabel.snp.makeConstraints { (make) in + make.left.right.equalToSuperview() + make.top.equalTo(imageView.snp.bottom) + make.height.equalTo(21) + } + } +} diff --git a/yinmeng-ios/Modules/Chat/Keyboard/ChatMoreMenuView.swift b/yinmeng-ios/Modules/Chat/Keyboard/ChatMoreMenuView.swift new file mode 100644 index 0000000..7738c3c --- /dev/null +++ b/yinmeng-ios/Modules/Chat/Keyboard/ChatMoreMenuView.swift @@ -0,0 +1,262 @@ +// +// ChatMoreMenuView.swift +// yinmeng-ios +// +// Created by MaiMang on 2024/2/29. +// + +import UIKit + +enum ChatMoreMenuType: Int { + case album = 1001 + case camera = 1002 +} + +/// 行数 +fileprivate let kRowNumber = 2 +/// 列数 +fileprivate let kColumnNumber = 4 + +fileprivate let kMoreMenuCellNumberOfOnePage = kRowNumber * kColumnNumber + +protocol ChatMoreMenuViewDelegate { + /// 获取选择的菜单 + func menu(_ view: ChatMoreMenuView, DidSelected type: ChatMoreMenuType) +} + +extension ChatMoreMenuViewDelegate { + + func menu(_ view: ChatMoreMenuView, DidSelected type: ChatMoreMenuType) {} +} + +class ChatMoreMenuView: UIView { + + // MARK: - lazy var + + var delegate: ChatMoreMenuViewDelegate? + + /// 隐藏分页指示器 + var hidePageController: Bool = false { + didSet { + self.pageControl.alpha = self.hidePageController ? 0.0 : 1.0 + UIView.animate(withDuration: 0.15, delay: 0, options: .curveEaseOut, animations: { + self.pageControl.isHidden = self.hidePageController + }, completion: nil) + + self.collectionView.scrollToItem(at: IndexPath(row: 0, section: 0), at: .centeredHorizontally, animated: false) + } + } + + var pageIndicatorTintColor: UIColor? { + didSet { + guard pageIndicatorTintColor != nil else { + return + } + pageControl.pageIndicatorTintColor = pageIndicatorTintColor + } + } + + var currentPageIndicatorTintColor: UIColor? { + didSet { + guard currentPageIndicatorTintColor != nil else { + return + } + pageControl.currentPageIndicatorTintColor = currentPageIndicatorTintColor + } + } + + lazy var dataSource: [ChatMoreMnueConfig] = { + let configs = [ + ChatMoreMnueConfig(title: "图片", image: "ic_more_album", type: .album), + ChatMoreMnueConfig(title: "拍照", image: "ic_more_camera", type: .camera) + ] + return configs + }() + + lazy var collectionView: UICollectionView = { + let layout = ChatKeyboardFlowLayout(column: kColumnNumber, row: kRowNumber) + // collectionView + let collection = UICollectionView(frame: self.bounds, collectionViewLayout: layout) + collection.backgroundColor = ThemeColor(hexStr: "#F8F8F8") + collection.register(cellType: ChatMoreMenuCell.self) + collection.showsHorizontalScrollIndicator = true + collection.showsVerticalScrollIndicator = true + collection.dataSource = self + collection.delegate = self + collection.isPagingEnabled = true + return collection + }() + + lazy var pageControl: UIPageControl = { + let pager = UIPageControl() + pager.backgroundColor = .clear + pager.pageIndicatorTintColor = .white + pager.currentPageIndicatorTintColor = ThemeColor(hexStr: "#000000") + pager.currentPage = 0 + pager.isHidden = true + pager.numberOfPages = self.dataSource.count / kMoreMenuCellNumberOfOnePage + (self.dataSource.count % kMoreMenuCellNumberOfOnePage == 0 ? 0 : 1) + return pager + }() + + // MARK: - life cycle + + override init(frame: CGRect) { + super.init(frame: frame) + + makeUI() + reloadData() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + + makeUI() + reloadData() + } + + func makeUI() { + self.backgroundColor = ThemeColor(hexStr: "#F8F8F8") + + addSubview(pageControl) + addSubview(collectionView) + + pageControl.snp.makeConstraints { make in + make.bottom.equalToSuperview().offset(-SafeAraeBottomHeight) + make.centerX.equalToSuperview() + make.height.equalTo(30) + } + + collectionView.snp.makeConstraints { make in + make.top.left.right.equalToSuperview() + make.bottom.equalTo(pageControl.snp.top) + } + } + + open func reloadData() { + self.needsUpdateConstraints() + self.layoutIfNeeded() + + UIView.animate(withDuration: 0.15, delay: 0, options: .curveEaseOut, animations: { + self.pageControl.alpha = 1.0 + self.pageControl.isHidden = false + }, completion: nil) + + DispatchQueue.main.async { + self.collectionView.reloadData() + } + } +} + + +// MARK: - UICollectionViewDataSource + +extension ChatMoreMenuView: UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return dataSource.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(for: indexPath, cellType: ChatMoreMenuCell.self) + if let model = dataSource[safe: indexPath.row] { + cell.model = model + } + return cell + } + +} + +// MARK: - UICollectionViewDelegate + +extension ChatMoreMenuView: UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + if let model = dataSource[safe: indexPath.row] { + delegate?.menu(self, DidSelected: model.type!) + } + } +} + +extension ChatMoreMenuView { + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + if scrollView == collectionView { + let offsetX = scrollView.contentOffset.x + let index = offsetX / ScreenWidth + pageControl.currentPage = Int(index) + } + } +} + + class ChatKeyboardFlowLayout: UICollectionViewFlowLayout { + // 保存所有item + fileprivate var attributesArr: [UICollectionViewLayoutAttributes] = [] + fileprivate var col: Int = 0 + fileprivate var row: Int = 0 + + init(column: Int, row: Int) { + super.init() + self.col = column + self.row = row + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - 重新布局 + override func prepare() { + super.prepare() + + let itemWH: CGFloat = ScreenWidth / CGFloat(col) + + // 设置itemSize + itemSize = CGSize(width: itemWH, height: itemWH) + minimumLineSpacing = 0 + minimumInteritemSpacing = 0 + scrollDirection = .horizontal + + // 设置collectionView属性 + collectionView?.isPagingEnabled = true + collectionView?.showsHorizontalScrollIndicator = false + collectionView?.showsVerticalScrollIndicator = true + let insertMargin = (collectionView!.bounds.height - CGFloat(row) * itemWH) * 0.5 + collectionView?.contentInset = UIEdgeInsets(top: insertMargin, left: 0, bottom: insertMargin, right: 0) + + var page = 0 + let itemsCount = collectionView?.numberOfItems(inSection: 0) ?? 0 + for itemIndex in 0.. [UICollectionViewLayoutAttributes]? { + var rectAttributes: [UICollectionViewLayoutAttributes] = [] + _ = attributesArr.map({ + if rect.contains($0.frame) { + rectAttributes.append($0) + } + }) + return rectAttributes + } + + override var collectionViewContentSize: CGSize { + let size: CGSize = super.collectionViewContentSize + let collectionViewWidth: CGFloat = self.collectionView!.frame.size.width + let nbOfScreen: Int = Int(ceil(size.width / collectionViewWidth)) + let newSize: CGSize = CGSize(width: collectionViewWidth * CGFloat(nbOfScreen), height: size.height) + return newSize + } + } + diff --git a/yinmeng-ios/Modules/Chat/Keyboard/ChatMoreMnueConfig.swift b/yinmeng-ios/Modules/Chat/Keyboard/ChatMoreMnueConfig.swift new file mode 100644 index 0000000..8930c7a --- /dev/null +++ b/yinmeng-ios/Modules/Chat/Keyboard/ChatMoreMnueConfig.swift @@ -0,0 +1,25 @@ +// +// ChatMoreMnueConfig.swift +// yinmeng-ios +// +// Created by MaiMang on 2024/2/29. +// + +import Foundation + +class ChatMoreMnueConfig: NSObject { + var title: String? + var image: String? + + var type: ChatMoreMenuType? + + init(title: String, image: String, type: ChatMoreMenuType) { + super.init() + + self.title = title + self.image = image + self.type = type + } + + required override init() { } +} diff --git a/yinmeng-ios/Modules/Chat/View/ChatBaseCell.swift b/yinmeng-ios/Modules/Chat/View/ChatBaseCell.swift index 5f6866d..e1d72c6 100644 --- a/yinmeng-ios/Modules/Chat/View/ChatBaseCell.swift +++ b/yinmeng-ios/Modules/Chat/View/ChatBaseCell.swift @@ -13,7 +13,7 @@ protocol ChatBaseCellProtocol: NSObjectProtocol { class ChatBaseCell: UITableViewCell, Reusable{ weak var delegate: ChatBaseCellProtocol? - var model:ChatBaseObject? { + var model:ChatSessionProtocol? { didSet { guard let _ = model else {return} layoutMessageCell() diff --git a/yinmeng-ios/Modules/Chat/View/ChatList/ChatListCell.swift b/yinmeng-ios/Modules/Chat/View/ChatList/ChatListCell.swift new file mode 100644 index 0000000..27baa50 --- /dev/null +++ b/yinmeng-ios/Modules/Chat/View/ChatList/ChatListCell.swift @@ -0,0 +1,71 @@ +// +// ChatListCell.swift +// yinmeng-ios +// +// Created by MaiMang on 2024/2/29. +// + +import UIKit +import Reusable +class ChatListCell: UITableViewCell, Reusable { + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + contentView.addSubview(backView) + backView.addSubview(avatarImgView) + backView.addSubview(textLb) + backView.addSubview(nameLb) + backView.addSubview(dateLb) + } + + private lazy var backView: UIView = { + let view = UIView() + view.backgroundColor = ThemeColor(hexStr: "#525566") + view.layer.masksToBounds = true + return view + }() + + private lazy var avatarImgView: UIImageView = { + let imageView = UIImageView() + imageView.isUserInteractionEnabled = true + imageView.layer.masksToBounds = true + imageView.contentMode = .scaleAspectFill + imageView.layer.cornerRadius = 30 + return imageView + }() + + private lazy var textLb: UILabel = { + let label = UILabel() + label.textColor = UIColor(white: 1, alpha: 0.8) + label.font = UIFont.systemFont(ofSize: 14) + return label + }() + + private lazy var nameLb: UILabel = { + let label = UILabel() + label.textColor = .white + label.font = UIFont.systemFont(ofSize: 15, weight: .medium) + return label + }() + + private lazy var dateLb: UILabel = { + let label = UILabel() + label.textColor = .red + label.font = UIFont.systemFont(ofSize: 12, weight: .light) + return label + }() + + private lazy var badgeLb: UILabel = { + let label = UILabel() + label.textColor = .white + label.backgroundColor = .red + label.textAlignment = .center + label.font = UIFont.systemFont(ofSize: 11) + return label + }() + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/yinmeng-ios/Modules/Star/PlanetStarVC.swift b/yinmeng-ios/Modules/Star/PlanetStarVC.swift index 7f4567c..44d7437 100644 --- a/yinmeng-ios/Modules/Star/PlanetStarVC.swift +++ b/yinmeng-ios/Modules/Star/PlanetStarVC.swift @@ -85,8 +85,6 @@ class PlanetStarVC: BaseViewController,HiddenNavigationBarProtocol { self?.navigationController?.pushViewController(chatVC, animated: true) } - - } var giftModel:PlanetStarModel? diff --git a/yinmeng-ios/Modules/User/VC/AboutUsVC.swift b/yinmeng-ios/Modules/User/VC/AboutUsVC.swift index bd3e17c..397feff 100644 --- a/yinmeng-ios/Modules/User/VC/AboutUsVC.swift +++ b/yinmeng-ios/Modules/User/VC/AboutUsVC.swift @@ -62,7 +62,6 @@ make.size.equalTo(CGSize(width: 14, height: 14)) } @objc func privacyRecognizer() { - //TODO: 跳转隐私政策的 let web = WebViewController(url: "yinmeng/\(H5Utils.privacy.rawValue)") self.navigationController?.pushViewController(web, animated: true) } diff --git a/yinmeng-ios/Modules/User/VC/UserInfoVC.swift b/yinmeng-ios/Modules/User/VC/UserInfoVC.swift index ef6afad..b9102a1 100644 --- a/yinmeng-ios/Modules/User/VC/UserInfoVC.swift +++ b/yinmeng-ios/Modules/User/VC/UserInfoVC.swift @@ -71,7 +71,7 @@ class UserInfoVC: BaseViewController, HiddenNavigationBarProtocol { } @objc func logoutBtnAction() { - //TODO: 退出登录 + AuthViewModel.authVM.logout() } private lazy var backImgView: UIImageView = { @@ -135,7 +135,7 @@ class UserInfoVC: BaseViewController, HiddenNavigationBarProtocol { private lazy var logoutBtn: UIButton = { let button = UIButton(type: .custom) - button.setBackgroundImage(UIImage.gradient([ThemeColor(hexStr: "#FFC926"), ThemeColor(hexStr: "#FFE784")], radius: 0), for: .normal) + button.setBackgroundImage(UIImage.gradient([ThemeColor(hexStr: "#FF60FD"), ThemeColor(hexStr: "#8974FF"), ThemeColor(hexStr: "#69EBFF")], radius: 0), for: .normal) button.setTitle("退出登录", for: .normal) button.setTitleColor(UIColor.white, for: .normal) button.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium) diff --git a/yinmeng-ios/Modules/User/VM/UserViewModel.swift b/yinmeng-ios/Modules/User/VM/UserViewModel.swift index 0f9eef4..a7a4acc 100644 --- a/yinmeng-ios/Modules/User/VM/UserViewModel.swift +++ b/yinmeng-ios/Modules/User/VM/UserViewModel.swift @@ -6,18 +6,19 @@ // import Foundation - +import RxSwift class UserViewModel: NSObject { static let userVM = UserViewModel.init() + let userInfo = PublishSubject() func getUserInfo(uid:Int) { let params = ["uid":uid] RequestGet(path: "user/get", parma: params) { data in - if let info = Deserialized.toModel(with: data) { - print("用户信息是") + if var info = Deserialized.toModel(with: data) { + self.userInfo.onNext(info) } } fail: { code, message in - + } } }