标签:
初始化好友花名册
1 #pragma mark - 管理好友 2 // 获取管理好友的单例对象 3 XMPPRosterCoreDataStorage *rosterStorage = [XMPPRosterCoreDataStorage sharedInstance]; 4 // 用管理好友的单例对象初始化Roster花名册 5 // 好友操作是耗时操作 6 self.xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:rosterStorage dispatchQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; 7 // 在通道中激活xmppRoster 8 [self.xmppRoster activate:self.xmppStream]; 9 // 设置代理 10 [self.xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
XMPPRoster代理方法
好友列表
添加好友
删除好友
XMPPManager.h 新增代码
1 /// 管理好友,好友的列表 2 @property (nonatomic, strong) XMPPRoster *xmppRoster; 3 4 #pragma mark - 添加好友 5 - (void)addFriend; 6 7 #pragma mark - 删除好友 8 - (void)removeFriendWithName:(NSString *)userName;
XMPPManager.m 新增代码
1 /// 接收要添加好友的名字 2 @property (nonatomic, strong) UITextField *addText; 3 4 #pragma mark - 重写初始化方法 5 - (instancetype)init { 6 if (self = [super init]) { 7 #pragma mark - 创建通道 8 // 初始化通道对象 9 self.xmppStream = [[XMPPStream alloc] init]; 10 // 设置Openfire服务器主机名 11 self.xmppStream.hostName = kHostName; 12 // 设置服务器端口号 13 self.xmppStream.hostPort = kHostPort; 14 // 设置代理 15 [self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; 16 17 #warning -----------------以下是该方法中新增加的代码----------------------- 18 #pragma mark - 管理好友 19 // 获取管理好友的单例对象 20 XMPPRosterCoreDataStorage *rosterStorage = [XMPPRosterCoreDataStorage sharedInstance]; 21 // 用管理好友的单例对象初始化Roster花名册 22 // 好友操作是耗时操作 23 self.xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:rosterStorage dispatchQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; 24 // 在通道中激活xmppRoster 25 [self.xmppRoster activate:self.xmppStream]; 26 // 设置代理 27 [self.xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()]; 28 29 #warning -----------------以上是该方法中新增加的代码----------------------- 30 31 } 32 return self; 33 } 34 35 #pragma mark -----------------以下是管理好友列表---------------- 36 #pragma mark - 添加好友 37 - (void)addFriend { 38 NSLog(@"manager - 添加好友 %d", __LINE__); 39 UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"添加好友" message:@"请输入要添加好友的名字" preferredStyle:UIAlertControllerStyleAlert]; 40 __weak typeof(self)weakSelf = self; 41 [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 42 // 接收输入的好友名字 43 weakSelf.addText = textField; 44 }]; 45 UIAlertAction *cancleAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 46 UIAlertAction *sureAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 47 NSLog(@"======%@", weakSelf.addText.text); 48 // 使用JID记录 49 XMPPJID *addJID = [XMPPJID jidWithUser:weakSelf.addText.text domain:kDomin resource:kResource]; 50 // 监听好友的动作 51 [weakSelf.xmppRoster subscribePresenceToUser:addJID]; 52 // 添加好友 53 [weakSelf.xmppRoster addUser:addJID withNickname:weakSelf.addText.text]; 54 }]; 55 [alertController addAction:sureAction]; 56 [alertController addAction:cancleAction]; 57 [[self getCurrentVC ]presentViewController:alertController animated:YES completion:nil]; 58 } 59 60 #pragma mark - 删除好友 61 - (void)removeFriendWithName:(NSString *)userName { 62 NSLog(@"manager删除好友 %d", __LINE__); 63 // 使用JID记录要删除的用户名 64 XMPPJID *removeJID = [XMPPJID jidWithUser:userName domain:kDomin resource:kResource]; 65 // 停止监听好友 66 [self.xmppRoster unsubscribePresenceFromUser:removeJID]; 67 // 删除好友 68 [self.xmppRoster removeUser:removeJID]; 69 } 70 #pragma mark - 获取当前屏幕显示的viewcontroller 71 - (UIViewController *)getCurrentVC 72 { 73 UIViewController *result = nil; 74 75 UIWindow * window = [[UIApplication sharedApplication] keyWindow]; 76 if (window.windowLevel != UIWindowLevelNormal) 77 { 78 NSArray *windows = [[UIApplication sharedApplication] windows]; 79 for(UIWindow * tmpWin in windows) 80 { 81 if (tmpWin.windowLevel == UIWindowLevelNormal) 82 { 83 window = tmpWin; 84 break; 85 } 86 } 87 } 88 89 UIView *frontView = [[window subviews] objectAtIndex:0]; 90 id nextResponder = [frontView nextResponder]; 91 92 if ([nextResponder isKindOfClass:[UIViewController class]]) 93 result = nextResponder; 94 else 95 result = window.rootViewController; 96 97 return result; 98 } 99 100 #pragma mark -----------------以下XMPPRosterDelegate是协议中的方法---------------- 101 #pragma mark - 开始获取好友 102 - (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender { 103 NSLog(@"manager - 开始获取好友,%d", __LINE__); 104 } 105 106 #pragma mark - 获取好友结束 107 - (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender { 108 NSLog(@"manager - 获取好友结束, %d", __LINE__); 109 } 110 111 #pragma mark - 接收好友状态信息 112 - (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item { 113 // 接收好友状态 114 NSLog(@"manager - 好友信息%@ -- %d", item, __LINE__); 115 } 116 #pragma mark - 接收到添加好友的请求,选择接受or拒绝 117 - (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence { 118 NSLog(@"manager - 获取好友请求, %d", __LINE__); 119 } 120 121 #pragma mark -----------------以上XMPPRosterDelegate是协议中的方法----------------
RosterListTableViewController.m 好友列表显示页面
1 #import "RosterListTableViewController.h" 2 #import "XMPPManager.h" 3 #import "ChatTableViewController.h" 4 5 @interface RosterListTableViewController ()<XMPPRosterDelegate, XMPPStreamDelegate> 6 7 /// 好友列表 8 @property (nonatomic, strong) NSMutableArray *allRosterArray; 9 /// 用来存储发送好友请求者的JID 10 @property (nonatomic, strong) XMPPJID *fromJID; 11 12 @end 13 14 @implementation RosterListTableViewController 15 16 - (void)viewDidLoad { 17 [super viewDidLoad]; 18 // 初始化数组 19 self.allRosterArray = [NSMutableArray array]; 20 21 [[XMPPManager sharedXMPPManager].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; 22 [[XMPPManager sharedXMPPManager].xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()]; 23 self.title = @"好友列表"; 24 25 // 添加按钮 26 self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addFriendAction)]; 27 // 返回按钮 28 self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"注销" style:UIBarButtonItemStylePlain target:self action:@selector(cancleAction)]; 29 30 [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"RosterCell"]; 31 32 } 33 34 #pragma mark - 添加好友按钮点击事件 35 - (void)addFriendAction { 36 37 [[XMPPManager sharedXMPPManager] addFriend]; 38 } 39 40 #pragma mark - 注销按钮点击事件 41 - (void)cancleAction { 42 #warning 没有实现真正的注销?只是返回到了登录界面 43 [self.navigationController popViewControllerAnimated:YES]; 44 } 45 46 #pragma mark - Table view data source 47 48 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 49 return 1; 50 } 51 52 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 53 return self.allRosterArray.count; 54 } 55 56 57 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 58 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"RosterCell" forIndexPath:indexPath]; 59 60 // 根据项目情况分析,合理添加判断 61 if (self.allRosterArray.count > 0) { 62 // 获取用户 63 XMPPJID *jid = [self.allRosterArray objectAtIndex:indexPath.row]; 64 cell.textLabel.text = jid.user; 65 NSLog(@"bare %@", jid.bare); 66 } 67 68 return cell; 69 } 70 71 72 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { 73 return YES; 74 } 75 76 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { 77 if (editingStyle == UITableViewCellEditingStyleDelete) { 78 // 删除一个好友 79 XMPPJID *jid = self.allRosterArray[indexPath.row]; 80 // 根据名字删除好友 81 [[XMPPManager sharedXMPPManager] removeFriendWithName:jid.user]; 82 // 从数组中移除 83 [self.allRosterArray removeObjectAtIndex:indexPath.row]; 84 [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; 85 } else if (editingStyle == UITableViewCellEditingStyleInsert) { 86 87 } 88 } 89 98 99 #pragma mark - ----------------XMPPRosterDelegate代理方法---------------- 100 #pragma mark - 开始获取好友 101 - (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender { 102 NSLog(@"开始获取好友列表"); 103 } 104 105 #pragma mark - 结束获取好友 106 - (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender { 107 // 当前页面是用于显示好友列表的,所以在结束获取好友的代理方法中要进行页面刷新页面,然后将数据显示。 108 // 刷新UI 109 [self.tableView reloadData]; 110 } 111 112 #pragma mark - 接收好友信息 113 // 这个代理方法会被执行很多次,每次添加完好友,相对应的好友信息都要反馈 114 - (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item { 115 NSLog(@"listTVC 接收好友信息 %d", __LINE__); 116 /** 117 * 好友信息状态有5种 118 both - 互为好友 119 none - 互不为好友 120 to - 请求添加对方为好友,对方还没有同意 121 from - 对方添加我为好友,自己还没有同意 122 remove - 曾经删除的好友 123 */ 124 125 // 自己和对方之间的关系 126 NSString *description = [[item attributeForName:@"subscription"] stringValue]; 127 NSLog(@"关系%@", description); 128 if ([description isEqualToString:@"both"] || [description isEqualToString:@"to"] || [description isEqualToString:@"from"] || [description isEqualToString:@"none"]) { 129 // 添加好友 130 // 获取好友的JID 131 NSString *friendJID = [[item attributeForName:@"jid"] stringValue]; 132 XMPPJID *jid = [XMPPJID jidWithString:friendJID]; 133 // 如果数组中含有这个用户,那么不再进行接下来的操作 134 if ([self.allRosterArray containsObject:jid]) { 135 NSLog(@"已经有该好友"); 136 return; 137 } 138 // 添加好友到数组中 139 [self.allRosterArray addObject:jid]; 140 141 // 在TableView的最后一个cell下面添加这条数据 142 NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.allRosterArray.count - 1 inSection:0]; 143 [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft]; 144 } 145 } 146 147 #pragma mark - 接收到添加好友的请求,选择接受or拒绝 148 - (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence { 149 NSLog(@"listTVC 接收添加好友的请求 %d", __LINE__); 150 self.fromJID = presence.from; 151 // 需要相关的提醒框去确定是否接收 152 UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"请求添加好友" message:@"是否同意" preferredStyle:UIAlertControllerStyleAlert]; 153 __weak typeof(self)weakSelf = self; 154 UIAlertAction *acceptAction = [UIAlertAction actionWithTitle:@"同意" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 155 // 添加到花名册 156 [[XMPPManager sharedXMPPManager].xmppRoster acceptPresenceSubscriptionRequestFrom:weakSelf.fromJID andAddToRoster:YES]; 157 }]; 158 UIAlertAction *rejectAction = [UIAlertAction actionWithTitle:@"拒绝" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { 159 [[XMPPManager sharedXMPPManager].xmppRoster rejectPresenceSubscriptionRequestFrom:weakSelf.fromJID]; 160 }]; 161 [alertController addAction:acceptAction]; 162 [alertController addAction:rejectAction]; 163 [self presentViewController:alertController animated:YES completion:nil]; 164 } 165 166 #pragma mark - ----------------XMPPStreamDelegate代理方法---------------- 167 #pragma mark - 判断好友是否处于上线状态 168 - (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence { 169 NSLog(@"listTVC 判断好友是否处于上线状态 %@ %d", presence.status, __LINE__); 170 NSString *type = presence.type; 171 NSString *presenceUser = presence.to.user; 172 // 判断当前用户是否为好友 173 if ([presenceUser isEqualToString:[sender myJID].user]) { 174 if ([type isEqualToString:@"available"]) { 175 NSLog(@"该用户处于上线状态"); 176 } else if ([type isEqualToString:@"unavailable"]) { 177 NSLog(@"该用户处于下线状态"); 178 } 179 } 180 } 181 182 @end
聊天的规则:
1、从服务器获取聊天记录
2、根据消息类XMPPMessageArchiving_Message_CoreDataObject的对象的属性isOutgoing来判断该消息是不是对方发送过来的消息 YES - 对方发送的消息, NO - 自己发送给对方的消息
3、发送消息
4、接收消息
初始化消息归档
1 #pragma mark - 初始化消息归档 2 // 获取管理消息的存储对象 3 XMPPMessageArchivingCoreDataStorage *storage = [XMPPMessageArchivingCoreDataStorage sharedInstance]; 4 // 进行消息管理器的初始化 5 self.xmppMessageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:storage dispatchQueue:dispatch_get_main_queue()]; 6 // 在通道中激活xmppMessageArchiving 7 [self.xmppMessageArchiving activate:self.xmppStream]; 8 // 设置代理 9 [self.xmppMessageArchiving addDelegate:self delegateQueue:dispatch_get_main_queue()]; 10 // 设置管理上下文 (此时不再从AppDelegate获取) 11 self.context = storage.mainThreadManagedObjectContext;
获取聊天记录(使用CoreData的方式)
1、创建请求
2、创建实体描述,实体名: XMPPMessageArchiving_Message_CoreDataObject
3、创建谓词查询条件,条件:streamBareJidStr == 本人Jid AND bareJidStr == 好友Jid
4、创建排序对象,排序条件:timestamp
5、执行请求
接受、发送消息用到的代理方法
消息气泡
XMPPManager.h 新增代码
1 /// 和聊天相关的属性,消息归档 2 @property (nonatomic, strong) XMPPMessageArchiving *xmppMessageArchiving; 3 /// 管理数据库上下文 4 @property (nonatomic, strong) NSManagedObjectContext *context;
XMPPManager.m 新增代码
#pragma mark - 重写初始化方法 - (instancetype)init { if (self = [super init]) { #pragma mark - 创建通道 // 初始化通道对象 self.xmppStream = [[XMPPStream alloc] init]; // 设置Openfire服务器主机名 self.xmppStream.hostName = kHostName; // 设置服务器端口号 self.xmppStream.hostPort = kHostPort; // 设置代理 [self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; #pragma mark - 管理好友 // 获取管理好友的单例对象 XMPPRosterCoreDataStorage *rosterStorage = [XMPPRosterCoreDataStorage sharedInstance]; // 用管理好友的单例对象初始化Roster花名册 // 好友操作是耗时操作 self.xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:rosterStorage dispatchQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; // 在通道中激活xmppRoster [self.xmppRoster activate:self.xmppStream]; // 设置代理 [self.xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()]; #warning -----------------以下是该方法中新增加的代码----------------------- #pragma mark - 初始化管理归档 // 获取管理消息的存储对象 XMPPMessageArchivingCoreDataStorage *storage = [XMPPMessageArchivingCoreDataStorage sharedInstance]; // 进行消息管理器的初始化 self.xmppMessageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:storage dispatchQueue:dispatch_get_main_queue()]; // 在通道中激活xmppMessageArchiving [self.xmppMessageArchiving activate:self.xmppStream]; // 设置代理 [self.xmppMessageArchiving addDelegate:self delegateQueue:dispatch_get_main_queue()]; // 设置管理上下文 (此时不再从AppDelegate获取) self.context = storage.mainThreadManagedObjectContext; #warning -----------------以上是该方法中新增加的代码----------------------- } return self; }
从好友列表页面点击cell进入聊天界面
RosterListTableViewController.m 好友列表界面新增代码
1 #pragma mark - 点击cell进入聊天界面 2 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 3 ChatTableViewController *chatTVC = [[ChatTableViewController alloc] initWithStyle:UITableViewStylePlain]; 4 // 将当前好友的JID传到聊天界面 5 chatTVC.chatWithJID = self.allRosterArray[indexPath.row]; 6 [self.navigationController pushViewController:chatTVC animated:YES]; 7 }
聊天界面
ChatTableViewController.h
1 #import <UIKit/UIKit.h> 2 #import "XMPPManager.h" 3 4 @interface ChatTableViewController : UITableViewController 5 /// 当前和谁在聊天 6 @property (nonatomic, strong) XMPPJID *chatWithJID; 7 @end
ChatTableViewController.m
1 #import "ChatTableViewController.h" 2 #import "ChatTableViewCell.h" 3 4 @interface ChatTableViewController ()<XMPPStreamDelegate> 5 @property (nonatomic, strong) NSMutableArray *allMessageArray; 6 @end 7 8 @implementation ChatTableViewController 9 10 - (void)viewDidLoad { 11 [super viewDidLoad]; 12 self.allMessageArray = [NSMutableArray array]; 13 // 隐藏分割线 14 self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 15 // 注册cell 16 [self.tableView registerClass:[ChatTableViewCell class] forCellReuseIdentifier:@"chatCell"]; 17 // 发送按钮 18 self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"取消" style:UIBarButtonItemStylePlain target:self action:@selector(cancelAction)]; 19 self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"发送" style:UIBarButtonItemStylePlain target:self action:@selector(sendMessageAction)]; 20 // 取消按钮 21 [[XMPPManager sharedXMPPManager].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; 22 23 // 获取显示消息的方法 24 [self showMessage]; 25 26 } 27 28 #pragma mark - 取消按钮点击事件 29 - (void)cancelAction { 30 // 返回上一界面 31 [self.navigationController popViewControllerAnimated:YES]; 32 } 33 #pragma mark - 发送消息按钮点击事件 34 - (void)sendMessageAction { 35 XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.chatWithJID]; 36 // 设置message的body为固定值 (没有实现发送自定义消息) 37 [message addBody:@"我爱你"]; 38 // 通过通道进行消息发送 39 [[XMPPManager sharedXMPPManager].xmppStream sendElement:message]; 40 } 41 42 #pragma mark - 显示消息 43 - (void)showMessage { 44 // 获取管理对象上下文 45 NSManagedObjectContext *context = [XMPPManager sharedXMPPManager].context; 46 // 初始化请求对象 47 NSFetchRequest *request = [[NSFetchRequest alloc] init]; 48 // 获取实体 49 NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPMessageArchiving_Message_CoreDataObject" inManagedObjectContext:context]; 50 // 设置查询请求的实体 51 [request setEntity:entity]; 52 // 设置谓词查询 (当前用户的jid,对方用户的jid) (根据项目需求而定) 53 request.predicate = [NSPredicate predicateWithFormat:@"streamBareJidStr == %@ AND bareJidStr == %@",[XMPPManager sharedXMPPManager].xmppStream.myJID.bare,self.chatWithJID.bare]; 54 // 按照时间顺序排列 55 NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"timestamp" ascending:YES]; 56 [request setSortDescriptors:@[sort]]; 57 // 执行相关的操作 58 NSArray *resultArray = [context executeFetchRequest:request error:nil]; 59 // 先清空消息数组 (根据项目需求而定) 60 [self.allMessageArray removeAllObjects]; 61 // 将结果数组赋值给消息数组 62 self.allMessageArray = [resultArray mutableCopy]; 63 // 刷新UI 64 [self.tableView reloadData]; 65 // 当前聊天记录跳到最后一行 66 if (self.allMessageArray.count > 0) { 67 NSIndexPath * indexPath = [NSIndexPath indexPathForRow:self.allMessageArray.count - 1 inSection:0]; 68 // 跳到最后一行 69 [self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionBottom]; 70 } 71 [context save:nil]; 72 } 73 74 #pragma mark - Table view data source 75 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 76 return 1; 77 } 78 79 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 80 return self.allMessageArray.count; 81 } 82 83 84 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 85 ChatTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"chatCell" forIndexPath:indexPath]; 86 // 数组里存储的是XMPPMessageArchiving_Message_CoreDataObject对象 87 XMPPMessageArchiving_Message_CoreDataObject *message = [self.allMessageArray objectAtIndex:indexPath.row]; 88 // 设置cell中的相关数据 89 // 根据isOutgoing判断是不是对方发送过来的消息 YES - 对方发送的消息, NO - 自己发送给对方的消息 90 cell.isOut = message.isOutgoing; 91 cell.message = message.body; 92 93 return cell; 94 } 95 96 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 97 // cell的高度并没有自适应 98 return 70; 99 } 100 101 #pragma mark - ------------XMPPStreamDelegate相关代理------------ 102 #pragma mark - 已经发送消息 103 - (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message { 104 // 重新对消息进行操作 105 [self showMessage]; 106 } 107 #pragma mark - 已经接收消息 108 - (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message { 109 // 重新对消息进行操作 110 [self showMessage]; 111 } 112 113 #pragma mark - 消息发送失败 114 - (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error { 115 NSLog(@"消息发送失败"); 116 } 117 118 @end
自定义cell
ChatTableViewCell.h
#import <UIKit/UIKit.h> @interface ChatTableViewCell : UITableViewCell /// 判断是不是对方发送过来的消息 YES - 对方发送的消息, NO - 自己发送给对方的消息 @property (nonatomic, assign) BOOL isOut; /// 消息内容 @property (nonatomic, copy) NSString *message; @end
ChatTableViewCell.m
1 #import "ChatTableViewCell.h" 2 3 @interface ChatTableViewCell () 4 /// 头像 5 @property(nonatomic,strong)UIImageView * headerImageView; 6 /// 消息框背景 7 @property(nonatomic,strong)UIImageView * backgroundImageView; 8 /// 显示每一条聊天内容 9 @property(nonatomic,strong)UILabel * contentLabel; 10 @end 11 12 @implementation ChatTableViewCell 13 14 //使用懒加载创建并初始化所有UI控件 15 - (UILabel *)contentLabel{ 16 if (_contentLabel == nil) { 17 _contentLabel = [[UILabel alloc] init]; 18 } 19 return _contentLabel; 20 } 21 - (UIImageView *)backgroundImageView 22 { 23 if (_backgroundImageView == nil) { 24 _backgroundImageView = [[UIImageView alloc] init]; 25 } 26 return _backgroundImageView; 27 } 28 - (UIImageView *)headerImageView 29 { 30 if (_headerImageView == nil) { 31 _headerImageView = [[UIImageView alloc] init]; 32 } 33 return _headerImageView; 34 } 35 36 37 - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ 38 self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 39 if (self) { 40 //设置cell不能选中 41 self.selectionStyle = UITableViewCellSelectionStyleNone; 42 43 [self.contentView addSubview:self.backgroundImageView]; 44 [self.contentView addSubview:self.headerImageView]; 45 [self.backgroundImageView addSubview:self.contentLabel]; 46 47 } 48 return self; 49 } 50 51 - (void)awakeFromNib { 52 // Initialization code 53 54 55 } 56 57 //重写isOut的setter方法,来设定cell上的不同布局 58 - (void)setIsOut:(BOOL)isOut 59 { 60 _isOut = isOut; 61 CGRect rect = self.frame; 62 if (_isOut) { 63 self.headerImageView.frame = CGRectMake(rect.size.width-50, 10, 50, 50); 64 self.headerImageView.image = [UIImage imageNamed:@"nike"]; 65 }else{ 66 self.headerImageView.frame = CGRectMake(0, 10, 50, 50); 67 self.headerImageView.image = [UIImage imageNamed:@"keji"]; 68 } 69 } 70 //重写message方法,在cell上显示聊天记录 71 - (void)setMessage:(NSString *)message 72 { 73 if (_message != message) { 74 _message = message; 75 self.contentLabel.text = _message; 76 // self.contentLabel.numberOfLines = 0; 77 [self.contentLabel sizeToFit]; 78 79 CGRect rect = self.frame; 80 if (self.isOut) {//发出去的 81 // 消息气泡 82 self.backgroundImageView.image = [[UIImage imageNamed:@"chat_to"] stretchableImageWithLeftCapWidth:45 topCapHeight:40]; 83 self.backgroundImageView.frame = CGRectMake(rect.size.width - self.contentLabel.frame.size.width - 50-20, 10, self.contentLabel.frame.size.width+20, rect.size.height-20); 84 }else{//接收的 85 self.backgroundImageView.image = [[UIImage imageNamed:@"chat_from"] stretchableImageWithLeftCapWidth:45 topCapHeight:40]; 86 self.backgroundImageView.frame = CGRectMake(50, 10,self.contentLabel.frame.size.width+40, rect.size.height-20); 87 } 88 //因为contentLabel已经自适应文字大小,故不用设置宽高,但需要设置位置 89 self.contentLabel.center = CGPointMake(self.backgroundImageView.frame.size.width/2.0, self.backgroundImageView.frame.size.height/2.0); 90 91 } 92 } 93 94 95 @end
以上,一个超级简单的即时通讯工具就已经完成,有些逻辑思维是不固定的,可以根据自己的项目需求去更改代码
还有没有实现到的 图片/语音传输,在这里就只说一下原理
1、将图片/语音上传到服务器
2、根据和服务器的约定,拼好文件在服务器的地址(即图片/语音的URL)
3、调用XMPP发送消息方法,将地址发送出去
4、在接收端接收到的为一条文本信息,里面仅仅是一个指向资源文件的URL地址
5、在拿到URL后进行需要的操作(即请求图片/语音显示到页面上)
UI进阶 即时通讯之XMPP好友列表、添加好友、获取会话内容、简单聊天
标签:
原文地址:http://www.cnblogs.com/fearlessyyp/p/5510470.html