XMPPManager.h
1 #import <Foundation/Foundation.h> 2 #import "XMPPFramework.h" 3 4 @interface XMPPManager : NSObject 5 6 /** 7 * 创建一个通道的属性 8 */ 9 @property (nonatomic, strong) XMPPStream *xmppStream; 10 11 /** 12 * 管理好友,好友的列表 13 */ 14 @property (nonatomic, strong) XMPPRoster *xmppRoster; 15 16 /** 17 * 和聊天相关的属性 18 */ 19 @property (nonatomic, strong) XMPPMessageArchiving *messageArchiving; 20 21 /** 22 * 消息归档 23 */ 24 @property (nonatomic, strong) NSManagedObjectContext *context; 25 26 27 // 创建单例 28 + (instancetype)shareManager; 29 30 // 登录的方法 31 - (void)loginWithUserName:(NSString *)userName password:(NSString *)password; 32 33 // 注册的方法 34 - (void)registerWithUserName:(NSString *)userName password:(NSString *)password; 35 36 // 添加好友 37 - (void)addFriend; 38 // 删除好友 39 - (void)removeFriendWithName:(NSString *)name; 40 41 @end
XMPPManager.m
1 #import "XMPPManager.h" 2 3 // 列举枚举(登录和注册) 4 typedef NS_ENUM(NSInteger, ConnectToServerPurpose) { 5 ConnectToServerPurposeLogin, 6 ConnectToServerPurposeRegister, 7 }; 8 9 10 @interface XMPPManager () <XMPPStreamDelegate, XMPPRosterDelegate> 11 12 // 登录的属性 13 @property (nonatomic, strong) NSString *userName; // 用户名 14 @property (nonatomic, strong) NSString *password; // 密码 15 16 // 注册的属性 17 @property (nonatomic, strong) NSString *registerUserName; // 用户名 18 @property (nonatomic, strong) NSString *registerPassword; // 密码 19 20 // 记录当前是登录还是注册 21 @property (nonatomic, assign) ConnectToServerPurpose connectToserver; 22 23 // 接收添加好友的名字 24 @property (nonatomic, strong) UITextField *textField; 25 26 @end 27 28 29 static XMPPManager *xmpp = nil; 30 31 @implementation XMPPManager 32 33 // 创建单例 34 + (instancetype)shareManager { 35 36 static dispatch_once_t onceToken; 37 dispatch_once(&onceToken, ^{ 38 xmpp = [[XMPPManager alloc] init]; 39 }); 40 return xmpp; 41 } 42 43 44 #pragma mark - 初始化 45 - (instancetype)init { 46 47 if (self = [super init]) { 48 49 #pragma mark - 初始化通道 50 // 初始化通道对象 51 self.xmppStream = [[XMPPStream alloc] init]; 52 53 // 设置openfire服务器地址 54 self.xmppStream.hostName = kHostName; 55 56 // 设置服务器端口号 57 self.xmppStream.hostPort = kHostPort; 58 59 // 添加代理 60 [self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; 61 62 63 #pragma mark - 管理好友 64 //获取管理好友的单例对象 65 XMPPRosterCoreDataStorage *rosterStorage = [XMPPRosterCoreDataStorage sharedInstance]; 66 67 // 初始化好友列表 68 self.xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:rosterStorage dispatchQueue:dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_BACKGROUND)]; 69 70 // 在通道中激活添加好友 71 [self.xmppRoster activate:self.xmppStream]; 72 73 // 设置花名册代理 74 [self.xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()]; 75 76 77 #pragma mark - 管理消息 78 // 获取管理消息的存储对象 79 XMPPMessageArchivingCoreDataStorage *storage = [XMPPMessageArchivingCoreDataStorage sharedInstance]; 80 81 // 进行消息管理器的初始化 82 self.messageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:storage dispatchQueue:dispatch_get_main_queue()]; 83 84 // 在通道中激活 85 [self.messageArchiving activate:self.xmppStream]; 86 87 // 设置代理 88 [self.messageArchiving addDelegate:self delegateQueue:dispatch_get_main_queue()]; 89 90 // 管理上下文 91 self.context = storage.mainThreadManagedObjectContext; 92 93 } 94 95 return self; 96 } 97 98 99 #pragma mark - 登录和注册的方法 100 // 登录方法的实现 101 - (void)loginWithUserName:(NSString *)userName password:(NSString *)password { 102 103 self.userName = userName; 104 self.password= password; 105 106 // 记录登录状态 107 self.connectToserver = ConnectToServerPurposeLogin; 108 109 // 连接服务器 110 [self linkServer]; 111 } 112 113 114 // 注册方法的实现 115 - (void)registerWithUserName:(NSString *)userName password:(NSString *)password { 116 117 self.registerUserName = userName; 118 self.registerPassword = password; 119 120 // 记录登录状态 121 self.connectToserver = ConnectToServerPurposeRegister; 122 123 // 连接服务器 124 [self linkServer]; 125 } 126 127 128 #pragma mark - 连接服务器 129 - (void)linkServer { 130 131 // 判断是登录还是注册 132 NSString *string = [[NSString alloc] init]; 133 switch (self.connectToserver) { 134 case ConnectToServerPurposeLogin: 135 string = [NSString stringWithFormat:@"%@", self.userName]; 136 break; 137 case ConnectToServerPurposeRegister: 138 string = [NSString stringWithFormat:@"%@", self.registerUserName]; 139 break; 140 141 default: 142 break; 143 } 144 145 // 要链接服务器,要有用户认证 146 // 身份证 147 // 参数1:用户名 148 // 参数2:域名 149 // 参数3:资源 150 // 通过通道进行身份验证 151 XMPPJID *jid = [XMPPJID jidWithUser:string domain:kDomin resource:kResource]; 152 self.xmppStream.myJID = jid; 153 154 // 如果当前聊天工具处于链接状态或者已经链接了,此时,你需要去切断链接,【此逻辑不唯一】 155 if ([self.xmppStream isConnected] || [self.xmppStream isConnecting]) { 156 [self disconnectToServer]; 157 } 158 159 // 链接超时 160 NSError *error = nil; 161 [self.xmppStream connectWithTimeout:5 error:&error]; 162 163 if (error) { 164 NSLog(@"服务器链接超时"); 165 } 166 } 167 168 169 // 断开链接 170 - (void)disconnectToServer { 171 172 // 当前用户下线了 173 XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"]; 174 175 // 告诉通道下线了 176 [self.xmppStream sendElement:presence]; 177 178 // 使通道失去链接 179 [self.xmppStream disconnect]; 180 } 181 182 183 #pragma mark - XMPPStreamDelegate协议方法 184 #pragma mark - 链接超时 185 - (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender { 186 187 NSLog(@"连接超时"); 188 } 189 190 191 #pragma mark - 连接成功 192 - (void)xmppStreamDidConnect:(XMPPStream *)sender { 193 194 NSLog(@"连接成功"); 195 196 // 区分登录和注册进行密码验证 197 switch (self.connectToserver) { 198 case ConnectToServerPurposeLogin: 199 // 登录密码的验证 200 [self.xmppStream authenticateWithPassword:self.password error:nil]; 201 break; 202 case ConnectToServerPurposeRegister: 203 // 注册密码的验证 204 [self.xmppStream registerWithPassword:self.registerPassword error:nil]; 205 break; 206 207 default: 208 break; 209 } 210 } 211 212 213 #pragma mark - 断开链接 214 - (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error { 215 216 // 1.自己手动断开 217 // 2.被动断开 218 if (error) { 219 NSLog(@"断开链接,error = %@", error); 220 } 221 } 222 223 224 #pragma mark - 认证失败(登录失败) 225 - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error { 226 227 NSLog(@"认证失败, error = %@", error); 228 } 229 230 231 #pragma mark - 认证成功(登录成功) 232 - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender { 233 234 // 上线 235 XMPPPresence *presennce = [XMPPPresence presenceWithType:@"available"]; 236 [self.xmppStream sendElement:presennce]; 237 238 NSLog(@"登录成功"); 239 } 240 241 242 #pragma mark - 添加好友 243 - (void)addFriend { 244 245 UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"添加好友" message:@"请输入好友的姓名" preferredStyle:UIAlertControllerStyleAlert]; 246 247 __weak typeof(self)mySelf = self; 248 [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 249 mySelf.textField = textField; 250 }]; 251 252 UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { 253 NSLog(@"取消添加好友"); 254 }]; 255 UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 256 // 使用JID记录 257 XMPPJID *friendJID = [XMPPJID jidWithUser:mySelf.textField.text domain:kDomin resource:kResource]; 258 259 // 监听好友的动作 260 [mySelf.xmppRoster subscribePresenceToUser:friendJID]; 261 NSLog(@"===============%@", mySelf.textField.text); 262 // 添加好友 263 [mySelf.xmppRoster addUser:friendJID withNickname:mySelf.textField.text]; 264 }]; 265 266 [alertController addAction:action1]; 267 [alertController addAction:action2]; 268 269 [[self getCurrentVC] presentViewController:alertController animated:YES completion:nil]; 270 } 271 272 273 //获取当前屏幕显示的viewcontroller 274 - (UIViewController *)getCurrentVC 275 { 276 UIViewController *result = nil; 277 278 UIWindow * window = [[UIApplication sharedApplication] keyWindow]; 279 if (window.windowLevel != UIWindowLevelNormal) 280 { 281 NSArray *windows = [[UIApplication sharedApplication] windows]; 282 for(UIWindow * tmpWin in windows) 283 { 284 if (tmpWin.windowLevel == UIWindowLevelNormal) 285 { 286 window = tmpWin; 287 break; 288 } 289 } 290 } 291 292 UIView *frontView = [[window subviews] objectAtIndex:0]; 293 id nextResponder = [frontView nextResponder]; 294 295 if ([nextResponder isKindOfClass:[UIViewController class]]) 296 result = nextResponder; 297 else 298 result = window.rootViewController; 299 300 return result; 301 } 302 303 304 #pragma mark - 删除好友 305 - (void)removeFriendWithName:(NSString *)name { 306 307 // 使用JID记录要删除的用户 308 XMPPJID *friendJID = [XMPPJID jidWithUser:name domain:kDomin resource:kResource]; 309 310 // 停止监听好友 311 [self.xmppRoster unsubscribePresenceFromUser:friendJID]; 312 313 // 删除好友 314 [self.xmppRoster removeUser:friendJID]; 315 } 316 317 318 #pragma mark - XMPPRosterDelegate代理方法 319 // 开始获取好友 320 - (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender { 321 322 NSLog(@"开始获取好友"); 323 } 324 325 326 // 结束获取好友 327 - (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender { 328 329 NSLog(@"结束获取好友"); 330 } 331 332 333 // 接受好友的信息 334 - (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item { 335 336 // 好友状态的显示信息 337 NSLog(@"好友信息====%@", item); 338 } 339 340 341 // 收到好友监听请求,是同意还是拒绝 342 - (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence { 343 344 NSLog(@"获取好友请求"); 345 } 346 347 @end
LoginViewController.m
1 #import "LoginViewController.h" 2 #import "Masonry.h" 3 #import "RegisterViewController.h" 4 #import "XMPPManager.h" 5 #import "RosterTableViewController.h" 6 7 @interface LoginViewController () <XMPPStreamDelegate> 8 9 /** 10 * 姓名输入框 11 */ 12 @property (nonatomic, strong) UITextField *userTextField; 13 14 /** 15 * 密码输入框 16 */ 17 @property (nonatomic, strong) UITextField *passwordTextField; 18 19 /** 20 * 登录按钮 21 */ 22 @property (nonatomic, strong) UIButton *loginButton; 23 24 /** 25 * 注册按钮 26 */ 27 @property (nonatomic, strong) UIButton *registerButton; 28 29 @end 30 31 @implementation LoginViewController 32 33 - (void)viewDidLoad { 34 [super viewDidLoad]; 35 // Do any additional setup after loading the view. 36 37 self.navigationItem.title = @"首页"; 38 self.view.backgroundColor = [UIColor orangeColor]; 39 40 41 // 布局页面 42 [self initLayout]; 43 44 45 // 给登录和注册按钮添加事件 46 [_loginButton addTarget:self action:@selector(loginClick:) forControlEvents:UIControlEventTouchUpInside]; 47 [_registerButton addTarget:self action:@selector(registerClick:) forControlEvents:UIControlEventTouchUpInside]; 48 49 50 // 设置代理 51 [[XMPPManager shareManager].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; 52 53 } 54 55 56 #pragma mark - 登录和注册按钮的点击事件 57 // 登录按钮的点击事件 58 - (void)loginClick:(UIButton *)sender { 59 60 [[XMPPManager shareManager] loginWithUserName:self.userTextField.text password:self.passwordTextField.text]; 61 } 62 63 64 // 注册按钮的点击事件 65 - (void)registerClick:(UIButton *)sender { 66 67 RegisterViewController *registerVC = [[RegisterViewController alloc] init]; 68 [self.navigationController pushViewController:registerVC animated:YES]; 69 } 70 71 72 #pragma mark - XMPPStreamDelegate代理方法 73 // 登录成功 74 - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender { 75 76 NSLog(@"登录成功"); 77 78 RosterTableViewController *rosterVC = [[RosterTableViewController alloc] init]; 79 [self.navigationController pushViewController:rosterVC animated:YES]; 80 } 81 82 // 登录失败 83 - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error { 84 85 NSLog(@"登录失败"); 86 } 87 88 89 #pragma mark - 布局页面 90 - (void)initLayout { 91 92 // 创建子控件 93 _userTextField = [[UITextField alloc] init]; 94 _userTextField.borderStyle = UITextBorderStyleRoundedRect; 95 _userTextField.layer.cornerRadius = 5; 96 _userTextField.backgroundColor = [UIColor whiteColor]; 97 _userTextField.placeholder = @"请输入用户名"; 98 [self.view addSubview:_userTextField]; 99 100 _passwordTextField = [[UITextField alloc] init]; 101 _passwordTextField.borderStyle = UITextBorderStyleRoundedRect; 102 _passwordTextField.layer.cornerRadius = 5; 103 _passwordTextField.backgroundColor = [UIColor whiteColor]; 104 _passwordTextField.placeholder = @"请输入密码"; 105 [self.view addSubview:_passwordTextField]; 106 107 _loginButton = [UIButton buttonWithType:UIButtonTypeSystem]; 108 _loginButton.layer.cornerRadius = 5; 109 [_loginButton setTitle:@"登 录" forState:UIControlStateNormal]; 110 [_loginButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 111 _loginButton.backgroundColor = [UIColor whiteColor]; 112 [self.view addSubview:_loginButton]; 113 114 _registerButton = [UIButton buttonWithType:UIButtonTypeSystem]; 115 _registerButton.layer.cornerRadius = 5; 116 [_registerButton setTitle:@"注 册" forState:UIControlStateNormal]; 117 [_registerButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 118 _registerButton.backgroundColor = [UIColor whiteColor]; 119 [self.view addSubview:_registerButton]; 120 121 122 // 设置frame 123 [_userTextField mas_makeConstraints:^(MASConstraintMaker *make) { 124 125 make.top.offset(120); 126 make.left.offset(50); 127 make.right.offset(-50); 128 make.height.mas_equalTo(50); 129 130 }]; 131 132 [_passwordTextField mas_makeConstraints:^(MASConstraintMaker *make) { 133 134 make.top.equalTo(_userTextField.mas_bottom).offset(50); 135 make.left.offset(50); 136 make.right.offset(-50); 137 make.height.mas_equalTo(50); 138 139 }]; 140 141 [_loginButton mas_makeConstraints:^(MASConstraintMaker *make) { 142 143 make.top.equalTo(_passwordTextField.mas_bottom).offset(50); 144 make.left.offset(80); 145 make.width.mas_equalTo((self.view.frame.size.width - 80 - 80 - 50) / 2); 146 make.height.mas_equalTo(50); 147 148 }]; 149 150 [_registerButton mas_makeConstraints:^(MASConstraintMaker *make) { 151 152 make.top.equalTo(_loginButton.mas_top); 153 make.right.offset(-80); 154 make.width.mas_equalTo((self.view.frame.size.width - 80 - 80 - 50) / 2); 155 make.height.mas_equalTo(50); 156 157 }]; 158 } 159 160 @end
RegisterViewController.m
1 #import "RegisterViewController.h" 2 #import "Masonry.h" 3 #import "XMPPManager.h" 4 5 @interface RegisterViewController () <XMPPStreamDelegate> 6 7 /** 8 * 姓名输入框 9 */ 10 @property (nonatomic, strong) UITextField *userTextField; 11 12 /** 13 * 密码输入框 14 */ 15 @property (nonatomic, strong) UITextField *passwordTextField; 16 17 /** 18 * 注册按钮 19 */ 20 @property (nonatomic, strong) UIButton *registerButton; 21 22 @end 23 24 @implementation RegisterViewController 25 26 - (void)viewDidLoad { 27 [super viewDidLoad]; 28 // Do any additional setup after loading the view. 29 30 31 self.navigationItem.title = @"注册"; 32 self.view.backgroundColor = [UIColor purpleColor]; 33 34 35 // 页面布局 36 [self initLayout]; 37 38 39 // 设置代理 40 [[XMPPManager shareManager].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; 41 42 43 // 注册按钮的点击事件 44 [_registerButton addTarget:self action:@selector(registerClick:) forControlEvents:UIControlEventTouchUpInside]; 45 46 } 47 48 49 #pragma mark - 注册按钮点击事件 50 - (void)registerClick:(UIButton *)sender { 51 52 [[XMPPManager shareManager] registerWithUserName:self.userTextField.text password:self.passwordTextField.text]; 53 } 54 55 56 #pragma mark - XMPPStreamDelegate实现协议方法 57 // 实现协议是为了实现需求:注册成功之后dismiss到登录界面 58 // 注册成功 59 - (void)xmppStreamDidRegister:(XMPPStream *)sender { 60 61 NSLog(@"注册成功"); 62 [self.navigationController popViewControllerAnimated:YES]; 63 } 64 65 66 // 注册失败 67 - (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error { 68 69 NSLog(@"注册失败 = %@", error); 70 } 71 72 73 #pragma mark - 布局页面 74 - (void)initLayout { 75 76 // 创建子控件 77 _userTextField = [[UITextField alloc] init]; 78 _userTextField.borderStyle = UITextBorderStyleRoundedRect; 79 _userTextField.layer.cornerRadius = 5; 80 _userTextField.backgroundColor = [UIColor whiteColor]; 81 _userTextField.placeholder = @"请输入用户名"; 82 [self.view addSubview:_userTextField]; 83 84 _passwordTextField = [[UITextField alloc] init]; 85 _passwordTextField.borderStyle = UITextBorderStyleRoundedRect; 86 _passwordTextField.layer.cornerRadius = 5; 87 _passwordTextField.backgroundColor = [UIColor whiteColor]; 88 _passwordTextField.placeholder = @"请输入密码"; 89 [self.view addSubview:_passwordTextField]; 90 91 _registerButton = [UIButton buttonWithType:UIButtonTypeSystem]; 92 _registerButton.layer.cornerRadius = 5; 93 [_registerButton setTitle:@"注 册" forState:UIControlStateNormal]; 94 [_registerButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 95 _registerButton.backgroundColor = [UIColor whiteColor]; 96 [self.view addSubview:_registerButton]; 97 98 99 // 设置frame 100 [_userTextField mas_makeConstraints:^(MASConstraintMaker *make) { 101 102 make.top.offset(120); 103 make.left.offset(50); 104 make.right.offset(-50); 105 make.height.mas_equalTo(50); 106 107 }]; 108 109 [_passwordTextField mas_makeConstraints:^(MASConstraintMaker *make) { 110 111 make.top.equalTo(_userTextField.mas_bottom).offset(50); 112 make.left.offset(50); 113 make.right.offset(-50); 114 make.height.mas_equalTo(50); 115 116 }]; 117 118 [_registerButton mas_makeConstraints:^(MASConstraintMaker *make) { 119 120 make.top.equalTo(_passwordTextField.mas_bottom).offset(50); 121 make.left.offset(150); 122 make.right.offset(-150); 123 make.height.mas_equalTo(50); 124 125 }]; 126 } 127 128 @end
RosterTableViewController.m
1 #import "RosterTableViewController.h" 2 #import "XMPPManager.h" 3 #import "ChatTableViewController.h" 4 5 @interface RosterTableViewController () <XMPPStreamDelegate, XMPPRosterDelegate> 6 7 /** 8 * 存储好友列表的数组 9 */ 10 @property (nonatomic, strong) NSMutableArray *allRosterArray; 11 12 /** 13 * 用来存储发送请求者的JID 14 */ 15 @property (nonatomic, strong) XMPPJID *fromJID; 16 17 18 @end 19 20 @implementation RosterTableViewController 21 22 // 懒加载 23 - (NSMutableArray *)allRosterArray { 24 25 if (!_allRosterArray) { 26 _allRosterArray = [NSMutableArray array]; 27 } 28 return _allRosterArray; 29 } 30 31 32 - (void)viewDidLoad { 33 [super viewDidLoad]; 34 35 36 self.navigationItem.title = @"好友列表"; 37 38 39 // 设置代理 40 [[XMPPManager shareManager].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; 41 [[XMPPManager shareManager].xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()]; 42 43 44 // 注册cell 45 [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; 46 47 48 // 设置添加按钮和返回按钮 49 self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addFriendAction:)]; 50 self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelAction:)]; 51 52 } 53 54 55 #pragma mark - 添加和注销按钮的实现 56 // 添加按钮的方法实现 57 - (void)addFriendAction:(UIBarButtonItem *)sender { 58 59 [[XMPPManager shareManager] addFriend]; 60 } 61 62 // 注销按钮的方法实现 63 - (void)cancelAction:(UIBarButtonItem *)sender { 64 65 [self.navigationController popViewControllerAnimated:YES]; 66 } 67 68 69 #pragma mark - 设置cell 70 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 71 72 return self.allRosterArray.count; 73 } 74 75 76 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 77 78 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; 79 // 根据情况分析,合理添加判断 80 if (self.allRosterArray.count > 0) { 81 //获取用户 82 XMPPJID *jid = self.allRosterArray[indexPath.row]; 83 cell.textLabel.text = jid.user; 84 } 85 86 return cell; 87 } 88 89 90 #pragma mark - 编辑页面(删除) 91 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { 92 93 return YES; 94 } 95 96 97 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { 98 if (editingStyle == UITableViewCellEditingStyleDelete) { 99 // 处理要删除的数据 100 // 要删除用户的jid 101 XMPPJID *jid = self.allRosterArray[indexPath.row]; 102 [[XMPPManager shareManager] removeFriendWithName:jid.user]; 103 [self.allRosterArray removeObjectAtIndex:indexPath.row]; 104 [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; 105 } 106 } 107 108 109 #pragma mark - XMPPRosterDelegate代理方法 110 // 开始获取好友 111 - (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender { 112 113 NSLog(@"开始获取好友"); 114 } 115 116 // 结束获取好友 117 - (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender { 118 119 // 当前页面适用于显示好友列表的,所以在结束好友获取的代理方法中要进行刷新页面,然后将数据显示 120 // 刷新页面 121 [self.tableView reloadData]; 122 } 123 124 125 // 接收好友的信息 126 // 这个代理方法会被执行多次,每添加完好友,相应的好友信息都需要反馈 127 - (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item { 128 129 /* 130 both 互为好友 131 none 互不为好友 132 to 我已添加对方为好友,但是对方还没有接收 133 from 对方已添加自己为好友,但是自己还没有接收 134 remove 曾经删除的好友 135 */ 136 // 描述自己和对方之间的关系 137 NSString *description = [[item attributeForName:@"subscription"] stringValue]; 138 NSLog(@"description = %@", description); 139 140 if ([description isEqualToString:@"none"] || [description isEqualToString:@"to"] || [description isEqualToString:@"from"] || [description isEqualToString:@"both"]) { 141 142 // 添加好友 143 // 获取要添加好友的JID 144 NSString *friendJID = [[item attributeForName:@"jid"] stringValue]; 145 XMPPJID *jid = [XMPPJID jidWithString:friendJID]; 146 147 // 如果数组中含有这个用户就不用进行接下来的操作 148 if ([self.allRosterArray containsObject:jid]) { 149 return; 150 } 151 152 // 添加好友到数组中 153 [self.allRosterArray addObject:jid]; 154 155 // 在tableView中添加这条数据 156 NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.allRosterArray.count - 1 inSection:0]; 157 [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight]; 158 } 159 } 160 161 162 // 对方好友是同意还是拒绝 163 - (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence { 164 165 self.fromJID = presence.from; 166 // 需要相关的提醒框去确定是否接受 167 168 UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"好友请求" message:@"是否接受好友请求" preferredStyle:UIAlertControllerStyleAlert]; 169 170 __weak typeof(self)weakSelf = self; 171 UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"接受" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 172 // 在花名册中去接受好友 173 [[XMPPManager shareManager].xmppRoster acceptPresenceSubscriptionRequestFrom:weakSelf.fromJID andAddToRoster:YES]; 174 }]; 175 UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"拒绝" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { 176 // 在花名册中拒绝好友 177 [[XMPPManager shareManager].xmppRoster rejectPresenceSubscriptionRequestFrom:weakSelf.fromJID]; 178 }]; 179 180 [alertController addAction:action1]; 181 [alertController addAction:action2]; 182 183 [self presentViewController:alertController animated:YES completion:^{ 184 185 }]; 186 } 187 188 189 #pragma mark - XMPPStreamDelegate代理方法 190 // 获取好友是否在线 191 - (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence { 192 193 NSLog(@"status = %@", presence.status); 194 195 NSString *type = presence.type; 196 NSString *presenceOfUser = presence.to.user; 197 198 // 判断当前用户是否为你的好友 199 if ([presenceOfUser isEqualToString:[sender myJID].user]) { 200 if ([type isEqualToString:@"avaialble"]) { 201 NSLog(@"该用户处于上线状态"); 202 } else if([type isEqualToString:@"uianaialble"]) { 203 NSLog(@"该用户处于下线状态"); 204 } 205 } 206 207 } 208 209 210 #pragma mark - 点击cell进入聊天界面 211 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 212 213 ChatTableViewController *chatVC = [[ChatTableViewController alloc] init]; 214 XMPPJID *jid = self.allRosterArray[indexPath.row]; 215 216 chatVC.chatToJID = jid; 217 [self.navigationController pushViewController:chatVC animated:YES]; 218 } 219 220 @end
ChatTableViewController.h
1 #import <UIKit/UIKit.h> 2 #import "XMPPManager.h" 3 4 @interface ChatTableViewController : UITableViewController 5 6 /** 7 * 当前和谁进行聊天 8 */ 9 @property (nonatomic, strong) XMPPJID *chatToJID; 10 11 @end
ChatTableViewController.m
1 #import "ChatTableViewController.h" 2 #import "ChatCell.h" 3 4 @interface ChatTableViewController () <XMPPStreamDelegate> 5 6 /** 7 * 存储消息 8 */ 9 @property (nonatomic, strong) NSMutableArray *allMessageArray; 10 11 @end 12 13 @implementation ChatTableViewController 14 15 // 懒加载 16 - (NSMutableArray *)allMessageArray { 17 18 if (!_allMessageArray) { 19 _allMessageArray = [NSMutableArray array]; 20 } 21 return _allMessageArray; 22 } 23 24 25 - (void)viewDidLoad { 26 [super viewDidLoad]; 27 28 // 隐藏tableView每一行的线 29 self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 30 31 32 // 设置代理 33 [[XMPPManager shareManager].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; 34 35 36 // 获取显示消息的方法 37 [self showMessage]; 38 39 40 // 注册cell 41 [self.tableView registerClass:[ChatCell class] forCellReuseIdentifier:@"cell"]; 42 43 44 // 写两个按钮,一个用于发送,一个用于取消 45 self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"发送" style:UIBarButtonItemStylePlain target:self action:@selector(sureAction)]; 46 self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"取消" style:UIBarButtonItemStylePlain target:self action:@selector(cancelAction)]; 47 } 48 49 50 #pragma mark - 按钮的发送方法和取消方法 51 // 发送方法 52 - (void)sureAction { 53 54 // 设置message的body 55 XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.chatToJID]; 56 57 [message addBody:@"我爱你"]; 58 59 // 通过通道进行消息发送 60 [[XMPPManager shareManager].xmppStream sendElement:message]; 61 } 62 63 // 取消方法 64 - (void)cancelAction { 65 66 [self.navigationController popViewControllerAnimated:YES]; 67 } 68 69 70 #pragma mark - 显示消息 71 - (void)showMessage { 72 73 // 获取管理上下文 74 NSManagedObjectContext *context = [XMPPManager shareManager].context; 75 76 // 初始化请求对象 77 NSFetchRequest *request = [[NSFetchRequest alloc] init]; 78 79 // 获取实体 80 NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPMessageArchiving_Message_CoreDataObject" inManagedObjectContext:context]; 81 82 // 设置查询请求的实体 83 [request setEntity:entity]; 84 85 // 设置谓词查询(当前用户的JID,对方用户的JID) 86 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"streamBareJidStr == %@ AND bareJidStr == %@",[XMPPManager shareManager].xmppStream.myJID.bare,self.chatToJID.bare]; 87 88 [request setPredicate:predicate]; 89 90 // 按照时间顺序进行排列 91 NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"timestamp" ascending:YES]; 92 93 // 设置排序 94 [request setSortDescriptors:@[sort]]; 95 96 // 执行相关的操作 97 NSArray *resultArray = [context executeFetchRequest:request error:nil]; 98 99 // 先清空消息数组 100 [self.allMessageArray removeAllObjects]; 101 102 // 在进行添加context执行的结果数组 103 [self.allMessageArray addObjectsFromArray:resultArray]; 104 105 // 刷新UI 106 [self.tableView reloadData]; 107 108 // 当前聊天记录跳到最后一行 109 if (self.allMessageArray.count > 0) { 110 NSIndexPath * indexPath = [NSIndexPath indexPathForRow:self.allMessageArray.count-1 inSection:0]; 111 // 跳到最后一行 112 [self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionBottom]; 113 } 114 } 115 116 117 #pragma mark - 设置cell 118 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 119 120 return 1; 121 } 122 123 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 124 125 return self.allMessageArray.count; 126 } 127 128 129 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 130 131 ChatCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; 132 133 // 千万要记住数组里存储的message对象是XMPPMessageArchiving_Message_CoreDataObject类型的 134 XMPPMessageArchiving_Message_CoreDataObject *message = [self.allMessageArray objectAtIndex:indexPath.row]; 135 136 // 设置cell的相关数据 137 cell.isOut = message.isOutgoing; 138 cell.message = message.body; 139 140 141 return cell; 142 } 143 144 145 // 设置cell高度 146 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 147 148 return 70; 149 } 150 151 152 #pragma mark - XMPPStreamDelegate的相关代理方法 153 // 发送消息成功 154 - (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message { 155 156 // 重新显示相关信息 157 [self showMessage]; 158 } 159 160 // 接受消息成功 161 - (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message { 162 163 // 重新显示相关信息 164 [self showMessage]; 165 } 166 167 168 // 发送消息失败 169 - (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error { 170 171 NSLog(@"发送消息失败"); 172 } 173 174 @end
ChatCell.h
1 #import <UIKit/UIKit.h> 2 3 @interface ChatCell : UITableViewCell 4 5 /** 6 * 判断是对方发送过来的消息,还是自己发出的消息 7 */ 8 @property (nonatomic, assign) BOOL isOut; 9 10 /** 11 * 消息内容 12 */ 13 @property (nonatomic, strong) NSString *message; 14 15 @end
ChatCell.m
1 #import "ChatCell.h" 2 3 @interface ChatCell () 4 5 @property(nonatomic,strong)UIImageView * headerImageView; 6 @property(nonatomic,strong)UIImageView * backgroundImageView; 7 8 /** 9 * 显示每一条聊天内容 10 */ 11 @property(nonatomic,strong)UILabel * contentLabel; 12 13 @end 14 15 16 @implementation ChatCell 17 18 //使用懒加载创建并初始化所有UI控件 19 - (UILabel *)contentLabel{ 20 if (_contentLabel == nil) { 21 _contentLabel = [[UILabel alloc] init]; 22 } 23 return _contentLabel; 24 } 25 - (UIImageView *)backgroundImageView 26 { 27 if (_backgroundImageView == nil) { 28 _backgroundImageView = [[UIImageView alloc] init]; 29 } 30 return _backgroundImageView; 31 } 32 - (UIImageView *)headerImageView 33 { 34 if (_headerImageView == nil) { 35 _headerImageView = [[UIImageView alloc] init]; 36 } 37 return _headerImageView; 38 } 39 40 41 - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ 42 self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 43 if (self) { 44 //设置cell不能选中 45 self.selectionStyle = UITableViewCellSelectionStyleNone; 46 47 [self.contentView addSubview:self.backgroundImageView]; 48 [self.contentView addSubview:self.headerImageView]; 49 [self.backgroundImageView addSubview:self.contentLabel]; 50 } 51 return self; 52 } 53 54 55 //重写isOut的setter方法,来设定cell上的不同布局 56 - (void)setIsOut:(BOOL)isOut 57 { 58 _isOut = isOut; 59 CGRect rect = self.frame; 60 if (_isOut) { 61 self.headerImageView.frame = CGRectMake(rect.size.width-50, 10, 50, 50); 62 self.headerImageView.image = [UIImage imageNamed:@"xiaoming"]; 63 }else{ 64 self.headerImageView.frame = CGRectMake(0, 10, 50, 50); 65 self.headerImageView.image = [UIImage imageNamed:@"angel"]; 66 } 67 } 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 self.backgroundImageView.image = [[UIImage imageNamed:@"chat_to"] stretchableImageWithLeftCapWidth:45 topCapHeight:40]; 82 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); 83 }else{//接收的 84 self.backgroundImageView.image = [[UIImage imageNamed:@"chat_from"] stretchableImageWithLeftCapWidth:45 topCapHeight:40]; 85 self.backgroundImageView.frame = CGRectMake(50, 10,self.contentLabel.frame.size.width+40, rect.size.height-20); 86 } 87 //因为contentLabel已经自适应文字大小,故不用设置宽高,但需要设置位置 88 self.contentLabel.center = CGPointMake(self.backgroundImageView.frame.size.width/2.0, self.backgroundImageView.frame.size.height/2.0); 89 90 } 91 } 92 93 @end