您的位置:首页 > 产品设计 > UI/UE

XMPP框架 微信项目开发之Socket聊天室发送数据——获取键盘高度,修改控件的约束值,代码滚动UITabView到指定位置

2015-11-04 21:04 1011 查看
在上篇中已经建立了基本的登录和服务器的连接,接下来在此基础之上实现聊天室数据的发送:

新建工程,实现步骤具体如下:

0



1



2



3



4



5



6



7



8



9



10



11



12



13



具体代码如下所示:

<span style="font-size:18px;"><span style="font-size:18px;">//
//  ViewController.m
//  聊天室
//
//  Created by apple on 15/11/4.
//  Copyright (c) 2015年 LiuXun. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()<NSStreamDelegate,UITextFieldDelegate,UITableViewDataSource,UITableViewDelegate>
{
NSInputStream *_inputStream;  // 对应输入流
NSOutputStream *_outputStream; // 对应输出流
}
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *inputViewConstraint;

@property (weak, nonatomic) IBOutlet UITableView *tableView;

@property(nonatomic, strong) NSMutableArray *chatMsgs; // 聊天消息数组

@end

@implementation ViewController

-(NSMutableArray *)chatMsgs
{
if (_chatMsgs == nil) {

_chatMsgs = [NSMutableArray array];
}
return _chatMsgs;
}

- (void)viewDidLoad {
[super viewDidLoad];

//  收发数据
// 做一个聊天
// 1. 用户登录
// 2. 登录成功后即可收发数据

// 监听键盘
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kbFrmWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
}

-(void)kbFrmWillChange:(NSNotification *)notification
{
NSLog(@"%@", notification.userInfo);

// 获取窗口的高度
CGFloat windowH = [UIScreen mainScreen] .bounds.size.height;

// 键盘结束时的Frame
CGRect kbEndFrm = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];

// 获取键盘结束的y值
CGFloat kbEndY = kbEndFrm.origin.y;

self.inputViewConstraint.constant = windowH - kbEndY; // 键盘的高度
}

