diff --git a/xplan-ios.xcodeproj/project.pbxproj b/xplan-ios.xcodeproj/project.pbxproj index 8d24704a..7bfd9835 100644 --- a/xplan-ios.xcodeproj/project.pbxproj +++ b/xplan-ios.xcodeproj/project.pbxproj @@ -102,6 +102,10 @@ 18EE3FF42750FA3700A452BF /* UIView+NIM.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EE3FF32750FA3700A452BF /* UIView+NIM.m */; }; 73FFADDC93E195344047A2EC /* Pods_xplan_ios.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CACF623970097D653132D69A /* Pods_xplan_ios.framework */; }; 9B0E1C5926E77022005D4442 /* BaseNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B0E1C5826E77022005D4442 /* BaseNavigationController.m */; }; + 9B7D804A2753783D003DAC0C /* SessionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B7D80492753783D003DAC0C /* SessionViewController.m */; }; + 9B7D804D27537950003DAC0C /* MessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B7D804C27537950003DAC0C /* MessageCell.m */; }; + 9B7D80502753AA9D003DAC0C /* UITableView+NIMScrollToBottom.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B7D804E2753AA9D003DAC0C /* UITableView+NIMScrollToBottom.m */; }; + 9B7D80562753C595003DAC0C /* SendMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B7D80552753C595003DAC0C /* SendMessageView.m */; }; E80487652717DDD9008595F2 /* XPRoomMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = E80487642717DDD9008595F2 /* XPRoomMenuItem.m */; }; E811FFF72742367B00918544 /* XPGiftEmptyCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E811FFF62742367B00918544 /* XPGiftEmptyCollectionViewCell.m */; }; E8133916273E532D00708B66 /* XPGiftItemCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E8133915273E532D00708B66 /* XPGiftItemCollectionViewCell.m */; }; @@ -458,6 +462,14 @@ 7DB00EC07F1D0ADFF900B38D /* Pods-xplan-ios.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-xplan-ios.debug.xcconfig"; path = "Target Support Files/Pods-xplan-ios/Pods-xplan-ios.debug.xcconfig"; sourceTree = ""; }; 9B0E1C5726E77022005D4442 /* BaseNavigationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BaseNavigationController.h; sourceTree = ""; }; 9B0E1C5826E77022005D4442 /* BaseNavigationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BaseNavigationController.m; sourceTree = ""; }; + 9B7D80482753783D003DAC0C /* SessionViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SessionViewController.h; sourceTree = ""; }; + 9B7D80492753783D003DAC0C /* SessionViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SessionViewController.m; sourceTree = ""; }; + 9B7D804B27537950003DAC0C /* MessageCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MessageCell.h; sourceTree = ""; }; + 9B7D804C27537950003DAC0C /* MessageCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MessageCell.m; sourceTree = ""; }; + 9B7D804E2753AA9D003DAC0C /* UITableView+NIMScrollToBottom.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableView+NIMScrollToBottom.m"; sourceTree = ""; }; + 9B7D804F2753AA9D003DAC0C /* UITableView+NIMScrollToBottom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+NIMScrollToBottom.h"; sourceTree = ""; }; + 9B7D80542753C595003DAC0C /* SendMessageView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SendMessageView.h; sourceTree = ""; }; + 9B7D80552753C595003DAC0C /* SendMessageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SendMessageView.m; sourceTree = ""; }; 9BB865B4272076140029CDE0 /* RtcImplDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RtcImplDelegate.h; sourceTree = ""; }; B66633E061B1B34177CD011C /* Pods-xplan-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-xplan-ios.release.xcconfig"; path = "Target Support Files/Pods-xplan-ios/Pods-xplan-ios.release.xcconfig"; sourceTree = ""; }; CACF623970097D653132D69A /* Pods_xplan_ios.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_xplan_ios.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1288,18 +1300,13 @@ 18A61BCC274F72ED00A09A54 /* Message */ = { isa = PBXGroup; children = ( - 18A61BE6274F9CF000A09A54 /* SessionListViewController.h */, - 18A61BE7274F9CF000A09A54 /* SessionListViewController.m */, - 18EE3FDD2750C1F700A452BF /* SessionListCell.h */, - 18EE3FDE2750C1F700A452BF /* SessionListCell.m */, - 18EE3FE02750C29D00A452BF /* NIMBadgeView.h */, - 18EE3FE12750C29D00A452BF /* NIMBadgeView.m */, + 18EE3FF72754B01D00A452BF /* NIMViews */, + 18EE3FF52754AFD900A452BF /* SessionList */, + 9B7D804727537819003DAC0C /* Session */, 18EE3FEC2750CE6D00A452BF /* NIMMessageUtils.h */, 18EE3FED2750CE6D00A452BF /* NIMMessageUtils.m */, 18EE3FEF2750D2AD00A452BF /* NIMTimeUtils.h */, 18EE3FF02750D2AD00A452BF /* NIMTimeUtils.m */, - 18EE3FF22750FA3700A452BF /* UIView+NIM.h */, - 18EE3FF32750FA3700A452BF /* UIView+NIM.m */, ); path = Message; sourceTree = ""; @@ -1349,6 +1356,43 @@ path = XplanFrameworks; sourceTree = ""; }; + 18EE3FF52754AFD900A452BF /* SessionList */ = { + isa = PBXGroup; + children = ( + 18A61BE6274F9CF000A09A54 /* SessionListViewController.h */, + 18A61BE7274F9CF000A09A54 /* SessionListViewController.m */, + 18EE3FDD2750C1F700A452BF /* SessionListCell.h */, + 18EE3FDE2750C1F700A452BF /* SessionListCell.m */, + ); + path = SessionList; + sourceTree = ""; + }; + 18EE3FF72754B01D00A452BF /* NIMViews */ = { + isa = PBXGroup; + children = ( + 18EE3FE02750C29D00A452BF /* NIMBadgeView.h */, + 18EE3FE12750C29D00A452BF /* NIMBadgeView.m */, + 9B7D804F2753AA9D003DAC0C /* UITableView+NIMScrollToBottom.h */, + 9B7D804E2753AA9D003DAC0C /* UITableView+NIMScrollToBottom.m */, + 18EE3FF22750FA3700A452BF /* UIView+NIM.h */, + 18EE3FF32750FA3700A452BF /* UIView+NIM.m */, + ); + path = NIMViews; + sourceTree = ""; + }; + 9B7D804727537819003DAC0C /* Session */ = { + isa = PBXGroup; + children = ( + 9B7D80482753783D003DAC0C /* SessionViewController.h */, + 9B7D80492753783D003DAC0C /* SessionViewController.m */, + 9B7D80542753C595003DAC0C /* SendMessageView.h */, + 9B7D80552753C595003DAC0C /* SendMessageView.m */, + 9B7D804B27537950003DAC0C /* MessageCell.h */, + 9B7D804C27537950003DAC0C /* MessageCell.m */, + ); + path = Session; + sourceTree = ""; + }; BFB922F5D81845AC32D1E1ED /* Frameworks */ = { isa = PBXGroup; children = ( @@ -2318,6 +2362,7 @@ E824544E26F5BC1A00BE8163 /* XPMineModifPayPwdView.m in Sources */, E8B825CA26EA1231009E8E9F /* LoginVerifCodeViewController.m in Sources */, 189DD76226E60DDC00AB55B1 /* Api+Login.m in Sources */, + 9B7D80502753AA9D003DAC0C /* UITableView+NIMScrollToBottom.m in Sources */, 18E7B22626E8CDCF0064BC9B /* XplanFlutterBoostDelegate.m in Sources */, E8EEB8F326FC2050007C6EBA /* SDWaitingView.m in Sources */, E8788942273A55AD00BF1D57 /* XPGiftUsersView.m in Sources */, @@ -2340,6 +2385,7 @@ E890BC0D273D09A50007C46B /* XPGiftCountView.m in Sources */, E8AC722F26F49610007D6E91 /* XPMineNotificationItemModel.m in Sources */, E8E70D7A26F2F16600F03460 /* XPMinePresent.m in Sources */, + 9B7D80562753C595003DAC0C /* SendMessageView.m in Sources */, E84B0E3F2727EDF6008818C6 /* XPRoomMessageTableViewCell.m in Sources */, E8EEB8F226FC2050007C6EBA /* SDPhotoBrowser.m in Sources */, E81C27AB26EF2D920031E639 /* ThirdUserInfo.m in Sources */, @@ -2368,6 +2414,7 @@ 186A534E26FC6ED900D67B2C /* TTPopupService.m in Sources */, 18A61BD7274F7F6900A09A54 /* NetImageConfig.m in Sources */, E8AC722426F47E5E007D6E91 /* XPMineFeedbackViewController.m in Sources */, + 9B7D804D27537950003DAC0C /* MessageCell.m in Sources */, E81366E326F0A1FC0076364C /* LoginBindPhoneViewController.m in Sources */, 189DD68426E1FDBB00AB55B1 /* XCHUDTool.m in Sources */, 189DD73F26E21C3F00AB55B1 /* YYUtility+Carrier.m in Sources */, @@ -2468,6 +2515,7 @@ E824546426F5FF1C00BE8163 /* XPMineResetPayPasswordPresenter.m in Sources */, E884C3722743AEDE00E1EBED /* CustomAttachmentDecoder.m in Sources */, 18EE3FEE2750CE6D00A452BF /* NIMMessageUtils.m in Sources */, + 9B7D804A2753783D003DAC0C /* SessionViewController.m in Sources */, E8B846BC26FD7C1200A777FE /* UploadImage.m in Sources */, 186A534D26FC6ED900D67B2C /* TTActionSheetView.m in Sources */, E83DB481274649FB00D8CBD1 /* XPGiftBannerUserInfoModel.m in Sources */, diff --git a/xplan-ios/Main/Message/NIMBadgeView.h b/xplan-ios/Main/Message/NIMViews/NIMBadgeView.h similarity index 100% rename from xplan-ios/Main/Message/NIMBadgeView.h rename to xplan-ios/Main/Message/NIMViews/NIMBadgeView.h diff --git a/xplan-ios/Main/Message/NIMBadgeView.m b/xplan-ios/Main/Message/NIMViews/NIMBadgeView.m similarity index 100% rename from xplan-ios/Main/Message/NIMBadgeView.m rename to xplan-ios/Main/Message/NIMViews/NIMBadgeView.m diff --git a/xplan-ios/Main/Message/NIMViews/UITableView+NIMScrollToBottom.h b/xplan-ios/Main/Message/NIMViews/UITableView+NIMScrollToBottom.h new file mode 100644 index 00000000..8d69ec5e --- /dev/null +++ b/xplan-ios/Main/Message/NIMViews/UITableView+NIMScrollToBottom.h @@ -0,0 +1,14 @@ +// +// UITableView+NTESScrollToBottom.h +// NIMDemo +// +// Created by chris. +// Copyright (c) 2015年 Netease. All rights reserved. +// + +#import + +@interface UITableView (NIMKit) + +- (void)nim_scrollToBottom:(BOOL)animation; +@end diff --git a/xplan-ios/Main/Message/NIMViews/UITableView+NIMScrollToBottom.m b/xplan-ios/Main/Message/NIMViews/UITableView+NIMScrollToBottom.m new file mode 100644 index 00000000..c274ef23 --- /dev/null +++ b/xplan-ios/Main/Message/NIMViews/UITableView+NIMScrollToBottom.m @@ -0,0 +1,26 @@ +// +// UITableView+NTESScrollToBottom.m +// NIMDemo +// +// Created by chris. +// Copyright (c) 2015年 Netease. All rights reserved. +// + +#import "UITableView+NIMScrollToBottom.h" + +@implementation UITableView (NIMKit) + +- (void)nim_scrollToBottom:(BOOL)animation +{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + NSInteger row = [self numberOfRowsInSection:0] - 1; + if (row > 0) + { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0]; + [self scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:animation]; + } + }); +} + + +@end diff --git a/xplan-ios/Main/Message/UIView+NIM.h b/xplan-ios/Main/Message/NIMViews/UIView+NIM.h similarity index 100% rename from xplan-ios/Main/Message/UIView+NIM.h rename to xplan-ios/Main/Message/NIMViews/UIView+NIM.h diff --git a/xplan-ios/Main/Message/UIView+NIM.m b/xplan-ios/Main/Message/NIMViews/UIView+NIM.m similarity index 100% rename from xplan-ios/Main/Message/UIView+NIM.m rename to xplan-ios/Main/Message/NIMViews/UIView+NIM.m diff --git a/xplan-ios/Main/Message/Session/MessageCell.h b/xplan-ios/Main/Message/Session/MessageCell.h new file mode 100644 index 00000000..46e93200 --- /dev/null +++ b/xplan-ios/Main/Message/Session/MessageCell.h @@ -0,0 +1,20 @@ +// +// MessageCell.h +// xplan-ios +// +// Created by zu on 2021/11/28. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MessageCell : UITableViewCell + ++ (CGFloat)measureHeight:(NIMMessage *)message; +- (void)renderWithMessage:(NIMMessage *)message; + +@end + +NS_ASSUME_NONNULL_END diff --git a/xplan-ios/Main/Message/Session/MessageCell.m b/xplan-ios/Main/Message/Session/MessageCell.m new file mode 100644 index 00000000..38f2d84d --- /dev/null +++ b/xplan-ios/Main/Message/Session/MessageCell.m @@ -0,0 +1,193 @@ +// +// MessageCell.m +// xplan-ios +// +// Created by zu on 2021/11/28. +// + +#import "MessageCell.h" +#import "NetImageView.h" +#import "ThemeColor.h" + +#import "XPMacro.h" +#import + +@interface MessageCell() + +/** + 左侧头像(私聊对象) + */ +@property (nonatomic, strong) NetImageView * leftAvatar; + +/** + 右侧头像(自己) + */ +@property (nonatomic, strong) NetImageView * rightAvatar; + +/** + 消息背景 + */ +@property (nonatomic, strong) UIView * messageBackground; +@property (nonatomic, strong) MASConstraint * messageBackgroundLeft; +@property (nonatomic, strong) MASConstraint * messageBackgroundRight; + +/** + 消息文本 + */ +@property (nonatomic, strong) UILabel * messageText; + +@end + +@implementation MessageCell + +/** + 当前仅支持 Text Message。 + + 扩展方向: + 新增Measure(Layout)工具类,按照 NIMMessageType 进行 measure 和生成对应的 Message content view。 + */ ++ (CGFloat)measureHeight:(NIMMessage *)message { + CGFloat minHeight = 45 + 15 * 2; + if (!message) { + return minHeight; + } + + NSString * messageText = message.text; + if (!messageText || (message.messageType != NIMMessageTypeTip && message.messageType != NIMMessageTypeText)) { + messageText = @"未知消息类型"; + } + + CGSize dstRect = CGSizeMake(KScreenWidth - 15 * 2 * 2 - 45 * 2 - 10 * 2, MAXFLOAT); + + CGFloat msgHeight = [messageText boundingRectWithSize:dstRect options:NSStringDrawingUsesLineFragmentOrigin attributes:[self messageTextAttibutes] context:nil].size.height; + CGFloat height = msgHeight + 5 + 15 * 2; + + return MAX(minHeight, height); +} + ++ (NSDictionary *)messageTextAttibutes { + UIFont *font = [UIFont systemFontOfSize:13.f]; + NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + [paragraphStyle setLineSpacing:2.5]; + return @{ + NSFontAttributeName:font, + NSParagraphStyleAttributeName: paragraphStyle + }; +} + +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self) { + self.selectionStyle = UITableViewCellSelectionStyleNone; + [self initViews]; + [self initLayout]; + } + return self; +} + +- (void)initViews { + self.backgroundColor = UIColor.clearColor; + self.leftAvatar.backgroundColor = UIColor.redColor; + self.rightAvatar.backgroundColor = UIColor.blueColor; + [self.contentView addSubview:self.leftAvatar]; + [self.contentView addSubview:self.rightAvatar]; + [self.messageBackground addSubview:self.messageText]; + [self.contentView addSubview:self.messageBackground]; +} + +- (void)initLayout { + [self.leftAvatar mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.top.mas_equalTo(self).offset(15); + make.width.height.mas_equalTo(45); + }]; + + [self.rightAvatar mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(self).offset(15); + make.right.mas_equalTo(self).offset(-15); + make.width.height.mas_equalTo(45); + }]; + + [self.messageBackground mas_makeConstraints:^(MASConstraintMaker *make) { + self.messageBackgroundLeft = make.left.mas_equalTo(self.leftAvatar.mas_right).offset(15); + self.messageBackgroundRight = make.right.mas_equalTo(self.rightAvatar.mas_left).offset(-15); + make.top.mas_equalTo(self).offset(20); + }]; + + [self.messageText mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(self.messageBackground).with.insets(UIEdgeInsetsMake(10, 10, 10, 10)); + }]; +} + +- (void)renderWithMessage:(NIMMessage *)message { + NSString * avatarUrl = [[NIMSDK sharedSDK].userManager userInfo:message.from].userInfo.avatarUrl; + avatarUrl = [avatarUrl stringByReplacingOccurrencesOfString:@"https" withString:@"http"]; + BOOL isSelf = [[NIMSDK sharedSDK].loginManager.currentAccount isEqualToString:message.from]; + if (isSelf) { + self.leftAvatar.hidden = YES; + self.rightAvatar.hidden = NO; + [self.messageBackgroundLeft uninstall]; + [self.messageBackgroundRight install]; + self.rightAvatar.imageUrl = avatarUrl; + } else { + self.leftAvatar.hidden = NO; + self.rightAvatar.hidden = YES; + [self.messageBackgroundLeft install]; + [self.messageBackgroundRight uninstall]; + self.leftAvatar.imageUrl = avatarUrl; + } + + NSString * messageText = message.text; + if (!messageText || (message.messageType != NIMMessageTypeTip && message.messageType != NIMMessageTypeText)) { + messageText = @"未知消息类型"; + } + + _messageText.attributedText = [[NSAttributedString alloc] initWithString:messageText attributes:[MessageCell messageTextAttibutes]]; +} + +- (NetImageView *)leftAvatar { + if (!_leftAvatar) { + NetImageConfig * config = [[NetImageConfig alloc] init]; + config.placeHolder = [UIImageConstant defaultAvatarPlaceholder]; + config.radius = MAXFLOAT; + _leftAvatar = [[NetImageView alloc] initWithConfig:config]; + _leftAvatar.layer.masksToBounds = YES; + _leftAvatar.layer.cornerRadius = 45.f / 2; + _leftAvatar.hidden = YES; + } + return _leftAvatar; +} + +- (NetImageView *)rightAvatar { + if (!_rightAvatar) { + NetImageConfig * config = [[NetImageConfig alloc] init]; + config.placeHolder = [UIImageConstant defaultAvatarPlaceholder]; + config.radius = MAXFLOAT; + _rightAvatar = [[NetImageView alloc] initWithConfig:config]; + _rightAvatar.layer.masksToBounds = YES; + _rightAvatar.layer.cornerRadius = 45.f / 2; + _rightAvatar.hidden = YES; + } + return _rightAvatar; +} + +- (UIView *)messageBackground { + if (!_messageBackground) { + _messageBackground = [[UIView alloc]init]; + _messageBackground.backgroundColor = ThemeColor.appCellBackgroundColor; + _messageBackground.layer.masksToBounds = YES; + _messageBackground.layer.cornerRadius = 8.f; + } + return _messageBackground; +} + +- (UILabel *)messageText { + if (!_messageText) { + _messageText = [[UILabel alloc]initWithFrame:CGRectZero]; + _messageText.preferredMaxLayoutWidth = KScreenWidth - 15 * 2 * 2 - 45 * 2 - 10 * 2; + _messageText.textColor = ThemeColor.mainTextColor; + _messageText.numberOfLines = 0; + } + return _messageText; +} + +@end diff --git a/xplan-ios/Main/Message/Session/SendMessageView.h b/xplan-ios/Main/Message/Session/SendMessageView.h new file mode 100644 index 00000000..66530c4c --- /dev/null +++ b/xplan-ios/Main/Message/Session/SendMessageView.h @@ -0,0 +1,18 @@ +// +// SendMessageView.h +// xplan-ios +// +// Created by zu on 2021/11/28. +// + +#import + +#import "RoomHostDelegate.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SendMessageView : UIView + +@end + +NS_ASSUME_NONNULL_END diff --git a/xplan-ios/Main/Message/Session/SendMessageView.m b/xplan-ios/Main/Message/Session/SendMessageView.m new file mode 100644 index 00000000..a0eaed96 --- /dev/null +++ b/xplan-ios/Main/Message/Session/SendMessageView.m @@ -0,0 +1,187 @@ +// +// SendMessageView.m +// xplan-ios +// +// Created by zu on 2021/11/28. +// + +#import "SendMessageView.h" +#import +#import +#import "ThemeColor.h" +#import "XPMacro.h" +#import "UIImage+Utils.h" + +@interface SendMessageView () + +@property (nonatomic, strong) UIStackView *stackView; +@property (nonatomic, strong) UIView *inputBackgroundView; +@property (nonatomic, strong) UITextField *inputView; +@property (nonatomic, strong) UIButton *sendButton; + +@end + +#define VIEW_HEIGHT (kSafeAreaBottomHeight + 50) + +@implementation SendMessageView + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (instancetype)init { + self = [super initWithFrame:CGRectMake(0, KScreenHeight - VIEW_HEIGHT, KScreenWidth, VIEW_HEIGHT)]; + if (self) { + [self addNotification]; + [self initViews]; + [self initLayout]; + } + return self; +} + +#pragma mark - Response + +- (void)sendButtonDidClick:(UIButton *)sender { + +} + +- (void)keyboardWillShow:(NSNotification *)notification { + [self.superview bringSubviewToFront:self]; + NSDictionary *info = [notification userInfo]; + CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; + NSNumber *curve = [info objectForKey:UIKeyboardAnimationCurveUserInfoKey]; + CGFloat duration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]; + + CGRect newFrame = self.frame; + newFrame.origin.y = self.superview.frame.size.height - keyboardFrame.size.height - 50; + + UIViewAnimationOptions options = (UIViewAnimationOptions)curve << 16; + [UIView animateWithDuration:duration delay:0.0 options:options animations:^{ + self.frame = newFrame; + [self mas_remakeConstraints:^(MASConstraintMaker *make) { + make.left.right.equalTo(self.superview); + make.bottom.equalTo(self.superview).offset(-keyboardFrame.size.height + kSafeAreaBottomHeight); + make.height.equalTo(@(VIEW_HEIGHT)); + }]; + } completion:nil]; +} + +- (void)keyboardWillHidden:(NSNotification *)notification { + NSDictionary *info = [notification userInfo]; + NSNumber *curve = [info objectForKey:UIKeyboardAnimationCurveUserInfoKey]; + CGFloat duration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]; + + CGRect newFrame = self.frame; + newFrame.origin.y = self.superview.frame.size.height - VIEW_HEIGHT; + + UIViewAnimationOptions options = (UIViewAnimationOptions)curve << 16; + [UIView animateWithDuration:duration delay:0.0 options:options animations:^{ + self.frame = newFrame; + [self mas_remakeConstraints:^(MASConstraintMaker *make) { + make.bottom.left.right.equalTo(self.superview); + make.height.equalTo(@(VIEW_HEIGHT)); + }]; + } completion:nil]; +} + +-(void)textFieldEditChanged:(NSNotification *)notification{ + self.sendButton.enabled = self.inputView.text.length > 0; +} + +- (void)initViews { + self.backgroundColor = [ThemeColor appCellBackgroundColor]; + [self addSubview:self.stackView]; + [self.inputBackgroundView addSubview:self.inputView]; + [self.stackView addArrangedSubview:self.inputBackgroundView]; + [self.stackView addArrangedSubview:self.sendButton]; +} + +- (void)initLayout { + [self.stackView mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.mas_equalTo(self).offset(15); + make.right.mas_equalTo(self).offset(-15); + make.top.mas_equalTo(self); + make.bottom.mas_equalTo(self).offset(-kSafeAreaBottomHeight); + }]; + + [self.sendButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.width.mas_equalTo(50); + make.top.bottom.mas_equalTo(self.stackView).inset(10); + }]; + + [self.inputBackgroundView mas_makeConstraints:^(MASConstraintMaker *make) { + make.height.mas_equalTo(36); + make.left.mas_equalTo(self.stackView); + make.right.mas_equalTo(self.sendButton.mas_left).offset(-15); + }]; + + [self.inputView mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.mas_equalTo(self.inputBackgroundView).offset(8); + make.right.mas_equalTo(self.inputBackgroundView).offset(-8); + make.centerY.mas_equalTo(self.inputBackgroundView); + }]; + + [self mas_makeConstraints:^(MASConstraintMaker *make) { + make.height.mas_equalTo(VIEW_HEIGHT); + }]; +} + +- (void)addNotification { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHidden:) name:UIKeyboardWillHideNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldEditChanged:) name:UITextFieldTextDidChangeNotification object:self.inputView]; +} + +- (UIStackView *)stackView { + if (!_stackView) { + _stackView = [[UIStackView alloc] init]; + _stackView.axis = UILayoutConstraintAxisHorizontal; + _stackView.distribution = UIStackViewDistributionFill; + _stackView.alignment = UIStackViewAlignmentCenter; + _stackView.spacing = 10; + } + return _stackView; +} + +- (UITextField *)inputView{ + if (!_inputView) { + _inputView = [[UITextField alloc] init]; + NSAttributedString * attribute = [[NSAttributedString alloc] initWithString:@"请输入消息" attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:15], NSForegroundColorAttributeName :UIColorFromRGB(0x555574)}]; + _inputView.attributedPlaceholder = attribute; + _inputView.borderStyle = UITextBorderStyleNone; + _inputView.tintColor = [ThemeColor appMainColor]; + _inputView.textColor = [ThemeColor mainTextColor]; + _inputView.font = [UIFont systemFontOfSize:13.f]; + [_inputView setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal]; + + } + return _inputView; +} + +- (UIView *)inputBackgroundView { + if (!_inputBackgroundView) { + _inputBackgroundView = [[UIView alloc] init]; + _inputBackgroundView.backgroundColor = UIColorFromRGB(0x35354B); + _inputBackgroundView.layer.masksToBounds = YES; + _inputBackgroundView.layer.cornerRadius = 4.f; + } + return _inputBackgroundView; +} + +- (UIButton *)sendButton{ + if (!_sendButton) { + _sendButton = [[UIButton alloc] init]; + [_sendButton setTitle:@"发送" forState:UIControlStateNormal]; + _sendButton.titleLabel.textColor = [ThemeColor mainTextColor]; + _sendButton.titleLabel.font = [UIFont systemFontOfSize:15]; + [_sendButton setBackgroundImage:[UIImage imageWithColor:[ThemeColor disableButtonColor] ]forState:UIControlStateDisabled]; + [_sendButton setBackgroundImage:[UIImage gradientColorImageFromColors:@[[ThemeColor confirmButtonGradientStartColor], [ThemeColor confirmButtonGradientEndColor]] gradientType:GradientTypeLeftToRight imgSize:CGSizeMake(10, 10)] forState:UIControlStateNormal]; + _sendButton.enabled = NO; + _sendButton.layer.cornerRadius = 5.0; + _sendButton.layer.masksToBounds = YES; + [_sendButton addTarget:self action:@selector(sendButtonDidClick:) forControlEvents:UIControlEventTouchUpInside]; + } + return _sendButton; +} + +@end diff --git a/xplan-ios/Main/Message/Session/SessionViewController.h b/xplan-ios/Main/Message/Session/SessionViewController.h new file mode 100644 index 00000000..212a8727 --- /dev/null +++ b/xplan-ios/Main/Message/Session/SessionViewController.h @@ -0,0 +1,19 @@ +// +// SessionViewController.h +// xplan-ios +// +// Created by zu on 2021/11/28. +// + +#import "MvpViewController.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface SessionViewController : MvpViewController + +- (instancetype)initWithSession:(NIMSession *)session; + +@end + +NS_ASSUME_NONNULL_END diff --git a/xplan-ios/Main/Message/Session/SessionViewController.m b/xplan-ios/Main/Message/Session/SessionViewController.m new file mode 100644 index 00000000..27acd2cb --- /dev/null +++ b/xplan-ios/Main/Message/Session/SessionViewController.m @@ -0,0 +1,173 @@ +// +// SessionViewController.m +// xplan-ios +// +// Created by zu on 2021/11/28. +// + +#import "SessionViewController.h" +#import "MessageCell.h" +#import "SendMessageView.h" +#import "UITableView+NIMScrollToBottom.h" +#import "XPMacro.h" + +#import + +@interface SessionViewController () + +@property (nonatomic, strong) NIMSession * session; +@property (nonatomic, strong) NSMutableArray * messages; + +@property (nonatomic, strong) UITableView * sessionTableView; +@property (nonatomic, strong) SendMessageView * sendMessageView; + +@end + +@implementation SessionViewController + +- (void)dealloc { + [[NIMSDK sharedSDK].chatManager removeDelegate:self]; + [[NIMSDK sharedSDK].conversationManager removeDelegate:self]; + [[NIMSDK sharedSDK].mediaManager removeDelegate:self]; +} + +- (instancetype)initWithSession:(NIMSession *)session { + self = [super init]; + if (self) { + _session = session; + [self initViews]; + [self initLayout]; + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + [self initData]; + [[NIMSDK sharedSDK].chatManager addDelegate:self]; + [[NIMSDK sharedSDK].conversationManager addDelegate:self]; +} + +- (void)initViews { + NIMUser * user = [[NIMSDK sharedSDK].userManager userInfo:self.session.sessionId]; + if (user) { + self.title = user.userInfo.nickName; + } else { + self.title = @"加载中……"; + } + + + [self.view addSubview:self.sessionTableView]; + [self.view addSubview:self.sendMessageView]; +} + +- (void)initLayout { + [self.sessionTableView mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.right.top.mas_equalTo(self.view); + make.bottom.mas_equalTo(self.sendMessageView.mas_top); + }]; + + [self.sendMessageView mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.right.bottom.mas_equalTo(self.view); + }]; +} + +- (void)initData { + NSArray *messages = [[[NIMSDK sharedSDK] conversationManager] messagesInSession:self.session + message:nil + limit:1000]; + [self.messages addObjectsFromArray:messages.copy]; + [self.sessionTableView reloadData]; + [self.sessionTableView nim_scrollToBottom:NO]; + + [[NIMSDK sharedSDK].userManager fetchUserInfos:@[self.session.sessionId] completion:^(NSArray * _Nullable users, NSError * _Nullable error) { + if (!error) { + self.title = users[0].userInfo.nickName; + } + }]; + + [[NIMSDK sharedSDK].conversationManager markAllMessagesReadInSession:self.session]; +} + +#pragma mark - UITableViewDataSource +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return 1; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return self.messages.count; +} + +- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath { + MessageCell * cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([MessageCell class])]; + if (cell == nil) { + cell = [[MessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NSStringFromClass([MessageCell class])]; + } + [cell renderWithMessage:[self.messages objectAtIndex:indexPath.row]]; + return cell; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + NIMMessage *msg = [self.messages objectAtIndex:indexPath.row]; + return [MessageCell measureHeight:msg]; +} + +#pragma mark - NIMChatManagerDelegate +- (void)onRecvMessages:(NSArray *)messages { + NIMMessage *message = messages.firstObject; + NIMSession *session = message.session; + if (![session isEqual:self.session] || !messages.count) { + return; + } + for (NIMMessage *message in messages) { + if (message.isDeleted) { + continue; + } + [self.messages addObject:message]; + } + [self.sessionTableView reloadData]; + [self.sessionTableView nim_scrollToBottom:YES]; + + [[NIMSDK sharedSDK].conversationManager markAllMessagesReadInSession:self.session]; +} + +#pragma mark - NIMConversationManagerDelegate +- (void)messagesDeletedInSession:(NIMSession *)session { + [self.messages removeAllObjects]; + [self initData]; +} + +- (NSArray *)messages { + if (!_messages) { + _messages = [[NSMutableArray alloc] init]; + } + return _messages; +} + +- (UITableView *)sessionTableView { + if (!_sessionTableView) { + _sessionTableView = [[UITableView alloc] init]; + _sessionTableView.delegate = self; + _sessionTableView.dataSource = self; + _sessionTableView.backgroundColor = UIColor.clearColor; + _sessionTableView.separatorStyle = UITableViewCellSeparatorStyleNone; + + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(closeKeyBoard)]; + [_sessionTableView addGestureRecognizer:tap]; + } + return _sessionTableView; +} + +- (void)closeKeyBoard { + [self.view endEditing:YES]; + [self.sessionTableView endEditing:YES]; +} + +- (SendMessageView *)sendMessageView { + if (!_sendMessageView) { + _sendMessageView = [[SendMessageView alloc] init]; + } + return _sendMessageView; +} + +@end diff --git a/xplan-ios/Main/Message/SessionListCell.h b/xplan-ios/Main/Message/SessionList/SessionListCell.h similarity index 100% rename from xplan-ios/Main/Message/SessionListCell.h rename to xplan-ios/Main/Message/SessionList/SessionListCell.h diff --git a/xplan-ios/Main/Message/SessionListCell.m b/xplan-ios/Main/Message/SessionList/SessionListCell.m similarity index 99% rename from xplan-ios/Main/Message/SessionListCell.m rename to xplan-ios/Main/Message/SessionList/SessionListCell.m index 05f88eea..c4eac2d2 100644 --- a/xplan-ios/Main/Message/SessionListCell.m +++ b/xplan-ios/Main/Message/SessionList/SessionListCell.m @@ -64,7 +64,7 @@ self.badgeView.hidden = NO; self.badgeView.badgeValue = @(recent.unreadCount).stringValue; } else { - self.badgeView.hidden = NO; + self.badgeView.hidden = YES; } } diff --git a/xplan-ios/Main/Message/SessionListViewController.h b/xplan-ios/Main/Message/SessionList/SessionListViewController.h similarity index 100% rename from xplan-ios/Main/Message/SessionListViewController.h rename to xplan-ios/Main/Message/SessionList/SessionListViewController.h diff --git a/xplan-ios/Main/Message/SessionListViewController.m b/xplan-ios/Main/Message/SessionList/SessionListViewController.m similarity index 96% rename from xplan-ios/Main/Message/SessionListViewController.m rename to xplan-ios/Main/Message/SessionList/SessionListViewController.m index 2769d369..dffa93a5 100644 --- a/xplan-ios/Main/Message/SessionListViewController.m +++ b/xplan-ios/Main/Message/SessionList/SessionListViewController.m @@ -7,6 +7,7 @@ #import "SessionListViewController.h" #import "SessionListCell.h" +#import "SessionViewController.h" #import "ThemeColor.h" #import "XPMacro.h" @@ -83,7 +84,9 @@ #pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ [tableView deselectRowAtIndexPath:indexPath animated:YES]; - // TODO: 打开私聊 + NIMRecentSession *recentSession = self.recentSessions[indexPath.row]; + SessionViewController *vc = [[SessionViewController alloc] initWithSession:recentSession.session]; + [self.navigationController pushViewController:vc animated:YES]; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{