每日更新关注:https://weibo.com/hjq995 新浪微博
整体布局如下:
程序结构如右图:
每日更新关注:https://weibo.com/hjq995 新浪微博
==========================================================================
指定根视图:
RootViewController * rootVC = [[RootViewController alloc] init]; UINavigationController * nav = [[UINavigationController alloc] initWithRootViewController:rootVC]; self.window.rootViewController= nav;根视图:
#import "RootViewController.h" #import "BlueSessionManager.h" #import "ChatCell.h" #import "ChatItem.h" #import <AssetsLibrary/AssetsLibrary.h> #import <AVFoundation/AVFoundation.h> #define kRecordAudioFile @"myRecord.caf" // 判断大小 #define HEIGHT [UIScreen mainScreen].bounds.size.height #define WIDTH [UIScreen mainScreen].bounds.size.width #define ChatHeight 45.0 @interface RootViewController ()<NSStreamDelegate,UITableViewDataSource,UITableViewDelegate,UITextViewDelegate,UIActionSheetDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate,AVAudioRecorderDelegate,AVAudioPlayerDelegate> { float _sendBackViewHeight; float _sendTextViewHeight; UIImagePickerController * _picker; UIView * _backRemindRecordView; } // DataAndBlue @property(strong, nonatomic) BlueSessionManager *sessionManager; @property(strong, nonatomic) NSMutableArray *datasource; @property(strong, nonatomic) NSMutableArray * myDataArray; @property(strong, nonatomic) NSMutableData *streamData; @property(strong, nonatomic) NSOutputStream *outputStream; @property(strong, nonatomic) NSInputStream *inputStream; // UI @property(strong, nonatomic) UITableView * tableView; @property(strong, nonatomic) UIView * sendBackView; @property(strong, nonatomic) UITextView * sendTextView; @property(strong, nonatomic) UIButton * sendButton; // 语音播放 @property (nonatomic,strong) AVAudioRecorder *audioRecorder;//音频录音机 //音频播放器,用于播放录音文件 @property (nonatomic,strong) AVAudioPlayer *audioPlayer; @property (nonatomic,strong) NSTimer *timer;//录音声波监控(注意这里暂时不对播放进行监控) @property (strong, nonatomic) UIProgressView *audioPower;//音频波动 @end @implementation RootViewController - (void)viewDidLoad { [super viewDidLoad]; [self makeBlueData]; [self readyUI]; [self buildVideoForWe]; // Do any additional setup after loading the view } #pragma mark 基本制作 - (void)readyUI { self.title = @"蓝牙设置"; self.view.backgroundColor = [UIColor whiteColor]; self.automaticallyAdjustsScrollViewInsets = NO; NSArray * buttonTitleArray = @[@"寻找设备",@"打开天线"]; self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:buttonTitleArray[0] style:UIBarButtonItemStyleDone target:self action:@selector(lookOtherDevice)]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:buttonTitleArray[1] style:UIBarButtonItemStyleDone target:self action:@selector(showSelfAdvertiser)]; [self makeUIView]; } - (void)lookOtherDevice { [self.sessionManager browseWithControllerInViewController:self connected:^{ NSLog(@"connected"); } canceled:^{ NSLog(@"cancelled"); }]; } - (void)showSelfAdvertiser { [self.sessionManager advertiseForBrowserViewController]; } #pragma mark 制作页面UI - (void)makeUIView { // NSLog(@"width === %f,height===== %f",WIDTH,HEIGHT); self.myDataArray = [NSMutableArray arrayWithCapacity:0]; self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, WIDTH, HEIGHT - 64 - ChatHeight - 10)]; self.tableView.delegate = self; self.tableView.dataSource = self; self.tableView.separatorStyle = UITableViewCellSelectionStyleNone; [self.view addSubview:self.tableView]; //-------------------------------------------------------------------------// self.sendBackView = [[UIView alloc] initWithFrame:CGRectMake(0, HEIGHT - ChatHeight, WIDTH, ChatHeight)]; self.sendBackView.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1.0]; [self.view addSubview:self.sendBackView]; // float heightView = self.sendBackView.frame.size.height; self.sendTextView = [[UITextView alloc] initWithFrame:CGRectMake(10, 5, WIDTH - 10 - 90, 35)]; // self.sendTextView.backgroundColor = [UIColor lightGrayColor]; self.sendTextView.returnKeyType = UIReturnKeySend; self.sendTextView.font = [UIFont systemFontOfSize:17]; self.sendTextView.editable = YES; self.sendTextView.delegate = self; [self.sendBackView addSubview:self.sendTextView]; UIButton * addButton = [UIButton buttonWithType:UIButtonTypeContactAdd]; addButton.frame = CGRectMake(WIDTH - 85, 2, 37, 37); [addButton addTarget:self action:@selector(addNextImage) forControlEvents:UIControlEventTouchUpInside]; [self.sendBackView addSubview:addButton]; self.sendButton = [UIButton buttonWithType:UIButtonTypeCustom]; self.sendButton.frame = CGRectMake(WIDTH - 45, 5, 40, 30); [self.sendButton setImage:[UIImage imageNamed:@"record.png"] forState:UIControlStateNormal]; [self.sendButton addTarget:self action:@selector(videoRecord) forControlEvents:UIControlEventTouchUpInside]; [self.sendBackView addSubview:self.sendButton]; // 增加通知 [self addTheNoticeForKeyDownUp]; } #pragma mark 图片的传输---------/////// - (void)addNextImage { UIActionSheet *chooseImageSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"照相机",@"相册", nil]; [chooseImageSheet showInView:self.view]; } #pragma mark UIActionSheetDelegate Method -(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { _picker = [[UIImagePickerController alloc] init]; _picker.delegate = self; switch (buttonIndex) { case 0://Take picture if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { _picker.sourceType = UIImagePickerControllerSourceTypeCamera; } [self presentViewController:_picker animated:NO completion:nil]; break; case 1: //From album _picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; [self presentViewController:_picker animated:NO completion:^{ // 改变状态栏的颜色 为正常 这是这个独有的地方需要处理的 [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; }]; break; default: break; } } // 相册 -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { NSString *type = [info objectForKey:UIImagePickerControllerMediaType]; //当选择的类型是图片 if ([type isEqualToString:@"public.image"]) { //先把图片转成NSData UIImage* image = [info objectForKey:UIImagePickerControllerOriginalImage]; NSData *data; if (UIImagePNGRepresentation(image) == nil) { data = UIImageJPEGRepresentation(image, 1.0); } else { data = UIImagePNGRepresentation(image); } //图片保存的路径 //这里将图片放在沙盒的documents文件夹中 NSString * DocumentsPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; //文件管理器 NSFileManager *fileManager = [NSFileManager defaultManager]; //把刚刚图片转换的data对象拷贝至沙盒中 并保存为image.png [fileManager createDirectoryAtPath:DocumentsPath withIntermediateDirectories:YES attributes:nil error:nil]; [fileManager createFileAtPath:[DocumentsPath stringByAppendingString:@"/image.png"] contents:data attributes:nil]; //得到选择后沙盒中图片的完整路径 NSString * filePath = [[NSString alloc]initWithFormat:@"%@%@",DocumentsPath, @"/image.png"]; [_picker dismissViewControllerAnimated:NO completion:^{ // 改变状态栏的颜色 改变为白色 [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; // 这边是真正的发送 if(!self.sessionManager.isConnected) { UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"蓝牙已经断开了,请重新连接!" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil]; [alertView show]; return; } ChatItem * chatItem = [[ChatItem alloc] init]; chatItem.isSelf = YES; chatItem.states = picStates; chatItem.picImage = image; [self.datasource addObject:chatItem]; [self insertTheTableToButtom]; [self sendAsResource:filePath]; }]; } } - (void)sendAsResource:(NSString *)path { NSLog(@"dispaly ====%@",self.sessionManager.firstPeer.displayName); NSString * name = [NSString stringWithFormat:@"%@ForPic",[[UIDevice currentDevice] name]]; NSURL * url = [NSURL fileURLWithPath:path]; NSProgress *progress = [self.sessionManager sendResourceWithName:name atURL:url toPeer:self.sessionManager.firstPeer complete:^(NSError *error) { if(!error) { NSLog(@"finished sending resource"); } else { NSLog(@"%@", error); } }]; NSLog(@"%@", @(progress.fractionCompleted)); } #pragma mark 普通数据的传输 - (void)sendWeNeedNews { if(!self.sessionManager.isConnected) { UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"蓝牙已经断开了,请重新连接!" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil]; [alertView show]; return; } if([self.sendTextView.text isEqualToString:@""]) { return; } ChatItem * chatItem = [[ChatItem alloc] init]; chatItem.isSelf = YES; chatItem.states = textStates; chatItem.content = self.sendTextView.text; [self.datasource addObject:chatItem]; // 加到数组里面 // 添加行 indexPath描述位置的具体信息 [self insertTheTableToButtom]; NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.sendTextView.text]; NSError *error = [self.sessionManager sendDataToAllPeers:data]; if(!error) { //there was no error. } else { NSLog(@"%@", error); } [self returnTheNewBack]; } - (void)returnTheNewBack { // 归零 self.sendTextView.text = @""; [self.sendTextView resignFirstResponder]; self.tableView.frame = CGRectMake(0, 64, WIDTH, HEIGHT - 64 - ChatHeight - 10 ); self.sendBackView.frame = CGRectMake(0, HEIGHT - ChatHeight , WIDTH, ChatHeight); } // 这是一种很好的键盘下移方式 -(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { if ([text isEqualToString:@"\n"]) { [self sendWeNeedNews]; return NO; } return YES; } - (void)textViewDidChange:(UITextView *)textView { // 随机改变其高度 float textHeight = [self heightForString:textView.text fontSize:16 andWidth:textView.frame.size.width]; _sendTextViewHeight = textHeight; // NSLog(@"teztheight ===== %f",textHeight); self.sendTextView.frame = CGRectMake(10, 5, WIDTH - 10 - 90, _sendTextViewHeight); self.sendBackView.frame = CGRectMake(0, HEIGHT - _sendBackViewHeight - _sendTextViewHeight - 10, WIDTH, _sendTextViewHeight + 10); } - (float) heightForString:(NSString *)value fontSize:(float)fontSize andWidth:(float)width { UITextView *detailTextView = [[UITextView alloc]initWithFrame:CGRectMake(0, 0, width, 0)]; detailTextView.font = [UIFont systemFontOfSize:fontSize]; detailTextView.text = value; CGSize deSize = [detailTextView sizeThatFits:CGSizeMake(width,CGFLOAT_MAX)]; return deSize.height; } #pragma mark 以下是为了配合 键盘上移的变化 - (void)addTheNoticeForKeyDownUp { [ [NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleKeyBoardDidShow:) name:UIKeyboardDidShowNotification object:nil]; [ [NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleKeyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } -(void)handleKeyBoardDidShow:(NSNotification *)paramNotification { CGSize size = [[paramNotification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size; _sendBackViewHeight = size.height; [UIView animateWithDuration:0.000001 animations:^{ self.tableView.frame = CGRectMake(0, 64, WIDTH, HEIGHT - 64 - ChatHeight - size.height); self.sendBackView.frame = CGRectMake(0, HEIGHT - ChatHeight - size.height, WIDTH, ChatHeight); }]; } -(void)handleKeyboardWillHide:(NSNotification *)paramNotification { [UIView animateWithDuration:0.1 animations:^{ if(_sendTextViewHeight > 0) { self.tableView.frame = CGRectMake(0, 64, WIDTH, HEIGHT - 64 - _sendTextViewHeight + 10 ); self.sendBackView.frame = CGRectMake(0, HEIGHT - _sendTextViewHeight - 10, WIDTH, _sendTextViewHeight + 10); } else { self.tableView.frame = CGRectMake(0, 64, WIDTH, HEIGHT - 64 - ChatHeight - 10 ); self.sendBackView.frame = CGRectMake(0, HEIGHT - ChatHeight , WIDTH, ChatHeight); } }]; } /*--------------------------------------------------------------------------------------------*/ - (void)insertTheTableToButtom { // 哪一组 哪一段 NSIndexPath * indexPath = [NSIndexPath indexPathForRow:self.datasource.count- 1 inSection:0]; // 添加新的一行 [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; // 滑动到底部 第二个参数是滑动到底部 [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; } #pragma mark tableView 代理 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.datasource.count; } -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { ChatItem * chatItem = [self.datasource objectAtIndex:indexPath.row]; if(chatItem.states == picStates) { NSLog(@"widht====%f,height======%f",chatItem.picImage.size.width,chatItem.picImage.size.height); return 50; } else if(chatItem.states == textStates) { CGSize size = [chatItem.content boundingRectWithSize:CGSizeMake(250, 1000) options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName :[UIFont systemFontOfSize:14]} context:nil].size; return size.height + 20 + 10; // 与view的距离 + 与Cell的距离 } else { return 50; } } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString * iden = @"iden"; ChatCell * cell = [tableView dequeueReusableCellWithIdentifier:iden]; if(cell == nil) { cell = [[ChatCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:iden]; cell.selectionStyle = UITableViewCellSelectionStyleNone; // 让后面选中的没有阴影效果 } // 模型 ChatItem * chatItem = [self.datasource objectAtIndex:indexPath.row]; CGSize size = [chatItem.content boundingRectWithSize:CGSizeMake(250, 1000) options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName :[UIFont systemFontOfSize:14]} context:nil].size; //如果自己发的 if(chatItem.isSelf) { cell.leftHeadImage.hidden = YES; cell.rightHeadImage.hidden = NO; if(chatItem.states == picStates) { cell.lefeView.hidden = YES; cell.rightView.hidden = YES; cell.rightPicImage.image = chatItem.picImage; cell.leftPicImage.hidden = YES; cell.rightPicImage.hidden = NO; cell.leftVideoButton.hidden = YES; cell.rightVideoButton.hidden = YES; NSLog(@"self send"); } else if(chatItem.states == textStates) { cell.rightPicImage.hidden = YES; cell.leftPicImage.hidden = YES; cell.lefeView.hidden = YES; cell.rightView.hidden = NO; cell.leftVideoButton.hidden = YES; cell.rightVideoButton.hidden = YES; // 复用机制 cell.rightLabel.frame = CGRectMake(10, 5, size.width, size.height); cell.rightView.frame = CGRectMake(WIDTH - 40 -size.width-25, 5, size.width + 25, size.height + 18); cell.rightLabel.text = chatItem.content; } else { cell.rightView.hidden = YES; cell.lefeView.hidden = YES; cell.rightView.hidden = YES; cell.lefeView.hidden = YES; cell.leftVideoButton.hidden = YES; cell.rightVideoButton.hidden = NO; cell.rightVideoButton.tag = 300 + indexPath.row; [cell.rightVideoButton addTarget:self action:@selector(cellSelectIndex:) forControlEvents:UIControlEventTouchUpInside]; [cell.rightVideoButton setImage:[UIImage imageNamed:@"record.png"] forState:UIControlStateNormal]; } } else // 接受得到 { cell.leftHeadImage.hidden = NO; cell.rightHeadImage.hidden = YES; if(chatItem.states == picStates) { cell.rightView.hidden = YES; cell.lefeView.hidden = YES; cell.leftVideoButton.hidden = YES; cell.rightVideoButton.hidden = YES; cell.leftPicImage.image = chatItem.picImage; cell.rightPicImage.hidden = YES; cell.leftPicImage.hidden = NO; } else if(chatItem.states == textStates) { cell.rightPicImage.hidden = YES; cell.leftPicImage.hidden = YES; cell.rightView.hidden = YES; cell.lefeView.hidden = NO; cell.leftVideoButton.hidden = YES; cell.rightVideoButton.hidden = YES; cell.leftLabel.frame = CGRectMake(15, 5, size.width, size.height); cell.lefeView.frame = CGRectMake(40, 5, size.width +30, size.height + 25); cell.leftLabel.text = chatItem.content; } else { cell.rightView.hidden = YES; cell.lefeView.hidden = YES; cell.rightView.hidden = YES; cell.lefeView.hidden = YES; cell.leftVideoButton.hidden = NO; cell.rightVideoButton.hidden = YES; cell.leftVideoButton.tag = 300 + indexPath.row; [cell.leftVideoButton setImage:[UIImage imageNamed:@"record.png"] forState:UIControlStateNormal]; [cell.leftVideoButton addTarget:self action:@selector(cellSelectIndex:) forControlEvents:UIControlEventTouchUpInside]; } } return cell; } - (void)cellSelectIndex:(UIButton *)cellBtn { ChatItem *chatIden = [self.datasource objectAtIndex:cellBtn.tag - 300]; if(chatIden.states == videoStates) { NSLog(@"realy play"); // [self makeVideoPlayer:[self getVideoStremData]]; [self makeVideoPlayer:chatIden.recordData]; } } #pragma mark 下面是核心的连接MCSession 和 数据返回的地方 /***************************-------**********************************************/ - (void)makeBlueData { // 这是为了让 在block中弱引用 __weak typeof (self) weakSelf = self; self.datasource = [NSMutableArray arrayWithCapacity:0]; // 初始化 会议室 self.sessionManager = [[BlueSessionManager alloc]initWithDisplayName:[NSString stringWithFormat:@" %@", [[UIDevice currentDevice] name]]]; // [self.sessionManager didReceiveInvitationFromPeer:^void(MCPeerID *peer, NSData *context) { __strong typeof (weakSelf) strongSelf = weakSelf; UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"是否连接?" message:[NSString stringWithFormat:@"同 %@%@", peer.displayName, @" 连接?"] delegate:strongSelf cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil]; [alertView show]; }]; [self.sessionManager peerConnectionStatusOnMainQueue:YES block:^(MCPeerID *peer, MCSessionState state) { if(state == MCSessionStateConnected) { UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"已经连接" message:[NSString stringWithFormat:@"现在连接 %@了!", peer.displayName] delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil]; [alertView show]; } }]; // 发正常数据的返回 [self.sessionManager receiveDataOnMainQueue:YES block:^(NSData *data, MCPeerID *peer) { __strong typeof (weakSelf) strongSelf = weakSelf; NSString *string = [NSKeyedUnarchiver unarchiveObjectWithData:data]; ChatItem * chatItem = [[ChatItem alloc] init]; chatItem.isSelf = NO; chatItem.states = textStates; chatItem.content = string; [strongSelf.datasource addObject:chatItem]; // 加到数组里面 [strongSelf insertTheTableToButtom]; }]; // 发图片之后的返回 [self.sessionManager receiveFinalResourceOnMainQueue:YES complete:^(NSString *name, MCPeerID *peer, NSURL *url, NSError *error) { __strong typeof (weakSelf) strongSelf = weakSelf; NSData *data = [NSData dataWithContentsOfURL:url]; ChatItem * chatItem = [[ChatItem alloc] init]; chatItem.isSelf = NO; chatItem.states = picStates; chatItem.content = name; chatItem.picImage = [UIImage imageWithData:data]; [strongSelf.datasource addObject:chatItem]; [strongSelf insertTheTableToButtom]; }]; // 流 [self.sessionManager didReceiveStreamFromPeer:^(NSInputStream *stream, MCPeerID *peer, NSString *streamName) { __strong typeof (weakSelf) strongSelf = weakSelf; strongSelf.inputStream = stream; strongSelf.inputStream.delegate = self; [strongSelf.inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; [strongSelf.inputStream open]; NSLog(@"we need"); }]; } - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { [self.sessionManager connectToPeer:buttonIndex == 1]; } #pragma mark 下面是流的传输 /***********--------------------- 下面是流的传输 ------------------------***********************************/ - (void)videoRecord { // 播放录音 [self SetTempRecordView]; } - (void)sendAsStream { if(!self.sessionManager.isConnected) { UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"蓝牙已经断开了,请重新连接!" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil]; [alertView show]; return; } NSError *err; self.outputStream = [self.sessionManager streamWithName:@"super stream" toPeer:self.sessionManager.firstPeer error:&err]; self.outputStream.delegate = self; [self.outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; if(err || !self.outputStream) { NSLog(@"%@", err); } else { [self.outputStream open]; } } // 下面是一个代理 - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { if(eventCode == NSStreamEventHasBytesAvailable) { // 有可读的字节,接收到了数据 NSInputStream *input = (NSInputStream *)aStream; uint8_t buffer[1024]; NSInteger length = [input read:buffer maxLength:1024]; [self.streamData appendBytes:(const void *)buffer length:(NSUInteger)length]; // 记住这边的数据陆陆续续的 } else if(eventCode == NSStreamEventHasSpaceAvailable) { // 可以使用输出流的空间,此时可以发送数据给服务器 // 发送数据的 NSData *data = [self getVideoStremData]; ChatItem * chatItem = [[ChatItem alloc] init]; chatItem.isSelf = YES; chatItem.states = videoStates; chatItem.recordData = data; [self.datasource addObject:chatItem]; [self insertTheTableToButtom]; NSOutputStream *output = (NSOutputStream *)aStream; [output write:data.bytes maxLength:data.length]; [output close]; } if(eventCode == NSStreamEventEndEncountered) { // 流结束事件,在此事件中负责做销毁工作 // 同时也是获得最终数据的好地方 ChatItem * chatItem = [[ChatItem alloc] init]; chatItem.isSelf = NO; chatItem.states = videoStates; chatItem.recordData = self.streamData; [self.datasource addObject:chatItem]; [self insertTheTableToButtom]; [aStream close]; [aStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; if([aStream isKindOfClass:[NSInputStream class]]) { self.streamData = nil; } } if(eventCode == NSStreamEventErrorOccurred) { // 发生错误 NSLog(@"error"); } } - (NSMutableData *)streamData { if(!_streamData) { _streamData = [NSMutableData data]; } return _streamData; } /***********----------------------- 公用的数据 --------------------***********************************/ - (NSData *)imageData { return [NSData dataWithContentsOfURL:[self imageURL]]; } - (NSURL *)imageURL { NSString *path = [[NSBundle mainBundle]pathForResource:@"301-alien-ship@2x" ofType:@"png"]; // 这儿有个技术点 // 那个如何将 image转化成 路径 NSURL *url = [NSURL fileURLWithPath:path]; return url; } /***********----------------------------------------------***********************************/ #pragma mark 尝试空白处的连接 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch * touch = [[event allTouches] anyObject]; if(touch.tapCount >= 1) { [self.sendTextView resignFirstResponder]; } } /***********-------------------语音---------------------------***********************************/ #pragma mark 尝试语音的录制和播出 - (void)buildVideoForWe { // 设置录音会话 [self setAudioSession]; } - (void)SetTempRecordView { _backRemindRecordView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, WIDTH, 150)]; _backRemindRecordView.center = self.view.center; _backRemindRecordView.backgroundColor = [UIColor lightGrayColor]; [self.view addSubview:_backRemindRecordView]; UILabel * beginLabel = [[UILabel alloc] initWithFrame:CGRectMake(60, 50, WIDTH -120, 50)]; beginLabel.backgroundColor = [UIColor greenColor]; beginLabel.text = @"长按录音开始···"; beginLabel.tag = 1001; beginLabel.textAlignment = NSTextAlignmentCenter; beginLabel.userInteractionEnabled = YES; [_backRemindRecordView addSubview:beginLabel]; UILongPressGestureRecognizer * longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressNextDo:)]; [beginLabel addGestureRecognizer:longPress]; } - (void)longPressNextDo:(UILongPressGestureRecognizer * )longPress { if(longPress.state == UIGestureRecognizerStateBegan) { NSLog(@"begin"); UILabel * label = (UILabel *)[_backRemindRecordView viewWithTag:1001]; label.text = @"录音正在进行中···"; label.backgroundColor = [UIColor orangeColor]; [self BeginRecordClick]; } if(longPress.state == UIGestureRecognizerStateEnded) { [self OkStopClick]; [_backRemindRecordView removeFromSuperview]; [self sendAsStream]; NSLog(@"stop"); } } #pragma mark - 私有方法 /** * 设置音频会话 */ -(void)setAudioSession { AVAudioSession *audioSession=[AVAudioSession sharedInstance]; //设置为播放和录音状态,以便可以在录制完之后播放录音 [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; [audioSession setActive:YES error:nil]; } /** * 取得录音文件保存路径 * * @return 录音文件路径 */ -(NSURL *)getSavePath { NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; urlStr=[urlStr stringByAppendingPathComponent:kRecordAudioFile]; NSLog(@"file path:%@",urlStr); NSURL *url=[NSURL fileURLWithPath:urlStr]; return url; } - (NSData *)getVideoStremData { return [NSData dataWithContentsOfURL:[self getSavePath]]; } /** * 取得录音文件设置 * * @return 录音设置 */ -(NSDictionary *)getAudioSetting{ NSMutableDictionary *dicM=[NSMutableDictionary dictionary]; //设置录音格式 [dicM setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey]; //设置录音采样率,8000是电话采样率,对于一般录音已经够了 [dicM setObject:@(8000) forKey:AVSampleRateKey]; //设置通道,这里采用单声道 [dicM setObject:@(1) forKey:AVNumberOfChannelsKey]; //每个采样点位数,分为8、16、24、32 [dicM setObject:@(8) forKey:AVLinearPCMBitDepthKey]; //是否使用浮点数采样 [dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey]; //....其他设置等 return dicM; } /** * 获得录音机对象 * * @return 录音机对象 */ -(AVAudioRecorder *)audioRecorder { if (!_audioRecorder) { //创建录音文件保存路径 NSURL *url=[self getSavePath]; //创建录音格式设置 NSDictionary *setting=[self getAudioSetting]; //创建录音机 NSError *error=nil; _audioRecorder=[[AVAudioRecorder alloc]initWithURL:url settings:setting error:&error]; _audioRecorder.delegate=self; _audioRecorder.meteringEnabled=YES;//如果要监控声波则必须设置为YES if (error) { NSLog(@"创建录音机对象时发生错误,错误信息:%@",error.localizedDescription); return nil; } } return _audioRecorder; } /** * 创建播放器 * * @return 播放器 */ - (void)makeVideoPlayer:(NSData *)data { NSError *error=nil; self.audioPlayer=[[AVAudioPlayer alloc]initWithData:data error:&error]; self.audioPlayer.delegate = self; self.audioPlayer.numberOfLoops=0; [self.audioPlayer prepareToPlay]; if (error) { NSLog(@"创建播放器过程中发生错误,错误信息:%@",error.localizedDescription); } else { if (![self.audioPlayer isPlaying]) { NSLog(@"play"); [self.audioPlayer play]; } } } /** * 录音声波监控定制器 * * @return 定时器 */ -(NSTimer *)timer{ if (!_timer) { _timer=[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(audioPowerChange) userInfo:nil repeats:YES]; } return _timer; } /** * 录音声波状态设置 */ -(void)audioPowerChange{ [self.audioRecorder updateMeters];//更新测量值 float power= [self.audioRecorder averagePowerForChannel:0];//取得第一个通道的音频,注意音频强度范围时-160到0 CGFloat progress=(1.0/160.0)*(power+160.0); [self.audioPower setProgress:progress]; } #pragma mark - UI事件 /** * 点击录音按钮 * * @param sender 录音按钮 */ - (void)BeginRecordClick { if (![self.audioRecorder isRecording]) { [self.audioRecorder record];//首次使用应用时如果调用record方法会询问用户是否允许使用麦克风 self.timer.fireDate=[NSDate distantPast]; } } /** * 点击暂定按钮 * * @param sender 暂停按钮 */ - (void)StopPauseClick { if ([self.audioRecorder isRecording]) { [self.audioRecorder pause]; self.timer.fireDate=[NSDate distantFuture]; } } /** * 点击停止按钮 * * @param sender 停止按钮 */ - (void)OkStopClick { [self.audioRecorder stop]; self.timer.fireDate=[NSDate distantFuture]; self.audioPower.progress=0.0; } #pragma mark - 录音机代理方法 /** * 录音完成,录音完成后播放录音 * * @param recorder 录音机对象 * @param flag 是否成功 */ -(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag { // if (![self.audioPlayer isPlaying]) { // [self.audioPlayer play]; // } NSLog(@"录音完成!"); } - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag { // 每次完成后都将这个对象释放 player =nil; }
建一个model类:
.h #import <UIKit/UIKit.h> //@protocol CellSelectIndex <NSObject> // //- (void)cellSelectIndex; // //@end @interface ChatCell : UITableViewCell @property(nonatomic,strong)UIImageView * lefeView; @property(nonatomic,strong)UIImageView * rightView; @property(nonatomic,strong)UILabel * leftLabel; @property(nonatomic,strong)UILabel * rightLabel; @property(nonatomic,strong)UIImageView * leftHeadImage; @property(nonatomic,strong)UIImageView * rightHeadImage; @property(nonatomic,strong)UIImageView * leftPicImage; @property(nonatomic,strong)UIImageView * rightPicImage; @property(nonatomic ,strong)UIButton * leftVideoButton; @property(nonatomic, strong)UIButton * rightVideoButton; //@property(nonatomic,weak)id <CellSelectIndex> delegate; // 不能用名字相同的属性 // 记住自动的时候,讲一下 weak and strong @end .m #import "ChatCell.h" @implementation ChatCell - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { // Initialization code [self makeView]; } return self; } -(void)makeView { UIImage * leftImgae = [UIImage imageNamed:@"ReceiverTextNodeBkg.png"]; UIImage * rightImage = [UIImage imageNamed:@"SenderTextNodeBkg.png"]; //这里设定一行一像素 当图片拉伸的时候,只放大两个像素 leftImgae = [leftImgae stretchableImageWithLeftCapWidth:30 topCapHeight:35]; // 找一行一列的像素 rightImage = [rightImage stretchableImageWithLeftCapWidth:30 topCapHeight:35]; // 设定完了后生成了一个新的image; //----------------------------------------------------------------------------------------// // 左边头像 self.leftHeadImage = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 30, 30)]; self.leftHeadImage.layer.masksToBounds = YES; self.leftHeadImage.layer.cornerRadius = 12; self.leftHeadImage.image = [UIImage imageNamed:@"f-pCert.png"]; [self.contentView addSubview:self.leftHeadImage]; //左边气泡 self.leftVideoButton = [UIButton buttonWithType:UIButtonTypeCustom]; self.leftVideoButton.frame = CGRectMake(40, 5, 35, 35); // [self.leftVideoButton addTarget:self action:@selector(recordTheVoice) forControlEvents:UIControlEventTouchUpInside]; [self.contentView addSubview:self.leftVideoButton]; self.leftPicImage = [[UIImageView alloc] initWithFrame:CGRectMake(40, 5, 66, 30)]; [self.contentView addSubview:self.leftPicImage]; self.lefeView = [[UIImageView alloc] initWithFrame:CGRectMake(40, 5, 66, 30)]; self.lefeView.image = leftImgae; // 这里不是一个小像素的图片?? [self.contentView addSubview:self.lefeView]; self.leftLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 5, 1, 1)]; self.leftLabel.font = [UIFont systemFontOfSize:14]; self.leftLabel.numberOfLines = 0; // 换行 self.leftLabel.backgroundColor = [UIColor clearColor];// 设置透明的 [self.lefeView addSubview:self.leftLabel]; //----------------------------------------------------------------------------------------// self.rightHeadImage = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.size.width - 35, 5, 30, 30)]; self.rightHeadImage.layer.masksToBounds = YES; self.rightHeadImage.layer.cornerRadius = 12; self.rightHeadImage.image = [UIImage imageNamed:@"f-plove.png"]; [self.contentView addSubview:self.rightHeadImage]; self.rightVideoButton = [UIButton buttonWithType:UIButtonTypeCustom]; self.rightVideoButton.frame = CGRectMake(self.frame.size.width - 45 - 40, 5, 35, 35); // [self.rightVideoButton addTarget:self action:@selector(recordTheVoice) forControlEvents:UIControlEventTouchUpInside]; [self.contentView addSubview:self.rightVideoButton]; self.rightPicImage = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.size.width - 45 - 30, 5, 30, 30)]; [self.contentView addSubview:self.rightPicImage]; // 右边 self.rightView = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.size.width - (66+40), 5, 66, 30)]; self.rightView.image = rightImage; [self.contentView addSubview:self.rightView]; self.rightLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 5, 1, 1)]; self.rightLabel.font = [UIFont systemFontOfSize:14]; self.rightLabel.backgroundColor = [UIColor clearColor]; self.rightLabel.numberOfLines = 0; [self.rightView addSubview:self.rightLabel]; } //- (void)recordTheVoice //{ // [self.delegate cellSelectIndex]; //}每日更新关注:https://weibo.com/hjq995 新浪微博
基本知识必须了解的
MCAdvertiserAssistant //可以接收,并处理用户请求连接的响应。没有回调,会弹出默认的提示框,并处理连接。
MCNearbyServiceAdvertiser //可以接收,并处理用户请求连接的响应。但是,这个类会有回调,告知有用户要与您的设备连接,然后可以自定义提示框,以及自定义连接处理。
MCNearbyServiceBrowser //用于搜索附近的用户,并可以对搜索到的用户发出邀请加入某个会话中。
MCPeerID //这表明是一个用户
MCSession //启用和管理Multipeer连接会话中的所有人之间的沟通。 通过Sesion,给别人发送数据。
建一个类用来写所有方法:
.h #import <Foundation/Foundation.h> @import MultipeerConnectivity; @interface BlueSessionManager : NSObject @property(strong, nonatomic, readonly) NSArray *connectedPeers; @property(strong, nonatomic, readonly) MCSession *session; @property(nonatomic, readonly, getter = isConnected) BOOL connected; @property(strong, nonatomic) NSDictionary *discoveryInfo; // 发现设备的特征 @property(strong, nonatomic, readonly) MCPeerID *firstPeer; // 第一个连接的 用户 /** * The service type provided for browsing and advertising. * This should be a short text string that describes the * app's networking protocol. Should be something * in the form of `tjl_appname`. */ @property(strong, nonatomic) NSString *serviceType; /* 初始化一个 假设的用户名字 */ - (instancetype)__attribute__((nonnull(1)))initWithDisplayName:(NSString *)displayName; - (void)browseForProgrammaticDiscovery; - (void)advertiseForBrowserViewController; - (void)advertiseForProgrammaticDiscovery; - (void)didReceiveInvitationFromPeer:(void (^)(MCPeerID *peer, NSData *context))invite; - (NSError *)sendDataToAllPeers:(NSData *)data; - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers; - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers withMode:(MCSessionSendDataMode)mode; - (void)receiveDataOnMainQueue:(BOOL)mainQueue block:(void (^)(NSData *data, MCPeerID *peer))dataBlock; - (NSProgress *)sendResourceWithName:(NSString *)name atURL:(NSURL *)url toPeer:(MCPeerID *)peer complete:(void (^)(NSError *error))compelete; - (void)receiveFinalResourceOnMainQueue:(BOOL)mainQueue complete:(void (^)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error))block; - (void)startReceivingResourceOnMainQueue:(BOOL)mainQueue block:(void (^)(NSString *name, MCPeerID *peer, NSProgress *progress))block; - (NSOutputStream *)streamWithName:(NSString *)name toPeer:(MCPeerID *)peerID error:(NSError * __autoreleasing *)error; - (void)didReceiveStreamFromPeer:(void (^)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName))streamBlock; - (void)peerConnectionStatusOnMainQueue:(BOOL)mainQueue block:(void (^)(MCPeerID *peer, MCSessionState state))status; - (void)browseWithControllerInViewController:(UIViewController *)controller connected:(void (^)(void))connected canceled:(void (^)(void))cancelled; - (void)didFindPeerWithInfo:(void (^)(MCPeerID *peer, NSDictionary *info))found; - (void)connectToPeer:(BOOL)connect; // 邀请其他设备连接 - (void)invitePeerToConnect:(MCPeerID *)peer connected:(void (^)(void))connected; - (void)disconnectSession; // 停止打开自己 - (void)stopAdvertising; // 停止扫描 - (void)stopBrowsing; @end .m #import "BlueSessionManager.h" #define ServiceType @"MyService" /* MCAdvertiserAssistant //可以接收,并处理用户请求连接的响应。没有回调,会弹出默认的提示框,并处理连接。 MCNearbyServiceAdvertiser //可以接收,并处理用户请求连接的响应。但是,这个类会有回调,告知有用户要与您的设备连接,然后可以自定义提示框,以及自定义连接处理。 MCNearbyServiceBrowser //用于搜索附近的用户,并可以对搜索到的用户发出邀请加入某个会话中。 MCPeerID //这表明是一个用户 MCSession //启用和管理Multipeer连接会话中的所有人之间的沟通。 通过Sesion,给别人发送数据。 */ @interface BlueSessionManager ()<MCAdvertiserAssistantDelegate, MCNearbyServiceBrowserDelegate, MCSessionDelegate, MCBrowserViewControllerDelegate, MCNearbyServiceAdvertiserDelegate, UIAlertViewDelegate> @property(strong, nonatomic) MCSession *currentSession; // 当前会议 @property(strong, nonatomic) MCAdvertiserAssistant *advertisingAssistant; // 宣传助手 @property(strong, nonatomic) MCNearbyServiceAdvertiser *advertiser; // 服务助手 @property(strong, nonatomic) MCNearbyServiceBrowser *browser; // 搜索蓝牙者 @property(strong, nonatomic) MCPeerID *peerID; // 用户 // 以下都是用到的block @property(nonatomic, copy) void(^receiveDataBlock)(NSData *data, MCPeerID *peer); @property(nonatomic, copy) void(^receiveResourceBlock)(MCPeerID *peer, NSURL *url); @property(nonatomic, copy) void(^connectionStatus)(MCPeerID *peer, MCSessionState state); @property(nonatomic, copy) void(^browserConnected)(void); @property(nonatomic, copy) void(^browserCancelled)(void); @property(nonatomic, copy) void(^didFindPeer)(MCPeerID *peer, NSDictionary *info); @property(nonatomic, copy) void(^invitationHandler)(BOOL connect, MCSession *session); @property(nonatomic, copy) void(^inviteBlock)(MCPeerID *peer, NSData *context); @property(nonatomic, copy) void(^didStartReceivingResource)(NSString *name, MCPeerID *peer, NSProgress *progress); @property(nonatomic, copy) void(^finalResource)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error); @property(nonatomic, copy) void(^streamBlock)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName); // 各种判断 @property(nonatomic, assign) BOOL receiveOnMainQueue; @property(nonatomic, assign) BOOL statusOnMainQueue; @property(nonatomic, assign) BOOL resourceFinalOnMainQueue; @property(nonatomic, assign) BOOL resourceStart; @end @implementation BlueSessionManager #pragma mark 初始化自己 - (instancetype)initWithDisplayName:(NSString *)displayName { return [self initWithDisplayName:displayName securityIdentity:nil encryptionPreferences:MCEncryptionNone serviceType:ServiceType]; } // 为上面自定义 用户 - (instancetype)initWithDisplayName:(NSString *)displayName securityIdentity:(NSArray *)security encryptionPreferences:(MCEncryptionPreference)preference serviceType:(NSString *)type { self = [super init]; if(!self) { return nil; } self.peerID = [[MCPeerID alloc]initWithDisplayName:displayName]; self.currentSession = [[MCSession alloc]initWithPeer:self.peerID securityIdentity:security encryptionPreference:preference]; self.session.delegate = self; self.serviceType = type; return self; } #pragma mark 宣传自己 - (void)advertiseForBrowserViewController { [self advertiseForBrowserViewControllerWithDiscoveryInfo:nil]; } - (void)advertiseForBrowserViewControllerWithDiscoveryInfo:(NSDictionary *)info { // self.advertiser = [[MCNearbyServiceAdvertiser alloc]initWithPeer:self.peerID discoveryInfo:info serviceType:self.serviceType]; self.advertiser.delegate = self; [self.advertiser startAdvertisingPeer]; } - (void)advertiseForProgrammaticDiscovery { [self advertiseForProgrammaticDiscoveryWithDiscoveryInfo:nil]; } - (void)advertiseForProgrammaticDiscoveryWithDiscoveryInfo:(NSDictionary *)info { //自定义自己,为了让其他设备搜索到自己 self.advertisingAssistant = [[MCAdvertiserAssistant alloc]initWithServiceType:self.serviceType discoveryInfo:info session:self.session]; self.advertisingAssistant.delegate = self; [self.advertisingAssistant start]; } - (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession *))invitationHandler { self.invitationHandler = [invitationHandler copy]; if(self.inviteBlock) self.inviteBlock(peerID, context); } #pragma mark 下面是MCAdvertiserAssistant的两个代理 - (void)advertiserAssistantDidDismissInvitation:(MCAdvertiserAssistant *)advertiserAssistant { //TODO implement } - (void)advertiserAssitantWillPresentInvitation:(MCAdvertiserAssistant *)advertiserAssistant { //TODO implement } #pragma mark 扫描其他的设备 - (void)browseForProgrammaticDiscovery { self.browser = [[MCNearbyServiceBrowser alloc]initWithPeer:self.peerID serviceType:self.serviceType]; self.browser.delegate = self; [self.browser startBrowsingForPeers]; } #pragma mark MCNearbyServiceBrowserDelegate - (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID { //TODO implement } - (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info { if(self.didFindPeer) { self.didFindPeer(peerID, info); } } - (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error { //TODO implement } #pragma mark 参加会议 也是会议的代理 // 也是 ----- MCSessionDelegate // 这是完成会议的结果··· - (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error { if(self.resourceFinalOnMainQueue) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.finalResource) { self.finalResource(resourceName, peerID, localURL, error); } }]; } else { if(self.finalResource) { self.finalResource(resourceName, peerID, localURL, error); } } } // 这是参加 普通数据的会议 - (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID { if(self.receiveOnMainQueue) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.receiveDataBlock){ self.receiveDataBlock(data, peerID); } }]; } else { if(self.receiveDataBlock) { self.receiveDataBlock(data, peerID); } } } // 这是参加普通流的会议 - (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID { if(self.streamBlock) { self.streamBlock(stream, peerID, streamName); } } // 这是参加图片资源的会议 - (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress { if(self.resourceStart) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.didStartReceivingResource) { self.didStartReceivingResource(resourceName, peerID, progress); } }]; } else { if(self.didStartReceivingResource) { self.didStartReceivingResource(resourceName, peerID, progress); } } } // 这是不同数据,系那是不同会议时候的状态 - (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state { // 这个地方是当两个蓝牙设备一旦连接起来,就会形成的一个会议 if(self.statusOnMainQueue) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.connectionStatus) { self.connectionStatus(peerID, state); } }]; } else { if(self.connectionStatus) { self.connectionStatus(peerID, state); } } } #pragma mark send And receive //--------------------------------------------------------------------------------------------// // 发送消息 // 用户多个 - (NSError *)sendDataToAllPeers:(NSData *)data { // 普通数据的发送 return [self sendDataToAllPeers:data withMode:MCSessionSendDataReliable]; } - (NSError *)sendDataToAllPeers:(NSData *)data withMode:(MCSessionSendDataMode)mode { // 确实进入会议 NSError *error; [self.session sendData:data toPeers:self.session.connectedPeers withMode:mode error:&error]; return error; } // 用户确定一个 - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers { return [self sendData:data toPeers:peers withMode:MCSessionSendDataReliable]; } - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers withMode:(MCSessionSendDataMode)mode { NSError *error; [self.session sendData:data toPeers:peers withMode:mode error:&error]; return error; } // 接收消息 - (void)receiveDataOnMainQueue:(BOOL)mainQueue block:(void (^)(NSData *data, MCPeerID *peer))dataBlock { self.receiveDataBlock = [dataBlock copy]; self.receiveOnMainQueue = mainQueue; } //--------------------------------------------------------------------------------------------// - (NSProgress *)sendResourceWithName:(NSString *)name atURL:(NSURL *)url toPeer:(MCPeerID *)peer complete:(void (^)(NSError *error))compelete { // 图片资源数据的发送 return [self.session sendResourceAtURL:url withName:name toPeer:peer withCompletionHandler:compelete]; } // 用户连接确定 - (void)peerConnectionStatusOnMainQueue:(BOOL)mainQueue block:(void (^)(MCPeerID *peer, MCSessionState state))status { self.connectionStatus = [status copy]; self.statusOnMainQueue = mainQueue; } #pragma mark 自带的MCBrowserViewController类 - (void)browserViewControllerDidFinish:(MCBrowserViewController *)browserViewController { [browserViewController dismissViewControllerAnimated:YES completion:^{ if(self.browserConnected) self.browserConnected(); }]; } - (void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController { [browserViewController dismissViewControllerAnimated:YES completion:^{ if(self.browserCancelled) self.browserCancelled(); }]; } - (void)browseWithControllerInViewController:(UIViewController *)controller connected:(void (^)(void))connected canceled:(void (^)(void))cancelled { self.browserConnected = [connected copy]; self.browserCancelled = [cancelled copy]; // 注意这个自带的类 MCBrowserViewController *browser = [[MCBrowserViewController alloc]initWithServiceType:self.serviceType session:self.session]; browser.delegate = self; [controller presentViewController:browser animated:YES completion:nil]; } - (NSArray *)connectedPeers { return self.session.connectedPeers; } // 邀请某某连接 - (void)didReceiveInvitationFromPeer:(void (^)(MCPeerID *peer, NSData *context))invite; { self.inviteBlock = [invite copy]; } - (void)invitePeerToConnect:(MCPeerID *)peer connected:(void (^)(void))connected { [self.browser invitePeer:peer toSession:self.session withContext:nil timeout:30]; } - (void)startReceivingResourceOnMainQueue:(BOOL)mainQueue block:(void (^)(NSString *name, MCPeerID *peer, NSProgress *progress))block { self.didStartReceivingResource = [block copy]; self.resourceStart = mainQueue; } // 接收某某资源 - (void)receiveFinalResourceOnMainQueue:(BOOL)mainQueue complete:(void (^)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error))block { self.finalResource = [block copy]; self.resourceFinalOnMainQueue = mainQueue; } - (NSOutputStream *)streamWithName:(NSString *)name toPeer:(MCPeerID *)peerID error:(NSError * __autoreleasing *)error { return [self.session startStreamWithName:name toPeer:peerID error:error]; } // 开始转化为流 - (void)didReceiveStreamFromPeer:(void (^)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName))streamBlock { self.streamBlock = [streamBlock copy]; } - (void)didFindPeerWithInfo:(void (^)(MCPeerID *peer, NSDictionary *info))found { self.didFindPeer = [found copy]; } #pragma mark 一些断开的情况 - (void)disconnectSession { [self.session disconnect]; } - (void)stopAdvertising { [self.advertiser stopAdvertisingPeer]; [self.advertisingAssistant stop]; } - (void)stopBrowsing { [self.browser stopBrowsingForPeers]; } - (BOOL)isConnected { return self.session.connectedPeers && self.session.connectedPeers.count > 0; } // 是否连接 它 - (void)connectToPeer:(BOOL)connect { self.invitationHandler(connect, self.session); } - (MCSession *)session { return self.currentSession; } - (MCPeerID *)firstPeer { return self.session.connectedPeers.firstObject; } @end #import "BlueSessionManager.h" #define ServiceType @"MyService" /* MCAdvertiserAssistant //可以接收,并处理用户请求连接的响应。没有回调,会弹出默认的提示框,并处理连接。 MCNearbyServiceAdvertiser //可以接收,并处理用户请求连接的响应。但是,这个类会有回调,告知有用户要与您的设备连接,然后可以自定义提示框,以及自定义连接处理。 MCNearbyServiceBrowser //用于搜索附近的用户,并可以对搜索到的用户发出邀请加入某个会话中。 MCPeerID //这表明是一个用户 MCSession //启用和管理Multipeer连接会话中的所有人之间的沟通。 通过Sesion,给别人发送数据。 */ @interface BlueSessionManager ()<MCAdvertiserAssistantDelegate, MCNearbyServiceBrowserDelegate, MCSessionDelegate, MCBrowserViewControllerDelegate, MCNearbyServiceAdvertiserDelegate, UIAlertViewDelegate> @property(strong, nonatomic) MCSession *currentSession; // 当前会议 @property(strong, nonatomic) MCAdvertiserAssistant *advertisingAssistant; // 宣传助手 @property(strong, nonatomic) MCNearbyServiceAdvertiser *advertiser; // 服务助手 @property(strong, nonatomic) MCNearbyServiceBrowser *browser; // 搜索蓝牙者 @property(strong, nonatomic) MCPeerID *peerID; // 用户 // 以下都是用到的block @property(nonatomic, copy) void(^receiveDataBlock)(NSData *data, MCPeerID *peer); @property(nonatomic, copy) void(^receiveResourceBlock)(MCPeerID *peer, NSURL *url); @property(nonatomic, copy) void(^connectionStatus)(MCPeerID *peer, MCSessionState state); @property(nonatomic, copy) void(^browserConnected)(void); @property(nonatomic, copy) void(^browserCancelled)(void); @property(nonatomic, copy) void(^didFindPeer)(MCPeerID *peer, NSDictionary *info); @property(nonatomic, copy) void(^invitationHandler)(BOOL connect, MCSession *session); @property(nonatomic, copy) void(^inviteBlock)(MCPeerID *peer, NSData *context); @property(nonatomic, copy) void(^didStartReceivingResource)(NSString *name, MCPeerID *peer, NSProgress *progress); @property(nonatomic, copy) void(^finalResource)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error); @property(nonatomic, copy) void(^streamBlock)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName); // 各种判断 @property(nonatomic, assign) BOOL receiveOnMainQueue; @property(nonatomic, assign) BOOL statusOnMainQueue; @property(nonatomic, assign) BOOL resourceFinalOnMainQueue; @property(nonatomic, assign) BOOL resourceStart; @end @implementation BlueSessionManager #pragma mark 初始化自己 - (instancetype)initWithDisplayName:(NSString *)displayName { return [self initWithDisplayName:displayName securityIdentity:nil encryptionPreferences:MCEncryptionNone serviceType:ServiceType]; } // 为上面自定义 用户 - (instancetype)initWithDisplayName:(NSString *)displayName securityIdentity:(NSArray *)security encryptionPreferences:(MCEncryptionPreference)preference serviceType:(NSString *)type { self = [super init]; if(!self) { return nil; } self.peerID = [[MCPeerID alloc]initWithDisplayName:displayName]; self.currentSession = [[MCSession alloc]initWithPeer:self.peerID securityIdentity:security encryptionPreference:preference]; self.session.delegate = self; self.serviceType = type; return self; } #pragma mark 宣传自己 - (void)advertiseForBrowserViewController { [self advertiseForBrowserViewControllerWithDiscoveryInfo:nil]; } - (void)advertiseForBrowserViewControllerWithDiscoveryInfo:(NSDictionary *)info { // self.advertiser = [[MCNearbyServiceAdvertiser alloc]initWithPeer:self.peerID discoveryInfo:info serviceType:self.serviceType]; self.advertiser.delegate = self; [self.advertiser startAdvertisingPeer]; } - (void)advertiseForProgrammaticDiscovery { [self advertiseForProgrammaticDiscoveryWithDiscoveryInfo:nil]; } - (void)advertiseForProgrammaticDiscoveryWithDiscoveryInfo:(NSDictionary *)info { //自定义自己,为了让其他设备搜索到自己 self.advertisingAssistant = [[MCAdvertiserAssistant alloc]initWithServiceType:self.serviceType discoveryInfo:info session:self.session]; self.advertisingAssistant.delegate = self; [self.advertisingAssistant start]; } - (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession *))invitationHandler { self.invitationHandler = [invitationHandler copy]; if(self.inviteBlock) self.inviteBlock(peerID, context); } #pragma mark 下面是MCAdvertiserAssistant的两个代理 - (void)advertiserAssistantDidDismissInvitation:(MCAdvertiserAssistant *)advertiserAssistant { //TODO implement } - (void)advertiserAssitantWillPresentInvitation:(MCAdvertiserAssistant *)advertiserAssistant { //TODO implement } #pragma mark 扫描其他的设备 - (void)browseForProgrammaticDiscovery { self.browser = [[MCNearbyServiceBrowser alloc]initWithPeer:self.peerID serviceType:self.serviceType]; self.browser.delegate = self; [self.browser startBrowsingForPeers]; } #pragma mark MCNearbyServiceBrowserDelegate - (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID { //TODO implement } - (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info { if(self.didFindPeer) { self.didFindPeer(peerID, info); } } - (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error { //TODO implement } #pragma mark 参加会议 也是会议的代理 // 也是 ----- MCSessionDelegate // 这是完成会议的结果··· - (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error { if(self.resourceFinalOnMainQueue) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.finalResource) { self.finalResource(resourceName, peerID, localURL, error); } }]; } else { if(self.finalResource) { self.finalResource(resourceName, peerID, localURL, error); } } } // 这是参加 普通数据的会议 - (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID { if(self.receiveOnMainQueue) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.receiveDataBlock){ self.receiveDataBlock(data, peerID); } }]; } else { if(self.receiveDataBlock) { self.receiveDataBlock(data, peerID); } } } // 这是参加普通流的会议 - (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID { if(self.streamBlock) { self.streamBlock(stream, peerID, streamName); } } // 这是参加图片资源的会议 - (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress { if(self.resourceStart) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.didStartReceivingResource) { self.didStartReceivingResource(resourceName, peerID, progress); } }]; } else { if(self.didStartReceivingResource) { self.didStartReceivingResource(resourceName, peerID, progress); } } } // 这是不同数据,系那是不同会议时候的状态 - (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state { // 这个地方是当两个蓝牙设备一旦连接起来,就会形成的一个会议 if(self.statusOnMainQueue) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.connectionStatus) { self.connectionStatus(peerID, state); } }]; } else { if(self.connectionStatus) { self.connectionStatus(peerID, state); } } } #pragma mark send And receive //--------------------------------------------------------------------------------------------// // 发送消息 // 用户多个 - (NSError *)sendDataToAllPeers:(NSData *)data { // 普通数据的发送 return [self sendDataToAllPeers:data withMode:MCSessionSendDataReliable]; } - (NSError *)sendDataToAllPeers:(NSData *)data withMode:(MCSessionSendDataMode)mode { // 确实进入会议 NSError *error; [self.session sendData:data toPeers:self.session.connectedPeers withMode:mode error:&error]; return error; } // 用户确定一个 - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers { return [self sendData:data toPeers:peers withMode:MCSessionSendDataReliable]; } - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers withMode:(MCSessionSendDataMode)mode { NSError *error; [self.session sendData:data toPeers:peers withMode:mode error:&error]; return error; } // 接收消息 - (void)receiveDataOnMainQueue:(BOOL)mainQueue block:(void (^)(NSData *data, MCPeerID *peer))dataBlock { self.receiveDataBlock = [dataBlock copy]; self.receiveOnMainQueue = mainQueue; } //--------------------------------------------------------------------------------------------// - (NSProgress *)sendResourceWithName:(NSString *)name atURL:(NSURL *)url toPeer:(MCPeerID *)peer complete:(void (^)(NSError *error))compelete { // 图片资源数据的发送 return [self.session sendResourceAtURL:url withName:name toPeer:peer withCompletionHandler:compelete]; } // 用户连接确定 - (void)peerConnectionStatusOnMainQueue:(BOOL)mainQueue block:(void (^)(MCPeerID *peer, MCSessionState state))status { self.connectionStatus = [status copy]; self.statusOnMainQueue = mainQueue; } #pragma mark 自带的MCBrowserViewController类 - (void)browserViewControllerDidFinish:(MCBrowserViewController *)browserViewController { [browserViewController dismissViewControllerAnimated:YES completion:^{ if(self.browserConnected) self.browserConnected(); }]; } - (void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController { [browserViewController dismissViewControllerAnimated:YES completion:^{ if(self.browserCancelled) self.browserCancelled(); }]; } - (void)browseWithControllerInViewController:(UIViewController *)controller connected:(void (^)(void))connected canceled:(void (^)(void))cancelled { self.browserConnected = [connected copy]; self.browserCancelled = [cancelled copy]; // 注意这个自带的类 MCBrowserViewController *browser = [[MCBrowserViewController alloc]initWithServiceType:self.serviceType session:self.session]; browser.delegate = self; [controller presentViewController:browser animated:YES completion:nil]; } - (NSArray *)connectedPeers { return self.session.connectedPeers; } // 邀请某某连接 - (void)didReceiveInvitationFromPeer:(void (^)(MCPeerID *peer, NSData *context))invite; { self.inviteBlock = [invite copy]; } - (void)invitePeerToConnect:(MCPeerID *)peer connected:(void (^)(void))connected { [self.browser invitePeer:peer toSession:self.session withContext:nil timeout:30]; } - (void)startReceivingResourceOnMainQueue:(BOOL)mainQueue block:(void (^)(NSString *name, MCPeerID *peer, NSProgress *progress))block { self.didStartReceivingResource = [block copy]; self.resourceStart = mainQueue; } // 接收某某资源 - (void)receiveFinalResourceOnMainQueue:(BOOL)mainQueue complete:(void (^)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error))block { self.finalResource = [block copy]; self.resourceFinalOnMainQueue = mainQueue; } - (NSOutputStream *)streamWithName:(NSString *)name toPeer:(MCPeerID *)peerID error:(NSError * __autoreleasing *)error { return [self.session startStreamWithName:name toPeer:peerID error:error]; } // 开始转化为流 - (void)didReceiveStreamFromPeer:(void (^)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName))streamBlock { self.streamBlock = [streamBlock copy]; } - (void)didFindPeerWithInfo:(void (^)(MCPeerID *peer, NSDictionary *info))found { self.didFindPeer = [found copy]; } #pragma mark 一些断开的情况 - (void)disconnectSession { [self.session disconnect]; } - (void)stopAdvertising { [self.advertiser stopAdvertisingPeer]; [self.advertisingAssistant stop]; } - (void)stopBrowsing { [self.browser stopBrowsingForPeers]; } - (BOOL)isConnected { return self.session.connectedPeers && self.session.connectedPeers.count > 0; } // 是否连接 它 - (void)connectToPeer:(BOOL)connect { self.invitationHandler(connect, self.session); } - (MCSession *)session { return self.currentSession; } - (MCPeerID *)firstPeer { return self.session.connectedPeers.firstObject; } @end #import "BlueSessionManager.h" #define ServiceType @"MyService" /* MCAdvertiserAssistant //可以接收,并处理用户请求连接的响应。没有回调,会弹出默认的提示框,并处理连接。 MCNearbyServiceAdvertiser //可以接收,并处理用户请求连接的响应。但是,这个类会有回调,告知有用户要与您的设备连接,然后可以自定义提示框,以及自定义连接处理。 MCNearbyServiceBrowser //用于搜索附近的用户,并可以对搜索到的用户发出邀请加入某个会话中。 MCPeerID //这表明是一个用户 MCSession //启用和管理Multipeer连接会话中的所有人之间的沟通。 通过Sesion,给别人发送数据。 */ @interface BlueSessionManager ()<MCAdvertiserAssistantDelegate, MCNearbyServiceBrowserDelegate, MCSessionDelegate, MCBrowserViewControllerDelegate, MCNearbyServiceAdvertiserDelegate, UIAlertViewDelegate> @property(strong, nonatomic) MCSession *currentSession; // 当前会议 @property(strong, nonatomic) MCAdvertiserAssistant *advertisingAssistant; // 宣传助手 @property(strong, nonatomic) MCNearbyServiceAdvertiser *advertiser; // 服务助手 @property(strong, nonatomic) MCNearbyServiceBrowser *browser; // 搜索蓝牙者 @property(strong, nonatomic) MCPeerID *peerID; // 用户 // 以下都是用到的block @property(nonatomic, copy) void(^receiveDataBlock)(NSData *data, MCPeerID *peer); @property(nonatomic, copy) void(^receiveResourceBlock)(MCPeerID *peer, NSURL *url); @property(nonatomic, copy) void(^connectionStatus)(MCPeerID *peer, MCSessionState state); @property(nonatomic, copy) void(^browserConnected)(void); @property(nonatomic, copy) void(^browserCancelled)(void); @property(nonatomic, copy) void(^didFindPeer)(MCPeerID *peer, NSDictionary *info); @property(nonatomic, copy) void(^invitationHandler)(BOOL connect, MCSession *session); @property(nonatomic, copy) void(^inviteBlock)(MCPeerID *peer, NSData *context); @property(nonatomic, copy) void(^didStartReceivingResource)(NSString *name, MCPeerID *peer, NSProgress *progress); @property(nonatomic, copy) void(^finalResource)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error); @property(nonatomic, copy) void(^streamBlock)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName); // 各种判断 @property(nonatomic, assign) BOOL receiveOnMainQueue; @property(nonatomic, assign) BOOL statusOnMainQueue; @property(nonatomic, assign) BOOL resourceFinalOnMainQueue; @property(nonatomic, assign) BOOL resourceStart; @end @implementation BlueSessionManager #pragma mark 初始化自己 - (instancetype)initWithDisplayName:(NSString *)displayName { return [self initWithDisplayName:displayName securityIdentity:nil encryptionPreferences:MCEncryptionNone serviceType:ServiceType]; } // 为上面自定义 用户 - (instancetype)initWithDisplayName:(NSString *)displayName securityIdentity:(NSArray *)security encryptionPreferences:(MCEncryptionPreference)preference serviceType:(NSString *)type { self = [super init]; if(!self) { return nil; } self.peerID = [[MCPeerID alloc]initWithDisplayName:displayName]; self.currentSession = [[MCSession alloc]initWithPeer:self.peerID securityIdentity:security encryptionPreference:preference]; self.session.delegate = self; self.serviceType = type; return self; } #pragma mark 宣传自己 - (void)advertiseForBrowserViewController { [self advertiseForBrowserViewControllerWithDiscoveryInfo:nil]; } - (void)advertiseForBrowserViewControllerWithDiscoveryInfo:(NSDictionary *)info { // self.advertiser = [[MCNearbyServiceAdvertiser alloc]initWithPeer:self.peerID discoveryInfo:info serviceType:self.serviceType]; self.advertiser.delegate = self; [self.advertiser startAdvertisingPeer]; } - (void)advertiseForProgrammaticDiscovery { [self advertiseForProgrammaticDiscoveryWithDiscoveryInfo:nil]; } - (void)advertiseForProgrammaticDiscoveryWithDiscoveryInfo:(NSDictionary *)info { //自定义自己,为了让其他设备搜索到自己 self.advertisingAssistant = [[MCAdvertiserAssistant alloc]initWithServiceType:self.serviceType discoveryInfo:info session:self.session]; self.advertisingAssistant.delegate = self; [self.advertisingAssistant start]; } - (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession *))invitationHandler { self.invitationHandler = [invitationHandler copy]; if(self.inviteBlock) self.inviteBlock(peerID, context); } #pragma mark 下面是MCAdvertiserAssistant的两个代理 - (void)advertiserAssistantDidDismissInvitation:(MCAdvertiserAssistant *)advertiserAssistant { //TODO implement } - (void)advertiserAssitantWillPresentInvitation:(MCAdvertiserAssistant *)advertiserAssistant { //TODO implement } #pragma mark 扫描其他的设备 - (void)browseForProgrammaticDiscovery { self.browser = [[MCNearbyServiceBrowser alloc]initWithPeer:self.peerID serviceType:self.serviceType]; self.browser.delegate = self; [self.browser startBrowsingForPeers]; } #pragma mark MCNearbyServiceBrowserDelegate - (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID { //TODO implement } - (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info { if(self.didFindPeer) { self.didFindPeer(peerID, info); } } - (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error { //TODO implement } #pragma mark 参加会议 也是会议的代理 // 也是 ----- MCSessionDelegate // 这是完成会议的结果··· - (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error { if(self.resourceFinalOnMainQueue) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.finalResource) { self.finalResource(resourceName, peerID, localURL, error); } }]; } else { if(self.finalResource) { self.finalResource(resourceName, peerID, localURL, error); } } } // 这是参加 普通数据的会议 - (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID { if(self.receiveOnMainQueue) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.receiveDataBlock){ self.receiveDataBlock(data, peerID); } }]; } else { if(self.receiveDataBlock) { self.receiveDataBlock(data, peerID); } } } // 这是参加普通流的会议 - (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID { if(self.streamBlock) { self.streamBlock(stream, peerID, streamName); } } // 这是参加图片资源的会议 - (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress { if(self.resourceStart) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.didStartReceivingResource) { self.didStartReceivingResource(resourceName, peerID, progress); } }]; } else { if(self.didStartReceivingResource) { self.didStartReceivingResource(resourceName, peerID, progress); } } } // 这是不同数据,系那是不同会议时候的状态 - (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state { // 这个地方是当两个蓝牙设备一旦连接起来,就会形成的一个会议 if(self.statusOnMainQueue) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.connectionStatus) { self.connectionStatus(peerID, state); } }]; } else { if(self.connectionStatus) { self.connectionStatus(peerID, state); } } } #pragma mark send And receive //--------------------------------------------------------------------------------------------// // 发送消息 // 用户多个 - (NSError *)sendDataToAllPeers:(NSData *)data { // 普通数据的发送 return [self sendDataToAllPeers:data withMode:MCSessionSendDataReliable]; } - (NSError *)sendDataToAllPeers:(NSData *)data withMode:(MCSessionSendDataMode)mode { // 确实进入会议 NSError *error; [self.session sendData:data toPeers:self.session.connectedPeers withMode:mode error:&error]; return error; } // 用户确定一个 - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers { return [self sendData:data toPeers:peers withMode:MCSessionSendDataReliable]; } - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers withMode:(MCSessionSendDataMode)mode { NSError *error; [self.session sendData:data toPeers:peers withMode:mode error:&error]; return error; } // 接收消息 - (void)receiveDataOnMainQueue:(BOOL)mainQueue block:(void (^)(NSData *data, MCPeerID *peer))dataBlock { self.receiveDataBlock = [dataBlock copy]; self.receiveOnMainQueue = mainQueue; } //--------------------------------------------------------------------------------------------// - (NSProgress *)sendResourceWithName:(NSString *)name atURL:(NSURL *)url toPeer:(MCPeerID *)peer complete:(void (^)(NSError *error))compelete { // 图片资源数据的发送 return [self.session sendResourceAtURL:url withName:name toPeer:peer withCompletionHandler:compelete]; } // 用户连接确定 - (void)peerConnectionStatusOnMainQueue:(BOOL)mainQueue block:(void (^)(MCPeerID *peer, MCSessionState state))status { self.connectionStatus = [status copy]; self.statusOnMainQueue = mainQueue; } #pragma mark 自带的MCBrowserViewController类 - (void)browserViewControllerDidFinish:(MCBrowserViewController *)browserViewController { [browserViewController dismissViewControllerAnimated:YES completion:^{ if(self.browserConnected) self.browserConnected(); }]; } - (void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController { [browserViewController dismissViewControllerAnimated:YES completion:^{ if(self.browserCancelled) self.browserCancelled(); }]; } - (void)browseWithControllerInViewController:(UIViewController *)controller connected:(void (^)(void))connected canceled:(void (^)(void))cancelled { self.browserConnected = [connected copy]; self.browserCancelled = [cancelled copy]; // 注意这个自带的类 MCBrowserViewController *browser = [[MCBrowserViewController alloc]initWithServiceType:self.serviceType session:self.session]; browser.delegate = self; [controller presentViewController:browser animated:YES completion:nil]; } - (NSArray *)connectedPeers { return self.session.connectedPeers; } // 邀请某某连接 - (void)didReceiveInvitationFromPeer:(void (^)(MCPeerID *peer, NSData *context))invite; { self.inviteBlock = [invite copy]; } - (void)invitePeerToConnect:(MCPeerID *)peer connected:(void (^)(void))connected { [self.browser invitePeer:peer toSession:self.session withContext:nil timeout:30]; } - (void)startReceivingResourceOnMainQueue:(BOOL)mainQueue block:(void (^)(NSString *name, MCPeerID *peer, NSProgress *progress))block { self.didStartReceivingResource = [block copy]; self.resourceStart = mainQueue; } // 接收某某资源 - (void)receiveFinalResourceOnMainQueue:(BOOL)mainQueue complete:(void (^)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error))block { self.finalResource = [block copy]; self.resourceFinalOnMainQueue = mainQueue; } - (NSOutputStream *)streamWithName:(NSString *)name toPeer:(MCPeerID *)peerID error:(NSError * __autoreleasing *)error { return [self.session startStreamWithName:name toPeer:peerID error:error]; } // 开始转化为流 - (void)didReceiveStreamFromPeer:(void (^)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName))streamBlock { self.streamBlock = [streamBlock copy]; } - (void)didFindPeerWithInfo:(void (^)(MCPeerID *peer, NSDictionary *info))found { self.didFindPeer = [found copy]; } #pragma mark 一些断开的情况 - (void)disconnectSession { [self.session disconnect]; } - (void)stopAdvertising { [self.advertiser stopAdvertisingPeer]; [self.advertisingAssistant stop]; } - (void)stopBrowsing { [self.browser stopBrowsingForPeers]; } - (BOOL)isConnected { return self.session.connectedPeers && self.session.connectedPeers.count > 0; } // 是否连接 它 - (void)connectToPeer:(BOOL)connect { self.invitationHandler(connect, self.session); } - (MCSession *)session { return self.currentSession; } - (MCPeerID *)firstPeer { return self.session.connectedPeers.firstObject; } @end .m #import "BlueSessionManager.h" #define ServiceType @"MyService" /* MCAdvertiserAssistant //可以接收,并处理用户请求连接的响应。没有回调,会弹出默认的提示框,并处理连接。 MCNearbyServiceAdvertiser //可以接收,并处理用户请求连接的响应。但是,这个类会有回调,告知有用户要与您的设备连接,然后可以自定义提示框,以及自定义连接处理。 MCNearbyServiceBrowser //用于搜索附近的用户,并可以对搜索到的用户发出邀请加入某个会话中。 MCPeerID //这表明是一个用户 MCSession //启用和管理Multipeer连接会话中的所有人之间的沟通。 通过Sesion,给别人发送数据。 */ @interface BlueSessionManager ()<MCAdvertiserAssistantDelegate, MCNearbyServiceBrowserDelegate, MCSessionDelegate, MCBrowserViewControllerDelegate, MCNearbyServiceAdvertiserDelegate, UIAlertViewDelegate> @property(strong, nonatomic) MCSession *currentSession; // 当前会议 @property(strong, nonatomic) MCAdvertiserAssistant *advertisingAssistant; // 宣传助手 @property(strong, nonatomic) MCNearbyServiceAdvertiser *advertiser; // 服务助手 @property(strong, nonatomic) MCNearbyServiceBrowser *browser; // 搜索蓝牙者 @property(strong, nonatomic) MCPeerID *peerID; // 用户 // 以下都是用到的block @property(nonatomic, copy) void(^receiveDataBlock)(NSData *data, MCPeerID *peer); @property(nonatomic, copy) void(^receiveResourceBlock)(MCPeerID *peer, NSURL *url); @property(nonatomic, copy) void(^connectionStatus)(MCPeerID *peer, MCSessionState state); @property(nonatomic, copy) void(^browserConnected)(void); @property(nonatomic, copy) void(^browserCancelled)(void); @property(nonatomic, copy) void(^didFindPeer)(MCPeerID *peer, NSDictionary *info); @property(nonatomic, copy) void(^invitationHandler)(BOOL connect, MCSession *session); @property(nonatomic, copy) void(^inviteBlock)(MCPeerID *peer, NSData *context); @property(nonatomic, copy) void(^didStartReceivingResource)(NSString *name, MCPeerID *peer, NSProgress *progress); @property(nonatomic, copy) void(^finalResource)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error); @property(nonatomic, copy) void(^streamBlock)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName); // 各种判断 @property(nonatomic, assign) BOOL receiveOnMainQueue; @property(nonatomic, assign) BOOL statusOnMainQueue; @property(nonatomic, assign) BOOL resourceFinalOnMainQueue; @property(nonatomic, assign) BOOL resourceStart; @end @implementation BlueSessionManager #pragma mark 初始化自己 - (instancetype)initWithDisplayName:(NSString *)displayName { return [self initWithDisplayName:displayName securityIdentity:nil encryptionPreferences:MCEncryptionNone serviceType:ServiceType]; } // 为上面自定义 用户 - (instancetype)initWithDisplayName:(NSString *)displayName securityIdentity:(NSArray *)security encryptionPreferences:(MCEncryptionPreference)preference serviceType:(NSString *)type { self = [super init]; if(!self) { return nil; } self.peerID = [[MCPeerID alloc]initWithDisplayName:displayName]; self.currentSession = [[MCSession alloc]initWithPeer:self.peerID securityIdentity:security encryptionPreference:preference]; self.session.delegate = self; self.serviceType = type; return self; } #pragma mark 宣传自己 - (void)advertiseForBrowserViewController { [self advertiseForBrowserViewControllerWithDiscoveryInfo:nil]; } - (void)advertiseForBrowserViewControllerWithDiscoveryInfo:(NSDictionary *)info { // self.advertiser = [[MCNearbyServiceAdvertiser alloc]initWithPeer:self.peerID discoveryInfo:info serviceType:self.serviceType]; self.advertiser.delegate = self; [self.advertiser startAdvertisingPeer]; } - (void)advertiseForProgrammaticDiscovery { [self advertiseForProgrammaticDiscoveryWithDiscoveryInfo:nil]; } - (void)advertiseForProgrammaticDiscoveryWithDiscoveryInfo:(NSDictionary *)info { //自定义自己,为了让其他设备搜索到自己 self.advertisingAssistant = [[MCAdvertiserAssistant alloc]initWithServiceType:self.serviceType discoveryInfo:info session:self.session]; self.advertisingAssistant.delegate = self; [self.advertisingAssistant start]; } - (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession *))invitationHandler { self.invitationHandler = [invitationHandler copy]; if(self.inviteBlock) self.inviteBlock(peerID, context); } #pragma mark 下面是MCAdvertiserAssistant的两个代理 - (void)advertiserAssistantDidDismissInvitation:(MCAdvertiserAssistant *)advertiserAssistant { //TODO implement } - (void)advertiserAssitantWillPresentInvitation:(MCAdvertiserAssistant *)advertiserAssistant { //TODO implement } #pragma mark 扫描其他的设备 - (void)browseForProgrammaticDiscovery { self.browser = [[MCNearbyServiceBrowser alloc]initWithPeer:self.peerID serviceType:self.serviceType]; self.browser.delegate = self; [self.browser startBrowsingForPeers]; } #pragma mark MCNearbyServiceBrowserDelegate - (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID { //TODO implement } - (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info { if(self.didFindPeer) { self.didFindPeer(peerID, info); } } - (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error { //TODO implement } #pragma mark 参加会议 也是会议的代理 // 也是 ----- MCSessionDelegate // 这是完成会议的结果··· - (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error { if(self.resourceFinalOnMainQueue) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.finalResource) { self.finalResource(resourceName, peerID, localURL, error); } }]; } else { if(self.finalResource) { self.finalResource(resourceName, peerID, localURL, error); } } } // 这是参加 普通数据的会议 - (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID { if(self.receiveOnMainQueue) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.receiveDataBlock){ self.receiveDataBlock(data, peerID); } }]; } else { if(self.receiveDataBlock) { self.receiveDataBlock(data, peerID); } } } // 这是参加普通流的会议 - (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID { if(self.streamBlock) { self.streamBlock(stream, peerID, streamName); } } // 这是参加图片资源的会议 - (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress { if(self.resourceStart) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.didStartReceivingResource) { self.didStartReceivingResource(resourceName, peerID, progress); } }]; } else { if(self.didStartReceivingResource) { self.didStartReceivingResource(resourceName, peerID, progress); } } } // 这是不同数据,系那是不同会议时候的状态 - (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state { // 这个地方是当两个蓝牙设备一旦连接起来,就会形成的一个会议 if(self.statusOnMainQueue) { [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if(self.connectionStatus) { self.connectionStatus(peerID, state); } }]; } else { if(self.connectionStatus) { self.connectionStatus(peerID, state); } } } #pragma mark send And receive //--------------------------------------------------------------------------------------------// // 发送消息 // 用户多个 - (NSError *)sendDataToAllPeers:(NSData *)data { // 普通数据的发送 return [self sendDataToAllPeers:data withMode:MCSessionSendDataReliable]; } - (NSError *)sendDataToAllPeers:(NSData *)data withMode:(MCSessionSendDataMode)mode { // 确实进入会议 NSError *error; [self.session sendData:data toPeers:self.session.connectedPeers withMode:mode error:&error]; return error; } // 用户确定一个 - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers { return [self sendData:data toPeers:peers withMode:MCSessionSendDataReliable]; } - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers withMode:(MCSessionSendDataMode)mode { NSError *error; [self.session sendData:data toPeers:peers withMode:mode error:&error]; return error; } // 接收消息 - (void)receiveDataOnMainQueue:(BOOL)mainQueue block:(void (^)(NSData *data, MCPeerID *peer))dataBlock { self.receiveDataBlock = [dataBlock copy]; self.receiveOnMainQueue = mainQueue; } //--------------------------------------------------------------------------------------------// - (NSProgress *)sendResourceWithName:(NSString *)name atURL:(NSURL *)url toPeer:(MCPeerID *)peer complete:(void (^)(NSError *error))compelete { // 图片资源数据的发送 return [self.session sendResourceAtURL:url withName:name toPeer:peer withCompletionHandler:compelete]; } // 用户连接确定 - (void)peerConnectionStatusOnMainQueue:(BOOL)mainQueue block:(void (^)(MCPeerID *peer, MCSessionState state))status { self.connectionStatus = [status copy]; self.statusOnMainQueue = mainQueue; } #pragma mark 自带的MCBrowserViewController类 - (void)browserViewControllerDidFinish:(MCBrowserViewController *)browserViewController { [browserViewController dismissViewControllerAnimated:YES completion:^{ if(self.browserConnected) self.browserConnected(); }]; } - (void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController { [browserViewController dismissViewControllerAnimated:YES completion:^{ if(self.browserCancelled) self.browserCancelled(); }]; } - (void)browseWithControllerInViewController:(UIViewController *)controller connected:(void (^)(void))connected canceled:(void (^)(void))cancelled { self.browserConnected = [connected copy]; self.browserCancelled = [cancelled copy]; // 注意这个自带的类 MCBrowserViewController *browser = [[MCBrowserViewController alloc]initWithServiceType:self.serviceType session:self.session]; browser.delegate = self; [controller presentViewController:browser animated:YES completion:nil]; } - (NSArray *)connectedPeers { return self.session.connectedPeers; } // 邀请某某连接 - (void)didReceiveInvitationFromPeer:(void (^)(MCPeerID *peer, NSData *context))invite; { self.inviteBlock = [invite copy]; } - (void)invitePeerToConnect:(MCPeerID *)peer connected:(void (^)(void))connected { [self.browser invitePeer:peer toSession:self.session withContext:nil timeout:30]; } - (void)startReceivingResourceOnMainQueue:(BOOL)mainQueue block:(void (^)(NSString *name, MCPeerID *peer, NSProgress *progress))block { self.didStartReceivingResource = [block copy]; self.resourceStart = mainQueue; } // 接收某某资源 - (void)receiveFinalResourceOnMainQueue:(BOOL)mainQueue complete:(void (^)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error))block { self.finalResource = [block copy]; self.resourceFinalOnMainQueue = mainQueue; } - (NSOutputStream *)streamWithName:(NSString *)name toPeer:(MCPeerID *)peerID error:(NSError * __autoreleasing *)error { return [self.session startStreamWithName:name toPeer:peerID error:error]; } // 开始转化为流 - (void)didReceiveStreamFromPeer:(void (^)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName))streamBlock { self.streamBlock = [streamBlock copy]; } - (void)didFindPeerWithInfo:(void (^)(MCPeerID *peer, NSDictionary *info))found { self.didFindPeer = [found copy]; } #pragma mark 一些断开的情况 - (void)disconnectSession { [self.session disconnect]; } - (void)stopAdvertising { [self.advertiser stopAdvertisingPeer]; [self.advertisingAssistant stop]; } - (void)stopBrowsing { [self.browser stopBrowsingForPeers]; } - (BOOL)isConnected { return self.session.connectedPeers && self.session.connectedPeers.count > 0; } // 是否连接 它 - (void)connectToPeer:(BOOL)connect { self.invitationHandler(connect, self.session); } - (MCSession *)session { return self.currentSession; } - (MCPeerID *)firstPeer { return self.session.connectedPeers.firstObject; } @end最终效果只能真机测试,所以这里不做演示!
每日更新关注 : https://weibo.com/hjq995 新浪微博