#pragma mark OC输入输出流协议中的方法
-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
NSLog(@"%@", [NSThread currentThread]);
/*
NSStreamEventOpenCompleted = 1UL << 0,     // 输入输出流打开完成
NSStreamEventHasBytesAvailable = 1UL << 1, // 表示有字节可读(说明服务器已经返回数据到客户端)
NSStreamEventHasSpaceAvailable = 1UL << 2, // 可以发送字节
NSStreamEventErrorOccurred = 1UL << 3,        // 连接出现错误
NSStreamEventEndEncountered = 1UL << 4     // 连接结束
*/
switch (eventCode) {
case NSStreamEventOpenCompleted:
NSLog(@"输入输出流打开完成");
break;
case NSStreamEventHasBytesAvailable:
NSLog(@"有字节可读");
[self readData];
break;
case NSStreamEventHasSpaceAvailable:
NSLog(@"可以发送字节");
break;
case NSStreamEventErrorOccurred:
NSLog(@"连接出现错误");
break;
case NSStreamEventEndEncountered:
NSLog(@"连接结束");

// 关闭输入输出流
[_inputStream close];
[_outputStream close];

// 从主运行循环移除
[_inputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[_outputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
break;
default :
break;
}
}
#pragma mark 连接服务器主机
- (IBAction)connectToHost:(id)sender {
// 1. 第一步建立连接
/*  点击参数——>在编辑界面右侧就有此参数的描述
第一个参数CFAllocatorRef alloc:用于为输入输出流分配空间,如果传为NULL就会使用默认的大小分配
第二个参数CFStringRef host:所要连接到主机的IP地址。即要连接到哪台主机。
第三个参数port:所要连接到主机的具体端口号。
*/
NSString *host = @"127.0.0.1"; // 因为是C语言函数所以要进行桥接转换为C语言字符串
int port = 12345;
// 定义输入输出流
CFReadStreamRef  readStream;
CFWriteStreamRef  writeStream;
CFStreamCreatePairWithSocketToHost(NULL,  (__bridge CFStringRef)(host), port, &readStream, &writeStream);

// 将C语言的输入输出流转化为OC对象
// 如果要想知道连接成功还是失败,可以通过OC中的代理进行实现。所以需要将C语言输入输出流转化为OC对象。
_inputStream = (__bridge NSInputStream *)(readStream);
_outputStream = (__bridge NSOutputStream *)(writeStream);

// 设置代理
_inputStream.delegate = self;
_outputStream.delegate = self;

// 把输入输出流添加到主运行循环
// 不添加到主运行循环,代理有可能不工作
/**
以下情况需要添加到主运行循环:一是在子线程开启计时器。二是设置网络代理。
*/
[_inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

// 打开输入输出流——必须打开输入输出流,才可以建立连接,双方才可以发送数据
[_inputStream open];
[_outputStream open];

}

#pragma mark 用户登录
- (IBAction)loginBtnClick:(id)sender {

// 登录
// 发送用户名和密码
// 在这里做的时候,只发用户名,密码就不用发送

// 如果要登录,发送的数据格式为 "iam:zhangsan"
// 如果要发送聊天消息,数据格式为"msg:did you have dinner"
//    uint8_t typedef —> unsigned char uint8_t;
//   (const uint8_t *) 意味着要传入一个字节数组
//  maxLength: 表示发送字节数组中的前多少个字节

// 登录的指令
NSString *loginStr = @"iam:zhangsanXXXXXXX";

// 把Str转化为NSData
NSData  *data = [loginStr dataUsingEncoding:NSUTF8StringEncoding];

[_outputStream write:data.bytes maxLength:data.length];

}

#pragma mark - 读取服服务器返回的数据
-(void)readData
{
// 建立缓冲区,可以存放1024个字节
uint8_t buf[1024];

// 返回实际装的字节
NSInteger  len =  [_inputStream read:buf maxLength:sizeof(buf)];

// 把字节数组转化为字符串
NSData *data = [NSData dataWithBytes:buf length:len];

// 从服务器接收到的数据
NSString *reciveStr =  [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

NSLog(@"%@", reciveStr);

[self reloadDataWithText:reciveStr];
}

#pragma mark 实现TextField的Return键的方法
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
NSString *text = textField.text;
NSLog(@"%@", text);
// 已知:如果要发送聊天消息,数据格式为"msg:did you have dinner"

// 按服务器指定的发送内容的格式拼接字符串
// 聊天信息
NSString *msgStr = [NSString stringWithFormat:@"msg:%@", text];

// 将msgStr转化为NSData
NSData *data = [msgStr dataUsingEncoding:NSUTF8StringEncoding];

// 刷新表格
[self reloadDataWithText:msgStr];

// 发送数据
[_outputStream write:data.bytes maxLength:data.length];

// 发送完数据后,清空textField
textField.text = nil;
return YES;
}

#pragma mark 表格刷新数据
-(void)reloadDataWithText:(NSString *)text
{
[self.chatMsgs addObject:text];
[self.tableView reloadData];

// 数据多的时候表格应该向上滚动
// 获取表格的最后一行
NSIndexPath *lastPath = [NSIndexPath indexPathForRow:self.chatMsgs.count-1 inSection:0];

//  让表格滚动到最后一行底部
[self.tableView scrollToRowAtIndexPath:lastPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

#pragma mark 表格的数据源
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.chatMsgs.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}

cell.textLabel.text  = self.chatMsgs[indexPath.row];
return cell;
}

#pragma mark - 表格滚动时让输入控件收回
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[self.view endEditing:YES];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

@end</span></span>
运行结果如下:



开发步骤如下:

步骤一:建立网络连接。连接服务器主机,需要服务器主机的IP地址、服务器文件内指定的连接端口号、C语言格式的输入输出流。C语言格式的输入输出流为CFReadStreamRef和CFWriteStreamRef,需要注意的是服务器的IP地址为NSString类型需要桥接转化为C语言类型。可以点击错误圆点—>点击错误提示的第一项自动bridge转换。要使用C语言的CFStreamCreatePairWithSocketTOHost语句进行连接。具体如下:

// 1.第一步建立连接
/* 点击参数——>在编辑界面右侧就有此参数的描述
第一个参数CFAllocatorRef alloc:用于为输入输出流分配空间,如果传为NULL就会使用默认的大小分配
第二个参数CFStringRef host:所要连接到主机的IP地址。即要连接到哪台主机。
第三个参数port:所要连接到主机的具体端口号。
*/
NSString *host =@"127.0.0.1";
//因为是C语言函数所以要进行桥接转换为C语言字符串
int port =12345;
//定义输入输出流
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridgeCFStringRef)(host),
port, &readStream, &writeStream);
步骤二:将C语言的输入输出流转化为OC的输入输出流对象。因为要使用到OC中NSStreamDelegate协议中的方法。
具体实现如下:

//
将C语言的输入输出流转化为OC对象
//
如果要想知道连接成功还是失败,可以通过OC中的代理进行实现。所以需要将C语言输入输出流转化为OC对象。
_inputStream = (__bridgeNSInputStream *)(readStream);
_outputStream = (__bridgeNSOutputStream *)(writeStream);
步骤三:设置为全局的输入输出流设置代理,并让当前控制器遵守NSStreamDelegate协议。如下所示:
......步骤比较多不过都在代码中添加了注释,在此不再奥数。
不过应当掌握的几个其它知识点还是很重要的:
知识点一:监听键盘,修改容器的约束值。
知识点二:让UItabView组件滚动到指定的位置。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: