即时通讯之XMPP好友列表、添加好友、获取会话内容、简单聊天
1、好友列表
初始化好友花名册
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 #pragma mark - 添加好友
2 - (void)addFriend;
3
4 #pragma mark - 删除好友
5 - (void)removeFriendWithName:(NSString *)userName;
6
7 #pragma mark - 手动断开连接(注销)
8 - (void)disconnectionToServer;
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 }
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
35 #pragma mark - 添加好友按钮点击事件
36 - (void)addFriendAction {
37
38 [[XMPPManager sharedXMPPManager] addFriend];
39 }
40
41 #pragma mark - 注销按钮点击事件
42 - (void)cancleAction {
43 // 注销
44 [[XMPPManager sharedXMPPManager] disconnectionToServer];
45 [self.navigationController popViewControllerAnimated:YES];
46 }
47
48 #pragma mark - Table view data source
49
50 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
51 return 1;
52 }
53
54 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
55 return self.allRosterArray.count;
56 }
57
58
59 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
60 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"RosterCell" forIndexPath:indexPath];
61
62 // 根据项目情况分析,合理添加判断
63 if (self.allRosterArray.count > 0) {
64 // 获取用户
65 XMPPJID *jid = [self.allRosterArray objectAtIndex:indexPath.row];
66 cell.textLabel.text = jid.user;
67 NSLog(@"bare %@", jid.bare);
68 }
69
70 return cell;
71 }
72
73
74 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
75 return YES;
76 }
77
78 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
79 if (editingStyle == UITableViewCellEditingStyleDelete) {
80 // 删除一个好友
81 XMPPJID *jid = self.allRosterArray[indexPath.row];
82 // 根据名字删除好友
83 [[XMPPManager sharedXMPPManager] removeFriendWithName:jid.user];
84 // 从数组中移除
85 [self.allRosterArray removeObjectAtIndex:indexPath.row];
86 [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
87 } else if (editingStyle == UITableViewCellEditingStyleInsert) {
88
89 }
90 }
91
92 #pragma mark - 点击cell进入聊天界面
93 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
94 ChatTableViewController *chatTVC = [[ChatTableViewController alloc] initWithStyle:UITableViewStylePlain];
95 // 将当前好友的JID传到聊天界面
96 chatTVC.chatWithJID = self.allRosterArray[indexPath.row];
97 [self.navigationController pushViewController:chatTVC animated:YES];
98 }
99
100 #pragma mark - ----------------XMPPRosterDelegate代理方法----------------
101 #pragma mark - 开始获取好友
102 - (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender {
103 NSLog(@"listTVC 开始获取好友 %d", __LINE__);
104 }
105
106 #pragma mark - 结束获取好友
107 - (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender {
108 NSLog(@"listTVC 获取好友结束 %d", __LINE__);
109 // 当前页面是用于显示好友列表的,所以在结束获取好友的代理方法中要进行页面刷新页面,然后将数据显示。
110 // 刷新UI
111 [self.tableView reloadData];
112 }
113
114 #pragma mark - 接收好友信息
115
116 // 获取好友列表时会执行多次,每次获取一个好友信息并将该好友信息添加到数组中
117 // 发送完添加好友请求会执行
118 // 同意别人的好友请求会执行
119 // 删除好友时会执行
120 - (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item {
121 NSLog(@"listTVC 接收好友信息 %d", __LINE__);
122 /**
123 * 好友信息状态有5种
124 both - 互为好友
125 none - 互不为好友
126 to - 请求添加对方为好友,对方还没有同意
127 from - 对方添加我为好友,自己还没有同意
128 remove - 曾经删除的好友
129 */
130
131 // 自己和对方之间的关系
132 NSString *description = [[item attributeForName:@"subscription"] stringValue];
133 NSLog(@"关系%@", description);
134
135 // 显示我的好友
136 if ([description isEqualToString:@"both"]) {
137 // 添加好友
138 // 获取好友的JID
139 NSString *friendJID = [[item attributeForName:@"jid"] stringValue];
140 XMPPJID *jid = [XMPPJID jidWithString:friendJID];
141 // 如果数组中含有这个用户,那么不添加进数组
142 if ([self.allRosterArray containsObject:jid]) {
143 NSLog(@"已经有该好友");
144 return;
145 }
146 // 添加好友到数组中
147 [self.allRosterArray addObject:jid];
148
149 // 在TableView的最后一个cell下面添加这条数据
150 NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.allRosterArray.count - 1 inSection:0];
151 [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
152 }
153 }
154
155 #pragma mark - 接收到添加好友的请求,选择接受or拒绝
156 - (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence {
157 NSLog(@"listTVC 接收添加好友的请求 %d", __LINE__);
158 self.fromJID = presence.from;
159 // 需要相关的提醒框去确定是否接受好友请求
160 UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"请求添加好友" message:@"是否同意" preferredStyle:UIAlertControllerStyleAlert];
161 __weak typeof(self)weakSelf = self;
162 UIAlertAction *acceptAction = [UIAlertAction actionWithTitle:@"同意" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
163 // 添加到花名册
164 [[XMPPManager sharedXMPPManager].xmppRoster acceptPresenceSubscriptionRequestFrom:weakSelf.fromJID andAddToRoster:YES];
165 }];
166 UIAlertAction *rejectAction = [UIAlertAction actionWithTitle:@"拒绝" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
167 [[XMPPManager sharedXMPPManager].xmppRoster rejectPresenceSubscriptionRequestFrom:weakSelf.fromJID];
168 }];
169 [alertController addAction:acceptAction];
170 [alertController addAction:rejectAction];
171 [self presentViewController:alertController animated:YES completion:nil];
172 }
173
174 #pragma mark - ----------------XMPPStreamDelegate代理方法----------------
175 #pragma mark - 判断好友是否处于上线状态
176 - (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence {
177 NSLog(@"listTVC 判断好友是否处于上线状态 %@ %d", presence.status, __LINE__);
178 NSString *type = presence.type;
179 NSString *presenceUser = presence.to.user;
180 // 判断当前用户是否为好友
181 if ([presenceUser isEqualToString:[sender myJID].user]) {
182 if ([type isEqualToString:@"available"]) {
183 NSLog(@"该用户处于上线状态");
184 } else if ([type isEqualToString:@"unavailable"]) {
185 NSLog(@"该用户处于下线状态");
186 }
187 }
188 }
189
190 @end
2、聊天
聊天的规则:
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 // 取消按钮 20 self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"发送" style:UIBarButtonItemStylePlain target:self action:@selector(sendMessageAction)];
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后进行需要的操作(即请求图片/语音显示到页面上)