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, )];
// 在通道中激活xmppRoster
[self.xmppRoster activate:self.xmppStream];
// 设置代理
[self.xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
XMPPRoster代理方法
好友列表
添加好友
删除好友
XMPPManager.h 新增代码
#pragma mark - 添加好友 - (void)addFriend; #pragma mark - 删除好友 - (void)removeFriendWithName:(NSString *)userName; #pragma mark - 手动断开连接(注销) - (void)disconnectionToServer;
XMPPManager.m 新增代码
/// 接收要添加好友的名字
@property (nonatomic, strong) UITextField *addText;
#pragma mark - 重写初始化方法
- (instancetype)init {
if (self = [super init]) {
#pragma mark - 创建通道
// 初始化通道对象
self.xmppStream = [[XMPPStream alloc] init];
// 设置Openfire服务器主机名
self.xmppStream.hostName = kHostName;
// 设置服务器端口号
self.xmppStream.hostPort = kHostPort;
// 设置代理
[self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
#warning -----------------以下是该方法中新增加的代码-----------------------
#pragma mark - 管理好友
// 获取管理好友的单例对象
XMPPRosterCoreDataStorage *rosterStorage = [XMPPRosterCoreDataStorage sharedInstance];
// 用管理好友的单例对象初始化Roster花名册
// 好友操作是耗时操作
self.xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:rosterStorage dispatchQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, )];
// 在通道中激活xmppRoster
[self.xmppRoster activate:self.xmppStream];
// 设置代理
[self.xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
#warning -----------------以上是该方法中新增加的代码-----------------------
}
return self;
}
#pragma mark -----------------以下是管理好友列表----------------
#pragma mark - 添加好友
- (void)addFriend {
NSLog(@"manager - 添加好友 %d", __LINE__);
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"添加好友" message:@"请输入要添加好友的名字" preferredStyle:UIAlertControllerStyleAlert];
__weak typeof(self)weakSelf = self;
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
// 接收输入的好友名字
weakSelf.addText = textField;
}];
UIAlertAction *cancleAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
UIAlertAction *sureAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"======%@", weakSelf.addText.text);
// 使用JID记录
XMPPJID *addJID = [XMPPJID jidWithUser:weakSelf.addText.text domain:kDomin resource:kResource];
// 监听好友的动作
[weakSelf.xmppRoster subscribePresenceToUser:addJID];
// 添加好友
[weakSelf.xmppRoster addUser:addJID withNickname:weakSelf.addText.text];
}];
[alertController addAction:sureAction];
[alertController addAction:cancleAction];
[[self getCurrentVC ]presentViewController:alertController animated:YES completion:nil];
}
#pragma mark - 删除好友
- (void)removeFriendWithName:(NSString *)userName {
NSLog(@"manager删除好友 %d", __LINE__);
// 使用JID记录要删除的用户名
XMPPJID *removeJID = [XMPPJID jidWithUser:userName domain:kDomin resource:kResource];
// 停止监听好友
[self.xmppRoster unsubscribePresenceFromUser:removeJID];
// 删除好友
[self.xmppRoster removeUser:removeJID];
}
#pragma mark - 获取当前屏幕显示的viewcontroller
- (UIViewController *)getCurrentVC
{
UIViewController *result = nil;
UIWindow * window = [[UIApplication sharedApplication] keyWindow];
if (window.windowLevel != UIWindowLevelNormal)
{
NSArray *windows = [[UIApplication sharedApplication] windows];
for(UIWindow * tmpWin in windows)
{
if (tmpWin.windowLevel == UIWindowLevelNormal)
{
window = tmpWin;
break;
}
}
}
UIView *frontView = [[window subviews] objectAtIndex:];
id nextResponder = [frontView nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]])
result = nextResponder;
else
result = window.rootViewController;
return result;
}
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 {
;
}
- (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];
// 根据项目情况分析,合理添加判断
) {
// 获取用户
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:(DDXMLElement *)item {
NSLog(@"listTVC 接收好友信息 %d", __LINE__);
/**
123 * 好友信息状态有5种
124 both - 互为好友
125 none - 互不为好友
126 to - 请求添加对方为好友,对方还没有同意
127 from - 对方添加我为好友,自己还没有同意
128 remove - 曾经删除的好友
129 */
// 自己和对方之间的关系
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 - inSection:];
[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、执行请求
接受、发送消息用到的代理方法
消息气泡
XMPPManager.h 新增代码
/// 和聊天相关的属性,消息归档 @property (nonatomic, strong) XMPPMessageArchiving *xmppMessageArchiving; /// 管理数据库上下文 @property (nonatomic, strong) NSManagedObjectContext *context;
XMPPManager.m 新增代码
#pragma mark - 重写初始化方法
- (instancetype)init {
if (self = [super init]) {
#pragma mark - 创建通道
// 初始化通道对象
self.xmppStream = [[XMPPStream alloc] init];
// 设置Openfire服务器主机名
self.xmppStream.hostName = kHostName;
// 设置服务器端口号
self.xmppStream.hostPort = kHostPort;
// 设置代理
[self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
#pragma mark - 管理好友
// 获取管理好友的单例对象
XMPPRosterCoreDataStorage *rosterStorage = [XMPPRosterCoreDataStorage sharedInstance];
// 用管理好友的单例对象初始化Roster花名册
// 好友操作是耗时操作
self.xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:rosterStorage dispatchQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, )];
// 在通道中激活xmppRoster
[self.xmppRoster activate:self.xmppStream];
// 设置代理
[self.xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
#warning -----------------以下是该方法中新增加的代码-----------------------
#pragma mark - 初始化管理归档
// 获取管理消息的存储对象
XMPPMessageArchivingCoreDataStorage *storage = [XMPPMessageArchivingCoreDataStorage sharedInstance];
// 进行消息管理器的初始化
self.xmppMessageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:storage dispatchQueue:dispatch_get_main_queue()];
// 在通道中激活xmppMessageArchiving
[self.xmppMessageArchiving activate:self.xmppStream];
// 设置代理
[self.xmppMessageArchiving addDelegate:self delegateQueue:dispatch_get_main_queue()];
// 设置管理上下文 (此时不再从AppDelegate获取)
self.context = storage.mainThreadManagedObjectContext;
#warning -----------------以上是该方法中新增加的代码-----------------------
}
return self;
}
从好友列表页面点击cell进入聊天界面
RosterListTableViewController.m 好友列表界面新增代码
#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];
}
复制代码
聊天界面
ChatTableViewController.h
1 #import <UIKit/UIKit.h> 2 #import "XMPPManager.h" 3 4 @interface ChatTableViewController : UITableViewController 5 /// 当前和谁在聊天 6 @property (nonatomic, strong) XMPPJID *chatWithJID; 7 @end
ChatTableViewController.m
#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];
// 当前聊天记录跳到最后一行
) {
NSIndexPath * indexPath = [NSIndexPath indexPathForRow:self.allMessageArray.count - inSection:];
// 跳到最后一行
[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionBottom];
}
[context save:nil];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
;
}
- (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的高度并没有自适应
;
}
#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-, , , );
self.headerImageView.image = [UIImage imageNamed:@"nike"];
}else{
self.headerImageView.frame = CGRectMake(, , , );
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: topCapHeight:];
self.backgroundImageView.frame = CGRectMake(rect.size.width - self.contentLabel.frame.size.width - -, , self.contentLabel.frame.size.width+, rect.size.height-);
}else{//接收的
self.backgroundImageView.image = [[UIImage imageNamed: topCapHeight:];
self.backgroundImageView.frame = CGRectMake(, ,self.contentLabel.frame.size.width+, rect.size.height-);
}
//因为contentLabel已经自适应文字大小,故不用设置宽高,但需要设置位置
self.contentLabel.center = CGPointMake(self.backgroundImageView.frame.size.width/2.0, self.backgroundImageView.frame.size.height/2.0);
}
}
@end
以上,一个超级简单的即时通讯工具就已经完成,有些逻辑思维是不固定的,可以根据自己的项目需求去更改代码
还有没有实现到的 图片/语音传输,在这里就只说一下原理
1、将图片/语音上传到服务器
2、根据和服务器的约定,拼好文件在服务器的地址(即图片/语音的URL)
3、调用XMPP发送消息方法,将地址发送出去
4、在接收端接收到的为一条文本信息,里面仅仅是一个指向资源文件的URL地址
5、在拿到URL后进行需要的操作(即请求图片/语音显示到页面上)
UI进阶 即时通讯之XMPP好友列表、添加好友、获取会话内容、简单聊天的更多相关文章
- UI进阶 即时通讯之XMPP登录、注册
1.XMPP环境搭建 http://www.cnblogs.com/fearlessyyp/p/5506644.html 第一次打开可能会有点儿慢,图片很多,步骤很详细,祝搭建成功. 2.工程中添加X ...
- UI进阶 即时通讯之XMPP环境搭建
内容中包含 base64string 图片造成字符过多,拒绝显示
- UI进阶 即时通讯之卸载Openfire
首先,确保你已经关掉了openfire打开终端 (在应用程序-->实用工具-->)输入以下命令sudo rm -rf /Library/PreferencePanes/Openfire.p ...
- 【即时通讯】XMPP调试与简单使用
上篇讲了[即时通讯]即时通讯及XMPP概述及环境配置,接下来我们就要进行调试,看看是否可用! 在测试之前我们需要先事先保存一些东西,以便后面使用 —— 登录openfire后台 ——获取服务器名和端口 ...
- 【即时通讯】即时通讯及XMPP概述及…
在讲解XMPP前,我们需要先了解什么是即时通讯技术: * 即时通讯技术(IM - InstantMessaging)支持用户在线实时交谈.当一方需要发送消息时,用户必须打开一个窗口,以便让用户与交流对 ...
- XMPP即时通讯协议使用(十三)——获取当前在线用户或关闭指定用户
1.开启REST API插件或根据需求修改其插件源码: 2.添加服务器->服务器管理->系统属性中添加 plugin.restapi.enabled=true 3.pom依赖 <de ...
- 【Android】17.0 UI开发(八)——利用RecyclerView列表控件实现精美的聊天界面
1.0 首先新建一个项目,名叫:UIBestPractice,目录如下: 2.0 这里需要先准备两张图片,放在app\src\main\res\drawable-xhdpi目录下. 这里图片名称已经制 ...
- XMPP即时通讯资料记录
几天开始研究XMPP即时通讯的技术,来实现移动应用的计时聊天功能.记录下参考的博客地址,还挺详细的. http://blog.csdn.net/fhbystudy/article/details/16 ...
- 微信小程序接入腾讯云IM即时通讯(会话列表)
会话列表功能概述: 登录 :先用自己的账号登录腾讯云: 获取会话列表 :登录之后再获取会话列表: 更新未读消息数量 :获取会话列表之后更新未读消息数量 WXML代码(自己写的将就看一下) <vi ...
随机推荐
- 程序ajax请求公共组件:app-jquery-http.js
// --------网络操作-------------------- $.HTTP = { getUrlParam : function(name) { var reg = new RegExp(& ...
- linux shell 之if-------用if做判断
综合网络,略有修改, 一 简介 1 字符串判断 str1 = str2 当两个串有相同内容.长度时为真 str1 != str2 当串str1和str2不等时为真 -n str1 当串的长度大于0 ...
- CodeForces 622C Not Equal on a Segment
预处理p[i],p[i]表示:[p[i],i]这段闭区间上所有数字都是a[i] 询问的时候,如果xi==a[ri]并且p[ri]<=li,一定无解 剩下的情况都是有解的,如果xi!=a[ri], ...
- JavaScriptConvert.SerializeObject转换出错
The length of the string exceeds the value set on the maxJsonLength property(字符串的长度超过maxjsonlength上设 ...
- javascript 闭包理解
摘自:http://www.cnblogs.com/jkswjw/p/3180384.html javascript 闭包基础分享 闭包向来给包括JavaScript程序员在内的程序员以神秘,高深的感 ...
- Masonry布局框架的使用
Masonry是一个轻量级的布局框架 拥有自己的描述语法 采用更优雅的链式语法封装自动布局 简洁明了 并具有高可读性.比我们使用自动布局,繁琐的约束条件,好用多了.下面我们来学学masonry的使用方 ...
- 试水MongoDB
1)安装好后启动mongodb 服务 1_1) 建立data/db ,保证至少有3g大小的盘 1_2) 建立log 文件夹 1_3)配置文件 内容,指定数据存放位置.日志文件位置 dbpath ...
- scrapy bug
Issue one describle: scrapy No module named mail.smtp solution:sudo apt-get install python-twisted
- PHP检测获取内存信息
PHP也可以检测获取到Windows的内存信息,而且代码还挺简单,无意发现的,觉得以后能用上,在此与大家分享. 本代码将得到总内存.初始使用等内存信息: <?php echo "初始: ...
- Android测试点
记录下之前项目测试中涉及到的Android测试点: 1.APP基本功能 按照back log整理测试用例,测试中发现有需求变动.或未考虑完全,及时更新测试用例. 测试用例包括:全功能点用例+重点功能快 ...