你的位置:首页 > 操作系统

[操作系统]UI进阶 即时通讯之XMPP好友列表、添加好友、获取会话内容、简单聊天

UI进阶 即时通讯之XMPP好友列表、添加好友、获取会话内容、简单聊天

这篇博客的代码是直接在上篇博客的基础上增加的,先给出部分代码,最后会给出能实现简单功能的完整代码。

UI进阶 即时通讯之XMPP登录、注册

 

1、好友列表

初始化好友花名册

#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()];

XMPPRoster代理方法

  好友列表

 

  删除好友

 


RosterListTableViewController.m 好友列表显示页面

#import "RosterListTableViewController.h"#import "XMPPManager.h"#import "ChatTableViewController.h"@interface RosterListTableViewController ()<XMPPRosterDelegate, XMPPStreamDelegate>/// 好友列表@property (nonatomic, strong) NSMutableArray *allRosterArray;/// 用来存储发送好友请求者的JID@property (nonatomic, strong) XMPPJID *fromJID;@end@implementation RosterListTableViewController- (void)viewDidLoad {  [super viewDidLoad];  // 初始化数组  self.allRosterArray = [NSMutableArray array];    [[XMPPManager sharedXMPPManager].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];  [[XMPPManager sharedXMPPManager].xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];  self.title = @"好友列表";    // 添加按钮  self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addFriendAction)];  // 返回按钮  self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"注销" style:UIBarButtonItemStylePlain target:self action:@selector(cancleAction)];    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"RosterCell"];}#pragma mark - 添加好友按钮点击事件- (void)addFriendAction {    [[XMPPManager sharedXMPPManager] addFriend];}#pragma mark - 注销按钮点击事件- (void)cancleAction {  // 注销  [[XMPPManager sharedXMPPManager] disconnectionToServer];  [self.navigationController popViewControllerAnimated:YES];}#pragma mark - Table view data source- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {  return 1;}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {  return self.allRosterArray.count;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"RosterCell" forIndexPath:indexPath];    // 根据项目情况分析,合理添加判断  if (self.allRosterArray.count > 0) {    // 获取用户    XMPPJID *jid = [self.allRosterArray objectAtIndex:indexPath.row];    cell.textLabel.text = jid.user;    NSLog(@"bare %@", jid.bare);  }    return cell;}- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {  return YES;}- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {  if (editingStyle == UITableViewCellEditingStyleDelete) {    // 删除一个好友    XMPPJID *jid = self.allRosterArray[indexPath.row];    // 根据名字删除好友    [[XMPPManager sharedXMPPManager] removeFriendWithName:jid.user];    // 从数组中移除    [self.allRosterArray removeObjectAtIndex:indexPath.row];    [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];  } else if (editingStyle == UITableViewCellEditingStyleInsert) {    }  }#pragma mark - 点击cell进入聊天界面- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {  ChatTableViewController *chatTVC = [[ChatTableViewController alloc] initWithStyle:UITableViewStylePlain];  // 将当前好友的JID传到聊天界面  chatTVC.chatWithJID = self.allRosterArray[indexPath.row];  [self.navigationController pushViewController:chatTVC animated:YES];}#pragma mark - ----------------XMPPRosterDelegate代理方法----------------#pragma mark - 开始获取好友- (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender {  NSLog(@"listTVC 开始获取好友 %d", __LINE__);}#pragma mark - 结束获取好友- (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender {  NSLog(@"listTVC 获取好友结束 %d", __LINE__);  // 当前页面是用于显示好友列表的,所以在结束获取好友的代理方法中要进行页面刷新页面,然后将数据显示。  // 刷新UI  [self.tableView reloadData];}#pragma mark - 接收好友信息// 获取好友列表时会执行多次,每次获取一个好友信息并将该好友信息添加到数组中// 发送完添加好友请求会执行// 同意别人的好友请求会执行// 删除好友时会执行- (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DD)item {  NSLog(@"listTVC 接收好友信息 %d", __LINE__);  /**   * 好友信息状态有5种    both - 互为好友    none - 互不为好友    to - 请求添加对方为好友,对方还没有同意    from - 对方添加我为好友,自己还没有同意    remove - 曾经删除的好友   */    // 自己和对方之间的关系  NSString *description = [[item attributeForName:@"subscription"] stringValue];  NSLog(@"关系%@", description);  // 显示我的好友  if ([description isEqualToString:@"both"]) {    // 添加好友    // 获取好友的JID    NSString *friendJID = [[item attributeForName:@"jid"] stringValue];    XMPPJID *jid = [XMPPJID jidWithString:friendJID];    // 如果数组中含有这个用户,那么不添加进数组    if ([self.allRosterArray containsObject:jid]) {      NSLog(@"已经有该好友");      return;    }    // 添加好友到数组中    [self.allRosterArray addObject:jid];    // 在TableView的最后一个cell下面添加这条数据    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.allRosterArray.count - 1 inSection:0];    [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];  }}#pragma mark - 接收到添加好友的请求,选择接受or拒绝- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence {  NSLog(@"listTVC 接收添加好友的请求 %d", __LINE__);  self.fromJID = presence.from;  // 需要相关的提醒框去确定是否接受好友请求  UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"请求添加好友" message:@"是否同意" preferredStyle:UIAlertControllerStyleAlert];  __weak typeof(self)weakSelf = self;  UIAlertAction *acceptAction = [UIAlertAction actionWithTitle:@"同意" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {    // 添加到花名册    [[XMPPManager sharedXMPPManager].xmppRoster acceptPresenceSubscriptionRequestFrom:weakSelf.fromJID andAddToRoster:YES];  }];  UIAlertAction *rejectAction = [UIAlertAction actionWithTitle:@"拒绝" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {    [[XMPPManager sharedXMPPManager].xmppRoster rejectPresenceSubscriptionRequestFrom:weakSelf.fromJID];  }];  [alertController addAction:acceptAction];  [alertController addAction:rejectAction];  [self presentViewController:alertController animated:YES completion:nil];}#pragma mark - ----------------XMPPStreamDelegate代理方法----------------#pragma mark - 判断好友是否处于上线状态- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence {  NSLog(@"listTVC 判断好友是否处于上线状态 %@ %d", presence.status, __LINE__);  NSString *type = presence.type;  NSString *presenceUser = presence.to.user;  // 判断当前用户是否为好友  if ([presenceUser isEqualToString:[sender myJID].user]) {    if ([type isEqualToString:@"available"]) {      NSLog(@"该用户处于上线状态");    } else if ([type isEqualToString:@"unavailable"]) {      NSLog(@"该用户处于下线状态");    }  }}@end

