iOS开发利用系统推送Notifaction和轮询实现简单聊天系统
话不多说,先看一下做好的聊天软件界面:
首先在StoryBoard里拖了一个UItableView和一个view用来输入文字或者语音,右边的按钮用来切换文字和语音:
1.设置一下tableview的headView
<strong> </strong><span style="font-size:14px;"> //初始换录音 [self initRecord]; //获取用户头像 [self getHeadImg]; //获取订单详情 [self getOrderDetailInfo];</span>
c.self.chatArray读取数据库的聊天消息,如果数组为空返回,如果不为空刷新tableview
<span style="font-size:14px;">#pragma mark - 轮询消息 - (void)runLoopMessage { SpeakType speaker = [YCChatInfo getSpeakerPassengerBy:self.orderInfo.bigType]; [[YCReceiveMessageCenter defaultMessageCenter] getMessageListBySpeaker:speaker isRemote:NO andPushId:nil]; }</span>第一句代码是获取会话类型,这里定义了一个枚举值,主要有以下几个角色:
//会话类型 enum SpeakType { //Business YCDriverType = 11, YCPassenger = 12, YCSystem = 13, //v5.2.2 增加系统角色 YCLoctionAutoReply = 14, //v5.2.2添加本地自动回复 YCDriverAutoReply = 15, //v5.2.2添加司机自动回复 司机角色 YCLoctionUpdateVersionReply = 16, //v5.2.2 未知消息类型回复 【提示不支持的消息类型。请升级】 }; typedef NSInteger SpeakType;然后根据会话类型去请求聊天list接口,然后请求成功后对数据进行处理:
//isRemote 点击推送栏消息 if (response && [response[@"ret_code"] integerValue] == 200) { NSArray *array = response[@"result"]; id topVC = [[YCAppDelegate sharedDelegate] topViewController]; __block NSString *orderID = nil; __block NSString *dType = nil; [array enumerateObjectsUsingBlock:^(NSDictionary *result, NSUInteger idx, BOOL *stop) { NSString *type = [self controlMessageDispatch:result]; dType = type; <span style="white-space:pre"> </span>//此处省略五百字 } } else { DLog(@"轮询数据失败 response = %@, error = %@", response, error); }
如果code == 200的时候证明请求成功,然后用数组取出所有的聊天消息,然后用enumerateObjectsUsingBlock方法便利数组里每个元素,每个元素即一条聊天消息。然后通过controlMessageDispatch来获取消息的类型(dType):
typedef NS_ENUM(NSInteger, ClassType) { OrderClass = 1, ChatClass = 2, UserClass = 3, };
- (void)receiveChatMessage:(id)object方法,然后有一个消息状态字段kChatRepeatState,如果kChatRepeatState == 20,表示已读消息,直接返回。如果不是,用content初始化YCChatInfo:
NSDictionary *content = dic[@"content"]; YCChatInfo *item; item = [[YCChatInfo alloc] initWithDictionary:content];然后去判断ChatType,有以下几种:
//聊天 类型 enum ChatType { ChatText = 1, ChatImage = 2, ChatAudio = 3, ChatPOI = 4, ChatMix = 5, //混合内容 ChatCard = 6,//v5.2.2卡片消息 ChatUpdateHint = 701 //v5.2.2不支持类型升级提示 }; typedef NSInteger ChatType;
如果是语音消息,需要异步先去请求下载语音消息,下载完后先显示到界面上同事置为未读消息然后再储存到数据库里,然后回到主线程发NotifactionName:kChatMessageNotification通知聊天界面接收到聊天信息,展示到界面后然后存到数据库中:
[[NSNotificationCenter defaultCenter] postNotificationName:kChatMessageNotification object:nil userInfo:@{@"chatInfo" : chatInfo}]; [self.chatStore insertChat:chatInfo];这里上次有个bug,聊天消息去重之后一直收不到语音消息,就是因为我先把新聊天消息先插入数据,然后再去发通知,导致往界面上显示聊天消息是总是显示不上去。
如果是文字消息,把state置为MessageRead已读,然后发Notifaction通知聊天页面,然后存到数据库,如果是其他消息类型,把content改为“不支持的消息类型|您的当前版本过低,点击升级客户端”,然后在发出通知,存到数据库里。
//首先判断是否是推送消息, 且判断该条点击的推送id进入的 if (isRemote && [pushId isEqualToString:result[@"id"]]) { if (!orderID && ([type isEqualToString:@"new_chat"] || [type isEqualToString:@"DRIVER_ARRIVE"] || [type isEqualToString:@"RECEPTION_DRIVER"]|| [type isEqualToString:@"SERVICE_DONE"])) { if ([type isEqualToString:@"new_chat"]) { orderID = result[@"content"][@"topic"]; } else if (![topVC isKindOfClass:[YCSelectDriverViewController class]] && ([type isEqualToString:@"DRIVER_ARRIVE"] || [type isEqualToString:@"RECEPTION_DRIVER"] || [type isEqualToString:@"SERVICE_DONE"])) { orderID = result[@"content"][@"order_id"]; } } } }]; if (orderID) { if ([DefaultValueForKey(kShowWelcome) boolValue]) { if (![topVC isKindOfClass:[YCWelcomeVC class]]) { [[NSNotificationCenter defaultCenter] postNotificationName:kRemotePushVC object:nil userInfo:@{@"orderID" : orderID, @"type":dType}]; } } }
首先判断是否是推送消息, 且判断该条点击的推送id进入的,如果orderID不存在且如果type是新聊天消息或司机已到达或者司机接单或者服务结束就进入if判断里,然后在判断type是不是聊天,如果是聊天orderID是content里的topic字段,如果不是新聊天且当前最顶层ViewController不是YCSelectDriverViewController类且type是司机已到达或者司机接单或者服务结束就进入if判断里,orderID是content里的order_id字段。
- (void)receiveMessage:(NSNotification *)notification { YCChatInfo *chatInfo = notification.userInfo[@"chatInfo"]; //如果此时来的消息不输入当前会话 页面不进行操作 NSString *string1 = [[NSString alloc] initWithFormat:@"%@", chatInfo.orderID]; NSString *string2 = [[NSString alloc] initWithFormat:@"%@", self.orderInfo.serverOrderId]; if (![string1 isEqualToString:string2]) { return ; } NSInteger messageID = chatInfo.messageID; YCChatStore *chatStore = [[YCChatStore alloc]init]; BOOL isNotRepeat = [chatStore selectDBwithMessageID:messageID]; if (!isNotRepeat) { return; } [self insertRowToTableViewByIndexPath:chatInfo isSend:NO]; }第一句话是获取通知里传的聊天消息内容,然后拿chatinfo里的orderID和通过获取订单详情的接口里获得的orderID做比较,如果orderID不一致,直接return。如果一直就去YCChatStore里的selectDBwithMessageID方法里查询数据库是否有相同的messageID存在,如果存在直接return,如果不存在就插入界面
- (NSIndexPath *)insertRowToTableViewByIndexPath:(YCChatInfo *)chatInfo isSend:(BOOL)isSend { NSIndexPath *indexPath; [self.chatArray addObject:chatInfo]; indexPath = [NSIndexPath indexPathForRow:[self.chatArray count] - 1 inSection:0]; void(^ScrollBlock)() = ^{ DLog(@"%d",indexPath.row); [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; }; [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:YCTableViewRowAnimationFromBottom completion:^{ if (isSend) { ScrollBlock(); } }]; if (!isSend) { ScrollBlock(); } return indexPath; }
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated这个方法是把第几个indexpath滑动到tableview的最底部,
withRowAnimation:(YCTableViewRowAnimation)animation
completion:(void(^)(void))animationCompletion
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。