【原】iOS学习43即时通信之XMPP(2)

本篇是 即时通信之XMPP(2) 接上次 即时通信之XMPP(1)

1. 好友列表

 1> 初始化好友花名册

    // 获取管理好友的单例对象
    XMPPRosterCoreDataStorage *rosterStorage = [XMPPRosterCoreDataStorage sharedInstance];
    // 给roster属性进行初始化
    self.xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:rosterStorage dispatchQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)];
    // 将好友列表在通道中激活
    [self.xmppRoster activate:self.xmppStream];
    // 设置花名册代理
    [self.xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];

 2> XMPPRoster代理方法

  代码:

#pragma mark - XMPPRosterDelegate代理方法
/// 开始获取好友
- (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender
{
    NSLog(@"开始获取好友");
}

/// 结束获取好友
- (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender
{
    // 当前页面适用于显示好友列表的,所以在结束好友获取的代理方法中要进行刷新页面,然后将数据显示
    // 刷新页面
    [self.tableView reloadData];
}

// 接收好友的信息
// 这个代理方法会被执行多次,每添加完好友,相对应的好友信息都要有反馈
- (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item
{
    /*
     both 互为好友
     none 互不为好友
     to 我已经添加对方为好友,但是对方还没有接受
     from 对方已经添加我为好友,但是我还没有接受
     remove 曾经删除的好友
     */
    
    // 描述自己和对方之间的关系
    NSString *description = [[item attributeForName:@"subscription"] stringValue];
    
    NSLog(@"description = %@", description);
    
    if ([description isEqualToString:@"to"] || [description isEqualToString:@"none"] || [description isEqualToString:@"both"] || [description isEqualToString:@"from"]) {
        
        // 添加好友
        // 获取添加好友的JID
        NSString *friendJID = [[item attributeForName:@"jid"] stringValue];
        
        XMPPJID *jid = [XMPPJID jidWithString:friendJID];
        
        // 如果数组中有这个用户,就不用再进行操作
        if ([self.allRosterArray containsObject:jid]) {
            return;
        }
        
        // 添加好友到数组
        [self.allRosterArray addObject:jid];
        
        // 在tableView中添加这条数据
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.allRosterArray.count - 1 inSection:0];
        
        [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight];
    }
}

// 收到好友的监听请求(加好友请求),是否同意
- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence
{
    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 shareXMPPManager].xmppRoster acceptPresenceSubscriptionRequestFrom:weakSelf.fromJID andAddToRoster:YES];
    }];
    
    UIAlertAction *rejectAction = [UIAlertAction actionWithTitle:@"拒绝" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        
        [[XMPPManager shareXMPPManager].xmppRoster rejectPresenceSubscriptionRequestFrom:weakSelf.fromJID];
    }];
    
    [alertController addAction:acceptAction];
    [alertController addAction:rejectAction];
    
    [self presentViewController:alertController animated:YES completion:nil];
}

 3> 添加好友所需方法

  代码:

 

- (void)addFriend
{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"添加好友" message:@"请输入添加好友的名字" preferredStyle:UIAlertControllerStyleAlert];
 
    __weak typeof(self)mySlef = self;
    
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        mySlef.textField = textField;
    }];
    
    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"取消删除好友!");
    }];
    
    UIAlertAction *ensureAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        
        // 使用JID记录
        XMPPJID *friendJID = [XMPPJID jidWithString:[NSString stringWithFormat:@"%@@%@", mySlef.textField.text, kDomin]];
        // 监听好友的动作
        [mySlef.xmppRoster subscribePresenceToUser:friendJID];
        // 添加好友
        [mySlef.xmppRoster addUser:friendJID withNickname:mySlef.textField.text];
        
    }];
    
    [alertController addAction:ensureAction];
    [alertController addAction:cancelAction];
    
    [[self getCurrentVC] presentViewController:alertController animated:YES completion:nil];
}

4> 删除好友

   代码:

#pragma mark - 删除好友
- (void)removeFriendWithName:(NSString *)name
{
    // 使用JID记录
    XMPPJID *friendJID = [XMPPJID jidWithUser:name domain:kDomin resource:kResource];
    
    // 停止监听好友
    [self.xmppRoster unsubscribePresenceFromUser:friendJID];
    
    // 删除好友
    [self.xmppRoster removeUser:friendJID];
}

2. 聊天

 1> 聊天的规则:

  • 从服务器获取聊天记录,根据数据属性判断消息类型

  • 发送消息

  • 接收消息

 2> 初始化消息归档

    // 获取管理消息的存储对象
    XMPPMessageArchivingCoreDataStorage *storage = [XMPPMessageArchivingCoreDataStorage sharedInstance];
    // 消息管理器的初始化
    self.messageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:storage dispatchQueue:dispatch_get_main_queue()];
    // 激活通道
    [self.messageArchiving activate:self.xmppStream];
    // 设置代理
    [self.messageArchiving addDelegate:self delegateQueue:dispatch_get_main_queue()];
    // 设置消息管理上下文
    self.context = [storage mainThreadManagedObjectContext];

 3> 获取聊天记录

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

  • 创建请求

  • 创建实体描述,实体名:   XMPPMessageArchiving_Message_CoreDataObject

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

  • 创建排序对象,排序条件:timestamp

  • 执行请求

  代码:

#pragma mark - 显示消息
- (void)showMessage
{
    // 获取管理上下文
    NSManagedObjectContext *contxt = [XMPPManager shareXMPPManager].context;
    
    // 初始化请求对象
    NSFetchRequest *request = [NSFetchRequest new];
    
    // 获取实体
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPMessageArchiving_Message_CoreDataObject" inManagedObjectContext:contxt];
    
    // 设置查询请求的实体
    [request setEntity:entity];
    
    // 设置谓词查询
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"streamBareJidStr == %@ AND bareJidStr == %@", [XMPPManager shareXMPPManager].xmppStream.myJID.bare, self.chatToJID.bare];
    [request setPredicate:predicate];
    
    // 按照时间顺序排序
    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"timestamp" ascending:YES];
    [request setSortDescriptors:@[sort]];
    
    // 执行相关的操作
    NSArray *resultArray = [contxt executeFetchRequest:request error:nil];
    
    // 先清空消息数组
    [self.allMessageArray removeAllObjects];
    
    // 添加context执行结果数组
    [self.allMessageArray addObjectsFromArray:resultArray];
    
    // 刷新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];
    }
}

 4> 发送消息

#pragma mark - 发送点击方法
- (void)sendMessageAction
{
    // 设置message的body
    XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.chatToJID];
    
    // 发送的内容,一般是从输入框获取,这里我们就写成固定的值
    [message addBody:@"可以"];
    
    // 通过通道进行消息发送
    [[XMPPManager shareXMPPManager].xmppStream sendElement:message];
}

 5> 接收/发送消息的回调

  代码:

#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(@"发送消息失败");
}

 6> 消息气泡

   代码:

//重写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);
        
    }
}

 

以上代码均为练习代码的部分代码!完整练习代码github下载地址: https://github.com/AlonerOwl/UISenior11_-_1

由于只是练习代码,对于界面和部分功能没有优化,看起来比较low,如果有需求,请自己进行优化。

代码效果图:

 

 

posted @ 2016-05-19 20:17  墨隐于非  阅读(407)  评论(1编辑  收藏  举报