2、聊天

聊天的规则:

1、从服务器获取聊天记录

2、根据消息类XMPPMessageArchiving_Message_CoreDataObject的对象的属性isOutgoing来判断该消息是不是对方发送过来的消息 YES - 对方发送的消息, NO - 自己发送给对方的消息

3、发送消息

4、接收消息

  初始化消息归档

#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;

获取聊天记录(使用CoreData的方式)

1、创建请求

2、创建实体描述,实体名:   XMPPMessageArchiving_Message_CoreDataObject

3、创建谓词查询条件,条件:streamBareJidStr == 本人Jid AND bareJidStr == 好友Jid

4、创建排序对象,排序条件:timestamp

5、执行请求

接受、发送消息用到的代理方法

 

消息气泡

 

 

 

 

ChatTableViewController.m

#import "ChatTableViewController.h"#import "ChatTableViewCell.h"@interface ChatTableViewController ()<XMPPStreamDelegate>@property (nonatomic, strong) NSMutableArray *allMessageArray;@end@implementation ChatTableViewController- (void)viewDidLoad {  [super viewDidLoad];  self.allMessageArray = [NSMutableArray array];  // 隐藏分割线  self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;  // 注册cell  [self.tableView registerClass:[ChatTableViewCell class] forCellReuseIdentifier:@"chatCell"];  // 发送按钮  self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"取消" style:UIBarButtonItemStylePlain target:self action:@selector(cancelAction)];  // 取消按钮 20   self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"发送" style:UIBarButtonItemStylePlain target:self action:@selector(sendMessageAction)];  [[XMPPManager sharedXMPPManager].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];  // 获取显示消息的方法  [self showMessage];  }#pragma mark - 取消按钮点击事件- (void)cancelAction {  // 返回上一界面  [self.navigationController popViewControllerAnimated:YES];}#pragma mark - 发送消息按钮点击事件- (void)sendMessageAction {  XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.chatWithJID];  // 设置message的body为固定值 (没有实现发送自定义消息)  [message addBody:@"我爱你"];  // 通过通道进行消息发送  [[XMPPManager sharedXMPPManager].xmppStream sendElement:message];}#pragma mark - 显示消息- (void)showMessage {  // 获取管理对象上下文  NSManagedObjectContext *context = [XMPPManager sharedXMPPManager].context;  // 初始化请求对象  NSFetchRequest *request = [[NSFetchRequest alloc] init];  // 获取实体  NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPMessageArchiving_Message_CoreDataObject" inManagedObjectContext:context];  // 设置查询请求的实体  [request setEntity:entity];  // 设置谓词查询 (当前用户的jid,对方用户的jid) (根据项目需求而定)  request.predicate = [NSPredicate predicateWithFormat:@"streamBareJidStr == %@ AND bareJidStr == %@",[XMPPManager sharedXMPPManager].xmppStream.myJID.bare,self.chatWithJID.bare];  // 按照时间顺序排列  NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"timestamp" ascending:YES];  [request setSortDescriptors:@[sort]];  // 获取到存储在数据库中的聊天记录  NSArray *resultArray = [context executeFetchRequest:request error:nil];  // 先清空消息数组 (根据项目需求而定)  [self.allMessageArray removeAllObjects];  // 将结果数组赋值给消息数组  self.allMessageArray = [resultArray mutableCopy];  // 刷新UI  [self.tableView reloadData];  // 当前聊天记录跳到最后一行  if (self.allMessageArray.count > 0) {    NSIndexPath * indexPath = [NSIndexPath indexPathForRow:self.allMessageArray.count - 1 inSection:0];    // 跳到最后一行    [self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionBottom];  }  [context save:nil];}#pragma mark - Table view data source- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {  return 1;}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {  return self.allMessageArray.count;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {  ChatTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"chatCell" forIndexPath:indexPath];  // 数组里存储的是XMPPMessageArchiving_Message_CoreDataObject对象  XMPPMessageArchiving_Message_CoreDataObject *message = [self.allMessageArray objectAtIndex:indexPath.row];  // 设置cell中的相关数据  // 根据isOutgoing判断是不是对方发送过来的消息 YES - 对方发送的消息, NO - 自己发送给对方的消息  cell.isOut = message.isOutgoing;  cell.message = message.body;    return cell;}- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {  // cell的高度并没有自适应  return 70;}#pragma mark - ------------XMPPStreamDelegate相关代理------------#pragma mark - 已经发送消息- (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message {  // 重新对消息进行操作  [self showMessage];}#pragma mark - 已经接收消息- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message {  // 重新对消息进行操作  [self showMessage];}#pragma mark - 消息发送失败- (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error {  NSLog(@"消息发送失败");}@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

 

