• 即时通信


    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
  • 相关阅读:
    hadoop的50070端口不能访问网页的解决方法
    通过命令来查看NameNode的状态(是Active还是Standby)
    Linux 远程复制scp
    Centos 7 查看使用YUM 安装的JDK路径并配置JAVA_HOME
    【Centos7】hostnamectl 设置主机名
    centos7设置静态IP地址
    WPF窗体动态效果
    C#使用阿里云OSS
    关于WPF中的XAML
    解决Windows10关闭UAC后,开机启动项不生效的问题
  • 原文地址:https://www.cnblogs.com/zhizunbao/p/5517663.html
Copyright © 2020-2023  润新知