#import "ChatTableViewCell.h"@interface ChatTableViewCell ()/// 头像@property(nonatomic,strong)UIImageView * headerImageView;/// 消息框背景@property(nonatomic,strong)UIImageView * backgroundImageView;/// 显示每一条聊天内容@property(nonatomic,strong)UILabel * contentLabel;@end@implementation ChatTableViewCell//使用懒加载创建并初始化所有UI控件- (UILabel *)contentLabel{  if (_contentLabel == nil) {    _contentLabel = [[UILabel alloc] init];  }  return _contentLabel;}- (UIImageView *)backgroundImageView{  if (_backgroundImageView == nil) {    _backgroundImageView = [[UIImageView alloc] init];  }  return _backgroundImageView;}- (UIImageView *)headerImageView{  if (_headerImageView == nil) {    _headerImageView = [[UIImageView alloc] init];  }  return _headerImageView;}- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{  self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];  if (self) {    //设置cell不能选中    self.selectionStyle = UITableViewCellSelectionStyleNone;        [self.contentView addSubview:self.backgroundImageView];    [self.contentView addSubview:self.headerImageView];    [self.backgroundImageView addSubview:self.contentLabel];      }  return self;}- (void)awakeFromNib {  // Initialization code    }//重写isOut的setter方法,来设定cell上的不同布局- (void)setIsOut:(BOOL)isOut{  _isOut = isOut;  CGRect rect = self.frame;  if (_isOut) {    self.headerImageView.frame = CGRectMake(rect.size.width-50, 10, 50, 50);    self.headerImageView.image = [UIImage imageNamed:@"nike"];  }else{    self.headerImageView.frame = CGRectMake(0, 10, 50, 50);    self.headerImageView.image = [UIImage imageNamed:@"keji"];  }}//重写message方法,在cell上显示聊天记录- (void)setMessage:(NSString *)message{  if (_message != message) {    _message = message;    self.contentLabel.text = _message;    //    self.contentLabel.numberOfLines = 0;    [self.contentLabel sizeToFit];        CGRect rect = self.frame;    if (self.isOut) {//发出去的      // 消息气泡      self.backgroundImageView.image = [[UIImage imageNamed:@"chat_to"] stretchableImageWithLeftCapWidth:45 topCapHeight:40];      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);    }else{//接收的      self.backgroundImageView.image = [[UIImage imageNamed:@"chat_from"] stretchableImageWithLeftCapWidth:45 topCapHeight:40];      self.backgroundImageView.frame = CGRectMake(50, 10,self.contentLabel.frame.size.width+40, rect.size.height-20);    }    //因为contentLabel已经自适应文字大小,故不用设置宽高,但需要设置位置    self.contentLabel.center = CGPointMake(self.backgroundImageView.frame.size.width/2.0, self.backgroundImageView.frame.size.height/2.0);      }}@end

 

深圳有什么旅游景点深圳二日游攻略大全深圳好玩的旅游景点冬天去玉龙雪山会有高原反应吗? 冬天梅里雪山会封山吗? 2015年是什么年? 深圳园山风景区有游乐场吗?横岗园山游乐场几点开门? 深圳园山风景区门票包含游泳吗?横岗园山有哪些小孩玩的项目吗? 深圳园山风景区团购多少钱?园山风景区电话? 西岭雪山美食 西岭雪山旅游交通地图 西岭雪山旅游住宿 成都国色天香门票价格 成都文殊院门票价格 九寨沟骑马 LED part TLOE50C(F) Datasheet LED part TLOE50T Datasheet MR051C221MAAAP2 Datasheet MR051C222K Datasheet 华东出发去南沙旅游 华东出发去南沙旅游 华东出发去南沙旅游 华东出发去南田岛旅游 华东出发去南田岛旅游 华东出发去南田岛旅游