每日更新关注:http://weibo.com/hanjunqiang 
新浪微博

整体布局如下:

 
  程序结构如右图:

每日更新关注:http://weibo.com/hanjunqiang 
新浪微博

==========================================================================

指定根视图:

  1. RootViewController * rootVC = [[RootViewController alloc] init];
  2. UINavigationController * nav = [[UINavigationController alloc] initWithRootViewController:rootVC];
  3. self.window.rootViewController= nav;

根视图:

  1. #import "RootViewController.h"
  2. #import "BlueSessionManager.h"
  3. #import "ChatCell.h"
  4. #import "ChatItem.h"
  5.  
  6. #import <AssetsLibrary/AssetsLibrary.h>
  7. #import <AVFoundation/AVFoundation.h>
  8.  
  9. #define kRecordAudioFile @"myRecord.caf"
  10.  
  11. // 判断大小
  12. #define HEIGHT [UIScreen mainScreen].bounds.size.height
  13. #define WIDTH [UIScreen mainScreen].bounds.size.width
  14. #define ChatHeight 45.0
  15.  
  16. @interface RootViewController ()<NSStreamDelegate,UITableViewDataSource,UITableViewDelegate,UITextViewDelegate,UIActionSheetDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate,AVAudioRecorderDelegate,AVAudioPlayerDelegate>
  17. {
  18. float _sendBackViewHeight;
  19. float _sendTextViewHeight;
  20.  
  21. UIImagePickerController * _picker;
  22. UIView * _backRemindRecordView;
  23. }
  24.  
  25. // DataAndBlue
  26. @property(strong, nonatomic) BlueSessionManager *sessionManager;
  27.  
  28. @property(strong, nonatomic) NSMutableArray *datasource;
  29. @property(strong, nonatomic) NSMutableArray * myDataArray;
  30.  
  31. @property(strong, nonatomic) NSMutableData *streamData;
  32. @property(strong, nonatomic) NSOutputStream *outputStream;
  33. @property(strong, nonatomic) NSInputStream *inputStream;
  34.  
  35. // UI
  36. @property(strong, nonatomic) UITableView * tableView;
  37. @property(strong, nonatomic) UIView * sendBackView;
  38. @property(strong, nonatomic) UITextView * sendTextView;
  39. @property(strong, nonatomic) UIButton * sendButton;
  40.  
  41. // 语音播放
  42. @property (nonatomic,strong) AVAudioRecorder *audioRecorder;//音频录音机
  43. //音频播放器,用于播放录音文件
  44. @property (nonatomic,strong) AVAudioPlayer *audioPlayer;
  45.  
  46. @property (nonatomic,strong) NSTimer *timer;//录音声波监控(注意这里暂时不对播放进行监控)
  47.  
  48. @property (strong, nonatomic) UIProgressView *audioPower;//音频波动
  49.  
  50. @end
  51.  
  52. @implementation RootViewController
  53.  
  54. - (void)viewDidLoad {
  55. [super viewDidLoad];
  56.  
  57. [self makeBlueData];
  58.  
  59. [self readyUI];
  60.  
  61. [self buildVideoForWe];
  62.  
  63. // Do any additional setup after loading the view
  64. }
  65.  
  66. #pragma mark 基本制作
  67.  
  68. - (void)readyUI
  69. {
  70. self.title = @"蓝牙设置";
  71. self.view.backgroundColor = [UIColor whiteColor];
  72. self.automaticallyAdjustsScrollViewInsets = NO;
  73.  
  74. NSArray * buttonTitleArray = @[@"寻找设备",@"打开天线"];
  75. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:buttonTitleArray[0] style:UIBarButtonItemStyleDone target:self action:@selector(lookOtherDevice)];
  76. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:buttonTitleArray[1] style:UIBarButtonItemStyleDone target:self action:@selector(showSelfAdvertiser)];
  77. [self makeUIView];
  78.  
  79. }
  80. - (void)lookOtherDevice
  81. {
  82. [self.sessionManager browseWithControllerInViewController:self connected:^{
  83. NSLog(@"connected");
  84. } canceled:^{
  85. NSLog(@"cancelled");
  86. }];
  87. }
  88.  
  89. - (void)showSelfAdvertiser
  90. {
  91. [self.sessionManager advertiseForBrowserViewController];
  92. }
  93.  
  94. #pragma mark 制作页面UI
  95. - (void)makeUIView
  96. {
  97. // NSLog(@"width === %f,height===== %f",WIDTH,HEIGHT);
  98.  
  99. self.myDataArray = [NSMutableArray arrayWithCapacity:0];
  100.  
  101. self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, WIDTH, HEIGHT - 64 - ChatHeight - 10)];
  102. self.tableView.delegate = self;
  103. self.tableView.dataSource = self;
  104. self.tableView.separatorStyle = UITableViewCellSelectionStyleNone;
  105.  
  106. [self.view addSubview:self.tableView];
  107.  
  108. //-------------------------------------------------------------------------//
  109.  
  110. self.sendBackView = [[UIView alloc] initWithFrame:CGRectMake(0, HEIGHT - ChatHeight, WIDTH, ChatHeight)];
  111. self.sendBackView.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1.0];
  112. [self.view addSubview:self.sendBackView];
  113.  
  114. // float heightView = self.sendBackView.frame.size.height;
  115.  
  116. self.sendTextView = [[UITextView alloc] initWithFrame:CGRectMake(10, 5, WIDTH - 10 - 90, 35)];
  117. // self.sendTextView.backgroundColor = [UIColor lightGrayColor];
  118. self.sendTextView.returnKeyType = UIReturnKeySend;
  119. self.sendTextView.font = [UIFont systemFontOfSize:17];
  120. self.sendTextView.editable = YES;
  121. self.sendTextView.delegate = self;
  122. [self.sendBackView addSubview:self.sendTextView];
  123.  
  124. UIButton * addButton = [UIButton buttonWithType:UIButtonTypeContactAdd];
  125. addButton.frame = CGRectMake(WIDTH - 85, 2, 37, 37);
  126. [addButton addTarget:self action:@selector(addNextImage) forControlEvents:UIControlEventTouchUpInside];
  127. [self.sendBackView addSubview:addButton];
  128.  
  129. self.sendButton = [UIButton buttonWithType:UIButtonTypeCustom];
  130. self.sendButton.frame = CGRectMake(WIDTH - 45, 5, 40, 30);
  131.  
  132. [self.sendButton setImage:[UIImage imageNamed:@"record.png"] forState:UIControlStateNormal];
  133. [self.sendButton addTarget:self action:@selector(videoRecord) forControlEvents:UIControlEventTouchUpInside];
  134. [self.sendBackView addSubview:self.sendButton];
  135.  
  136. // 增加通知
  137. [self addTheNoticeForKeyDownUp];
  138.  
  139. }
  140.  
  141. #pragma mark 图片的传输---------///////
  142.  
  143. - (void)addNextImage
  144. {
  145.  
  146. UIActionSheet *chooseImageSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"照相机",@"相册", nil];
  147. [chooseImageSheet showInView:self.view];
  148. }
  149.  
  150. #pragma mark UIActionSheetDelegate Method
  151. -(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
  152. {
  153. _picker = [[UIImagePickerController alloc] init];
  154. _picker.delegate = self;
  155.  
  156. switch (buttonIndex) {
  157. case 0://Take picture
  158.  
  159. if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
  160. {
  161.  
  162. _picker.sourceType = UIImagePickerControllerSourceTypeCamera;
  163. }
  164.  
  165. [self presentViewController:_picker animated:NO completion:nil];
  166.  
  167. break;
  168.  
  169. case 1:
  170. //From album
  171. _picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
  172.  
  173. [self presentViewController:_picker animated:NO completion:^{
  174.  
  175. // 改变状态栏的颜色 为正常 这是这个独有的地方需要处理的
  176. [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];
  177. }];
  178. break;
  179.  
  180. default:
  181.  
  182. break;
  183. }
  184. }
  185.  
  186. // 相册
  187. -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
  188. {
  189.  
  190. NSString *type = [info objectForKey:UIImagePickerControllerMediaType];
  191. //当选择的类型是图片
  192. if ([type isEqualToString:@"public.image"])
  193. {
  194. //先把图片转成NSData
  195. UIImage* image = [info objectForKey:UIImagePickerControllerOriginalImage];
  196. NSData *data;
  197. if (UIImagePNGRepresentation(image) == nil)
  198. {
  199. data = UIImageJPEGRepresentation(image, 1.0);
  200. }
  201. else
  202. {
  203. data = UIImagePNGRepresentation(image);
  204. }
  205.  
  206. //图片保存的路径
  207. //这里将图片放在沙盒的documents文件夹中
  208. NSString * DocumentsPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
  209.  
  210. //文件管理器
  211. NSFileManager *fileManager = [NSFileManager defaultManager];
  212.  
  213. //把刚刚图片转换的data对象拷贝至沙盒中 并保存为image.png
  214. [fileManager createDirectoryAtPath:DocumentsPath withIntermediateDirectories:YES attributes:nil error:nil];
  215. [fileManager createFileAtPath:[DocumentsPath stringByAppendingString:@"/image.png"] contents:data attributes:nil];
  216.  
  217. //得到选择后沙盒中图片的完整路径
  218. NSString * filePath = [[NSString alloc]initWithFormat:@"%@%@",DocumentsPath, @"/image.png"];
  219.  
  220. [_picker dismissViewControllerAnimated:NO completion:^{
  221.  
  222. // 改变状态栏的颜色 改变为白色
  223. [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
  224.  
  225. // 这边是真正的发送
  226. if(!self.sessionManager.isConnected)
  227. {
  228. UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"蓝牙已经断开了,请重新连接!" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil];
  229. [alertView show];
  230. return;
  231. }
  232.  
  233. ChatItem * chatItem = [[ChatItem alloc] init];
  234. chatItem.isSelf = YES;
  235. chatItem.states = picStates;
  236. chatItem.picImage = image;
  237. [self.datasource addObject:chatItem];
  238.  
  239. [self insertTheTableToButtom];
  240.  
  241. [self sendAsResource:filePath];
  242.  
  243. }];
  244. }
  245.  
  246. }
  247. - (void)sendAsResource:(NSString *)path
  248. {
  249.  
  250. NSLog(@"dispaly ====%@",self.sessionManager.firstPeer.displayName);
  251. NSString * name = [NSString stringWithFormat:@"%@ForPic",[[UIDevice currentDevice] name]];
  252. NSURL * url = [NSURL fileURLWithPath:path];
  253.  
  254. NSProgress *progress = [self.sessionManager sendResourceWithName:name atURL:url toPeer:self.sessionManager.firstPeer complete:^(NSError *error) {
  255. if(!error) {
  256. NSLog(@"finished sending resource");
  257. }
  258. else {
  259. NSLog(@"%@", error);
  260. }
  261. }];
  262. NSLog(@"%@", @(progress.fractionCompleted));
  263. }
  264.  
  265. #pragma mark 普通数据的传输
  266. - (void)sendWeNeedNews
  267. {
  268. if(!self.sessionManager.isConnected)
  269. {
  270. UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"蓝牙已经断开了,请重新连接!" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil];
  271. [alertView show];
  272. return;
  273. }
  274. if([self.sendTextView.text isEqualToString:@""])
  275. {
  276. return;
  277. }
  278.  
  279. ChatItem * chatItem = [[ChatItem alloc] init];
  280. chatItem.isSelf = YES;
  281. chatItem.states = textStates;
  282. chatItem.content = self.sendTextView.text;
  283. [self.datasource addObject:chatItem];
  284. // 加到数组里面
  285.  
  286. // 添加行 indexPath描述位置的具体信息
  287. [self insertTheTableToButtom];
  288.  
  289. NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.sendTextView.text];
  290. NSError *error = [self.sessionManager sendDataToAllPeers:data];
  291. if(!error) {
  292. //there was no error.
  293. }
  294. else {
  295. NSLog(@"%@", error);
  296. }
  297.  
  298. [self returnTheNewBack];
  299. }
  300. - (void)returnTheNewBack
  301. {
  302. // 归零
  303. self.sendTextView.text = @"";
  304. [self.sendTextView resignFirstResponder];
  305. self.tableView.frame = CGRectMake(0, 64, WIDTH, HEIGHT - 64 - ChatHeight - 10 );
  306. self.sendBackView.frame = CGRectMake(0, HEIGHT - ChatHeight , WIDTH, ChatHeight);
  307.  
  308. }
  309.  
  310. // 这是一种很好的键盘下移方式
  311. -(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
  312. {
  313.  
  314. if ([text isEqualToString:@"\n"]) {
  315.  
  316. [self sendWeNeedNews];
  317.  
  318. return NO;
  319. }
  320.  
  321. return YES;
  322. }
  323.  
  324. - (void)textViewDidChange:(UITextView *)textView
  325. {
  326.  
  327. // 随机改变其高度
  328.  
  329. float textHeight = [self heightForString:textView.text fontSize:16 andWidth:textView.frame.size.width];
  330. _sendTextViewHeight = textHeight;
  331. // NSLog(@"teztheight ===== %f",textHeight);
  332.  
  333. self.sendTextView.frame = CGRectMake(10, 5, WIDTH - 10 - 90, _sendTextViewHeight);
  334. self.sendBackView.frame = CGRectMake(0, HEIGHT - _sendBackViewHeight - _sendTextViewHeight - 10, WIDTH, _sendTextViewHeight + 10);
  335.  
  336. }
  337.  
  338. - (float) heightForString:(NSString *)value fontSize:(float)fontSize andWidth:(float)width
  339. {
  340. UITextView *detailTextView = [[UITextView alloc]initWithFrame:CGRectMake(0, 0, width, 0)];
  341. detailTextView.font = [UIFont systemFontOfSize:fontSize];
  342. detailTextView.text = value;
  343. CGSize deSize = [detailTextView sizeThatFits:CGSizeMake(width,CGFLOAT_MAX)];
  344. return deSize.height;
  345. }
  346.  
  347. #pragma mark 以下是为了配合 键盘上移的变化
  348.  
  349. - (void)addTheNoticeForKeyDownUp
  350. {
  351. [ [NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleKeyBoardDidShow:) name:UIKeyboardDidShowNotification object:nil];
  352. [ [NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleKeyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
  353. }
  354.  
  355. - (void)dealloc
  356. {
  357. [[NSNotificationCenter defaultCenter] removeObserver:self];
  358. }
  359.  
  360. -(void)handleKeyBoardDidShow:(NSNotification *)paramNotification
  361. {
  362. CGSize size = [[paramNotification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
  363.  
  364. _sendBackViewHeight = size.height;
  365.  
  366. [UIView animateWithDuration:0.000001 animations:^{
  367. self.tableView.frame = CGRectMake(0, 64, WIDTH, HEIGHT - 64 - ChatHeight - size.height);
  368. self.sendBackView.frame = CGRectMake(0, HEIGHT - ChatHeight - size.height, WIDTH, ChatHeight);
  369.  
  370. }];
  371.  
  372. }
  373. -(void)handleKeyboardWillHide:(NSNotification *)paramNotification
  374. {
  375. [UIView animateWithDuration:0.1 animations:^{
  376. if(_sendTextViewHeight > 0)
  377. {
  378. self.tableView.frame = CGRectMake(0, 64, WIDTH, HEIGHT - 64 - _sendTextViewHeight + 10 );
  379. self.sendBackView.frame = CGRectMake(0, HEIGHT - _sendTextViewHeight - 10, WIDTH, _sendTextViewHeight + 10);
  380. }
  381. else
  382. {
  383. self.tableView.frame = CGRectMake(0, 64, WIDTH, HEIGHT - 64 - ChatHeight - 10 );
  384. self.sendBackView.frame = CGRectMake(0, HEIGHT - ChatHeight , WIDTH, ChatHeight);
  385. }
  386.  
  387. }];
  388. }
  389. /*--------------------------------------------------------------------------------------------*/
  390. - (void)insertTheTableToButtom
  391. {
  392. // 哪一组 哪一段
  393. NSIndexPath * indexPath = [NSIndexPath indexPathForRow:self.datasource.count- 1 inSection:0];
  394. // 添加新的一行
  395. [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
  396. // 滑动到底部 第二个参数是滑动到底部
  397. [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
  398. }
  399.  
  400. #pragma mark tableView 代理
  401. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  402. return 1;
  403. }
  404.  
  405. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  406. return self.datasource.count;
  407. }
  408.  
  409. -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  410. {
  411. ChatItem * chatItem = [self.datasource objectAtIndex:indexPath.row];
  412. if(chatItem.states == picStates)
  413. {
  414. NSLog(@"widht====%f,height======%f",chatItem.picImage.size.width,chatItem.picImage.size.height);
  415. return 50;
  416.  
  417. }
  418. else if(chatItem.states == textStates)
  419. {
  420. CGSize size = [chatItem.content boundingRectWithSize:CGSizeMake(250, 1000) options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName :[UIFont systemFontOfSize:14]} context:nil].size;
  421.  
  422. return size.height + 20 + 10; // 与view的距离 + 与Cell的距离
  423. }
  424. else
  425. {
  426. return 50;
  427. }
  428.  
  429. }
  430.  
  431. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  432. {
  433.  
  434. static NSString * iden = @"iden";
  435. ChatCell * cell = [tableView dequeueReusableCellWithIdentifier:iden];
  436. if(cell == nil)
  437. {
  438. cell = [[ChatCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:iden];
  439. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  440. // 让后面选中的没有阴影效果
  441.  
  442. }
  443.  
  444. // 模型
  445.  
  446. ChatItem * chatItem = [self.datasource objectAtIndex:indexPath.row];
  447.  
  448. CGSize size = [chatItem.content boundingRectWithSize:CGSizeMake(250, 1000) options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName :[UIFont systemFontOfSize:14]} context:nil].size;
  449.  
  450. //如果自己发的
  451. if(chatItem.isSelf)
  452. {
  453. cell.leftHeadImage.hidden = YES;
  454. cell.rightHeadImage.hidden = NO;
  455.  
  456. if(chatItem.states == picStates)
  457. {
  458. cell.lefeView.hidden = YES;
  459. cell.rightView.hidden = YES;
  460.  
  461. cell.rightPicImage.image = chatItem.picImage;
  462. cell.leftPicImage.hidden = YES;
  463. cell.rightPicImage.hidden = NO;
  464.  
  465. cell.leftVideoButton.hidden = YES;
  466. cell.rightVideoButton.hidden = YES;
  467.  
  468. NSLog(@"self send");
  469.  
  470. }
  471. else if(chatItem.states == textStates)
  472. {
  473. cell.rightPicImage.hidden = YES;
  474. cell.leftPicImage.hidden = YES;
  475.  
  476. cell.lefeView.hidden = YES;
  477. cell.rightView.hidden = NO;
  478.  
  479. cell.leftVideoButton.hidden = YES;
  480. cell.rightVideoButton.hidden = YES;
  481. // 复用机制
  482. cell.rightLabel.frame = CGRectMake(10, 5, size.width, size.height);
  483. cell.rightView.frame = CGRectMake(WIDTH - 40 -size.width-25, 5, size.width + 25, size.height + 18);
  484. cell.rightLabel.text = chatItem.content;
  485. }
  486. else
  487. {
  488. cell.rightView.hidden = YES;
  489. cell.lefeView.hidden = YES;
  490. cell.rightView.hidden = YES;
  491. cell.lefeView.hidden = YES;
  492.  
  493. cell.leftVideoButton.hidden = YES;
  494. cell.rightVideoButton.hidden = NO;
  495.  
  496. cell.rightVideoButton.tag = 300 + indexPath.row;
  497. [cell.rightVideoButton addTarget:self action:@selector(cellSelectIndex:) forControlEvents:UIControlEventTouchUpInside];
  498. [cell.rightVideoButton setImage:[UIImage imageNamed:@"record.png"] forState:UIControlStateNormal];
  499.  
  500. }
  501.  
  502. }
  503. else // 接受得到
  504. {
  505. cell.leftHeadImage.hidden = NO;
  506. cell.rightHeadImage.hidden = YES;
  507.  
  508. if(chatItem.states == picStates)
  509. {
  510. cell.rightView.hidden = YES;
  511. cell.lefeView.hidden = YES;
  512.  
  513. cell.leftVideoButton.hidden = YES;
  514. cell.rightVideoButton.hidden = YES;
  515.  
  516. cell.leftPicImage.image = chatItem.picImage;
  517. cell.rightPicImage.hidden = YES;
  518. cell.leftPicImage.hidden = NO;
  519.  
  520. }
  521. else if(chatItem.states == textStates)
  522. {
  523. cell.rightPicImage.hidden = YES;
  524. cell.leftPicImage.hidden = YES;
  525.  
  526. cell.rightView.hidden = YES;
  527. cell.lefeView.hidden = NO;
  528.  
  529. cell.leftVideoButton.hidden = YES;
  530. cell.rightVideoButton.hidden = YES;
  531.  
  532. cell.leftLabel.frame = CGRectMake(15, 5, size.width, size.height);
  533.  
  534. cell.lefeView.frame = CGRectMake(40, 5, size.width +30, size.height + 25);
  535.  
  536. cell.leftLabel.text = chatItem.content;
  537. }
  538. else
  539. {
  540. cell.rightView.hidden = YES;
  541. cell.lefeView.hidden = YES;
  542. cell.rightView.hidden = YES;
  543. cell.lefeView.hidden = YES;
  544.  
  545. cell.leftVideoButton.hidden = NO;
  546. cell.rightVideoButton.hidden = YES;
  547.  
  548. cell.leftVideoButton.tag = 300 + indexPath.row;
  549. [cell.leftVideoButton setImage:[UIImage imageNamed:@"record.png"] forState:UIControlStateNormal];
  550. [cell.leftVideoButton addTarget:self action:@selector(cellSelectIndex:) forControlEvents:UIControlEventTouchUpInside];
  551. }
  552.  
  553. }
  554.  
  555. return cell;
  556. }
  557.  
  558. - (void)cellSelectIndex:(UIButton *)cellBtn
  559. {
  560.  
  561. ChatItem *chatIden = [self.datasource objectAtIndex:cellBtn.tag - 300];
  562. if(chatIden.states == videoStates)
  563. {
  564. NSLog(@"realy play");
  565. // [self makeVideoPlayer:[self getVideoStremData]];
  566. [self makeVideoPlayer:chatIden.recordData];
  567. }
  568. }
  569.  
  570. #pragma mark 下面是核心的连接MCSession 数据返回的地方
  571.  
  572. /***************************-------**********************************************/
  573. - (void)makeBlueData
  574. {
  575. // 这是为了让 在block中弱引用
  576. __weak typeof (self) weakSelf = self;
  577. self.datasource = [NSMutableArray arrayWithCapacity:0];
  578.  
  579. // 初始化 会议室
  580. self.sessionManager = [[BlueSessionManager alloc]initWithDisplayName:[NSString stringWithFormat:@" %@", [[UIDevice currentDevice] name]]];
  581.  
  582. //
  583. [self.sessionManager didReceiveInvitationFromPeer:^void(MCPeerID *peer, NSData *context) {
  584. __strong typeof (weakSelf) strongSelf = weakSelf;
  585. UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"是否连接?" message:[NSString stringWithFormat:@"同 %@%@", peer.displayName, @" 连接?"] delegate:strongSelf cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
  586. [alertView show];
  587. }];
  588.  
  589. [self.sessionManager peerConnectionStatusOnMainQueue:YES block:^(MCPeerID *peer, MCSessionState state) {
  590. if(state == MCSessionStateConnected) {
  591. UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"已经连接" message:[NSString stringWithFormat:@"现在连接 %@了!", peer.displayName] delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil];
  592. [alertView show];
  593. }
  594. }];
  595.  
  596. // 发正常数据的返回
  597. [self.sessionManager receiveDataOnMainQueue:YES block:^(NSData *data, MCPeerID *peer) {
  598.  
  599. __strong typeof (weakSelf) strongSelf = weakSelf;
  600.  
  601. NSString *string = [NSKeyedUnarchiver unarchiveObjectWithData:data];
  602.  
  603. ChatItem * chatItem = [[ChatItem alloc] init];
  604. chatItem.isSelf = NO;
  605. chatItem.states = textStates;
  606. chatItem.content = string;
  607. [strongSelf.datasource addObject:chatItem];
  608. // 加到数组里面
  609.  
  610. [strongSelf insertTheTableToButtom];
  611.  
  612. }];
  613.  
  614. // 发图片之后的返回
  615. [self.sessionManager receiveFinalResourceOnMainQueue:YES complete:^(NSString *name, MCPeerID *peer, NSURL *url, NSError *error) {
  616.  
  617. __strong typeof (weakSelf) strongSelf = weakSelf;
  618. NSData *data = [NSData dataWithContentsOfURL:url];
  619.  
  620. ChatItem * chatItem = [[ChatItem alloc] init];
  621. chatItem.isSelf = NO;
  622. chatItem.states = picStates;
  623. chatItem.content = name;
  624. chatItem.picImage = [UIImage imageWithData:data];
  625. [strongSelf.datasource addObject:chatItem];
  626. [strongSelf insertTheTableToButtom];
  627.  
  628. }];
  629.  
  630. // 流
  631. [self.sessionManager didReceiveStreamFromPeer:^(NSInputStream *stream, MCPeerID *peer, NSString *streamName) {
  632. __strong typeof (weakSelf) strongSelf = weakSelf;
  633. strongSelf.inputStream = stream;
  634. strongSelf.inputStream.delegate = self;
  635. [strongSelf.inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
  636. [strongSelf.inputStream open];
  637.  
  638. NSLog(@"we need");
  639.  
  640. }];
  641.  
  642. }
  643. - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
  644. {
  645. [self.sessionManager connectToPeer:buttonIndex == 1];
  646. }
  647.  
  648. #pragma mark 下面是流的传输
  649.  
  650. /***********--------------------- 下面是流的传输 ------------------------***********************************/
  651.  
  652. - (void)videoRecord
  653. {
  654. // 播放录音
  655. [self SetTempRecordView];
  656. }
  657.  
  658. - (void)sendAsStream
  659. {
  660. if(!self.sessionManager.isConnected)
  661. {
  662. UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"蓝牙已经断开了,请重新连接!" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil];
  663. [alertView show];
  664. return;
  665. }
  666.  
  667. NSError *err;
  668. self.outputStream = [self.sessionManager streamWithName:@"super stream" toPeer:self.sessionManager.firstPeer error:&err];
  669. self.outputStream.delegate = self;
  670. [self.outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
  671. if(err || !self.outputStream) {
  672. NSLog(@"%@", err);
  673. }
  674. else
  675. {
  676.  
  677. [self.outputStream open];
  678. }
  679. }
  680.  
  681. // 下面是一个代理
  682. - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
  683. {
  684.  
  685. if(eventCode == NSStreamEventHasBytesAvailable)
  686. {
  687. // 有可读的字节,接收到了数据
  688. NSInputStream *input = (NSInputStream *)aStream;
  689. uint8_t buffer[1024];
  690. NSInteger length = [input read:buffer maxLength:1024];
  691. [self.streamData appendBytes:(const void *)buffer length:(NSUInteger)length];
  692. // 记住这边的数据陆陆续续的
  693. }
  694. else if(eventCode == NSStreamEventHasSpaceAvailable)
  695. {
  696. // 可以使用输出流的空间,此时可以发送数据给服务器
  697. // 发送数据的
  698. NSData *data = [self getVideoStremData];
  699. ChatItem * chatItem = [[ChatItem alloc] init];
  700. chatItem.isSelf = YES;
  701. chatItem.states = videoStates;
  702. chatItem.recordData = data;
  703.  
  704. [self.datasource addObject:chatItem];
  705. [self insertTheTableToButtom];
  706.  
  707. NSOutputStream *output = (NSOutputStream *)aStream;
  708. [output write:data.bytes maxLength:data.length];
  709. [output close];
  710. }
  711. if(eventCode == NSStreamEventEndEncountered)
  712. {
  713. // 流结束事件,在此事件中负责做销毁工作
  714. // 同时也是获得最终数据的好地方
  715.  
  716. ChatItem * chatItem = [[ChatItem alloc] init];
  717. chatItem.isSelf = NO;
  718. chatItem.states = videoStates;
  719. chatItem.recordData = self.streamData;
  720.  
  721. [self.datasource addObject:chatItem];
  722. [self insertTheTableToButtom];
  723.  
  724. [aStream close];
  725. [aStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
  726. if([aStream isKindOfClass:[NSInputStream class]])
  727. {
  728. self.streamData = nil;
  729. }
  730.  
  731. }
  732. if(eventCode == NSStreamEventErrorOccurred)
  733. {
  734. // 发生错误
  735. NSLog(@"error");
  736. }
  737. }
  738.  
  739. - (NSMutableData *)streamData
  740. {
  741. if(!_streamData) {
  742. _streamData = [NSMutableData data];
  743. }
  744. return _streamData;
  745. }
  746.  
  747. /***********----------------------- 公用的数据 --------------------***********************************/
  748.  
  749. - (NSData *)imageData
  750. {
  751. return [NSData dataWithContentsOfURL:[self imageURL]];
  752. }
  753.  
  754. - (NSURL *)imageURL {
  755. NSString *path = [[NSBundle mainBundle]pathForResource:@"301-alien-ship@2x" ofType:@"png"];
  756. // 这儿有个技术点
  757. // 那个如何将 image转化成 路径
  758.  
  759. NSURL *url = [NSURL fileURLWithPath:path];
  760. return url;
  761. }
  762.  
  763. /***********----------------------------------------------***********************************/
  764. #pragma mark 尝试空白处的连接
  765.  
  766. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
  767. {
  768. UITouch * touch = [[event allTouches] anyObject];
  769. if(touch.tapCount >= 1)
  770. {
  771. [self.sendTextView resignFirstResponder];
  772. }
  773. }
  774.  
  775. /***********-------------------语音---------------------------***********************************/
  776.  
  777. #pragma mark 尝试语音的录制和播出
  778.  
  779. - (void)buildVideoForWe
  780. {
  781. // 设置录音会话
  782. [self setAudioSession];
  783. }
  784.  
  785. - (void)SetTempRecordView
  786. {
  787. _backRemindRecordView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, WIDTH, 150)];
  788. _backRemindRecordView.center = self.view.center;
  789. _backRemindRecordView.backgroundColor = [UIColor lightGrayColor];
  790. [self.view addSubview:_backRemindRecordView];
  791.  
  792. UILabel * beginLabel = [[UILabel alloc] initWithFrame:CGRectMake(60, 50, WIDTH -120, 50)];
  793. beginLabel.backgroundColor = [UIColor greenColor];
  794. beginLabel.text = @"长按录音开始···";
  795. beginLabel.tag = 1001;
  796. beginLabel.textAlignment = NSTextAlignmentCenter;
  797. beginLabel.userInteractionEnabled = YES;
  798. [_backRemindRecordView addSubview:beginLabel];
  799.  
  800. UILongPressGestureRecognizer * longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressNextDo:)];
  801. [beginLabel addGestureRecognizer:longPress];
  802.  
  803. }
  804.  
  805. - (void)longPressNextDo:(UILongPressGestureRecognizer * )longPress
  806. {
  807. if(longPress.state == UIGestureRecognizerStateBegan)
  808. {
  809. NSLog(@"begin");
  810. UILabel * label = (UILabel *)[_backRemindRecordView viewWithTag:1001];
  811. label.text = @"录音正在进行中···";
  812. label.backgroundColor = [UIColor orangeColor];
  813. [self BeginRecordClick];
  814. }
  815. if(longPress.state == UIGestureRecognizerStateEnded)
  816. {
  817. [self OkStopClick];
  818. [_backRemindRecordView removeFromSuperview];
  819. [self sendAsStream];
  820. NSLog(@"stop");
  821.  
  822. }
  823. }
  824.  
  825. #pragma mark - 私有方法
  826. /**
  827. * 设置音频会话
  828. */
  829. -(void)setAudioSession
  830. {
  831. AVAudioSession *audioSession=[AVAudioSession sharedInstance];
  832. //设置为播放和录音状态,以便可以在录制完之后播放录音
  833. [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
  834. [audioSession setActive:YES error:nil];
  835. }
  836.  
  837. /**
  838. * 取得录音文件保存路径
  839. *
  840. * @return 录音文件路径
  841. */
  842. -(NSURL *)getSavePath
  843. {
  844. NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
  845. urlStr=[urlStr stringByAppendingPathComponent:kRecordAudioFile];
  846. NSLog(@"file path:%@",urlStr);
  847. NSURL *url=[NSURL fileURLWithPath:urlStr];
  848. return url;
  849. }
  850.  
  851. - (NSData *)getVideoStremData
  852. {
  853. return [NSData dataWithContentsOfURL:[self getSavePath]];
  854. }
  855.  
  856. /**
  857. * 取得录音文件设置
  858. *
  859. * @return 录音设置
  860. */
  861. -(NSDictionary *)getAudioSetting{
  862. NSMutableDictionary *dicM=[NSMutableDictionary dictionary];
  863. //设置录音格式
  864. [dicM setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey];
  865. //设置录音采样率,8000是电话采样率,对于一般录音已经够了
  866. [dicM setObject:@(8000) forKey:AVSampleRateKey];
  867. //设置通道,这里采用单声道
  868. [dicM setObject:@(1) forKey:AVNumberOfChannelsKey];
  869. //每个采样点位数,分为8、16、24、32
  870. [dicM setObject:@(8) forKey:AVLinearPCMBitDepthKey];
  871. //是否使用浮点数采样
  872. [dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey];
  873. //....其他设置等
  874. return dicM;
  875. }
  876.  
  877. /**
  878. * 获得录音机对象
  879. *
  880. * @return 录音机对象
  881. */
  882. -(AVAudioRecorder *)audioRecorder
  883. {
  884. if (!_audioRecorder) {
  885. //创建录音文件保存路径
  886. NSURL *url=[self getSavePath];
  887. //创建录音格式设置
  888. NSDictionary *setting=[self getAudioSetting];
  889. //创建录音机
  890. NSError *error=nil;
  891. _audioRecorder=[[AVAudioRecorder alloc]initWithURL:url settings:setting error:&error];
  892. _audioRecorder.delegate=self;
  893. _audioRecorder.meteringEnabled=YES;//如果要监控声波则必须设置为YES
  894. if (error) {
  895. NSLog(@"创建录音机对象时发生错误,错误信息:%@",error.localizedDescription);
  896. return nil;
  897. }
  898. }
  899. return _audioRecorder;
  900. }
  901.  
  902. /**
  903. * 创建播放器
  904. *
  905. * @return 播放器
  906. */
  907.  
  908. - (void)makeVideoPlayer:(NSData *)data
  909. {
  910. NSError *error=nil;
  911. self.audioPlayer=[[AVAudioPlayer alloc]initWithData:data error:&error];
  912. self.audioPlayer.delegate = self;
  913. self.audioPlayer.numberOfLoops=0;
  914. [self.audioPlayer prepareToPlay];
  915. if (error)
  916. {
  917. NSLog(@"创建播放器过程中发生错误,错误信息:%@",error.localizedDescription);
  918.  
  919. }
  920. else
  921. {
  922. if (![self.audioPlayer isPlaying]) {
  923. NSLog(@"play");
  924. [self.audioPlayer play];
  925. }
  926.  
  927. }
  928. }
  929.  
  930. /**
  931. * 录音声波监控定制器
  932. *
  933. * @return 定时器
  934. */
  935. -(NSTimer *)timer{
  936. if (!_timer) {
  937. _timer=[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(audioPowerChange) userInfo:nil repeats:YES];
  938. }
  939. return _timer;
  940. }
  941.  
  942. /**
  943. * 录音声波状态设置
  944. */
  945. -(void)audioPowerChange{
  946. [self.audioRecorder updateMeters];//更新测量值
  947. float power= [self.audioRecorder averagePowerForChannel:0];//取得第一个通道的音频,注意音频强度范围时-160到0
  948. CGFloat progress=(1.0/160.0)*(power+160.0);
  949. [self.audioPower setProgress:progress];
  950. }
  951. #pragma mark - UI事件
  952. /**
  953. * 点击录音按钮
  954. *
  955. * @param sender 录音按钮
  956. */
  957. - (void)BeginRecordClick
  958. {
  959. if (![self.audioRecorder isRecording])
  960. {
  961. [self.audioRecorder record];//首次使用应用时如果调用record方法会询问用户是否允许使用麦克风
  962. self.timer.fireDate=[NSDate distantPast];
  963. }
  964. }
  965.  
  966. /**
  967. * 点击暂定按钮
  968. *
  969. * @param sender 暂停按钮
  970. */
  971. - (void)StopPauseClick
  972. {
  973. if ([self.audioRecorder isRecording]) {
  974. [self.audioRecorder pause];
  975. self.timer.fireDate=[NSDate distantFuture];
  976. }
  977. }
  978.  
  979. /**
  980. * 点击停止按钮
  981. *
  982. * @param sender 停止按钮
  983. */
  984. - (void)OkStopClick
  985. {
  986. [self.audioRecorder stop];
  987. self.timer.fireDate=[NSDate distantFuture];
  988. self.audioPower.progress=0.0;
  989. }
  990.  
  991. #pragma mark - 录音机代理方法
  992. /**
  993. * 录音完成,录音完成后播放录音
  994. *
  995. * @param recorder 录音机对象
  996. * @param flag 是否成功
  997. */
  998. -(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
  999. {
  1000. // if (![self.audioPlayer isPlaying]) {
  1001. // [self.audioPlayer play];
  1002. // }
  1003. NSLog(@"录音完成!");
  1004. }
  1005.  
  1006. - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
  1007. {
  1008.  
  1009. // 每次完成后都将这个对象释放
  1010. player =nil;
  1011. }

建一个model类:

  1. .h
  2. #import <UIKit/UIKit.h>
  3. //@protocol CellSelectIndex <NSObject>
  4. //
  5. //- (void)cellSelectIndex;
  6. //
  7. //@end
  8.  
  9. @interface ChatCell : UITableViewCell
  10. @property(nonatomic,strong)UIImageView * lefeView;
  11. @property(nonatomic,strong)UIImageView * rightView;
  12. @property(nonatomic,strong)UILabel * leftLabel;
  13. @property(nonatomic,strong)UILabel * rightLabel;
  14.  
  15. @property(nonatomic,strong)UIImageView * leftHeadImage;
  16. @property(nonatomic,strong)UIImageView * rightHeadImage;
  17.  
  18. @property(nonatomic,strong)UIImageView * leftPicImage;
  19. @property(nonatomic,strong)UIImageView * rightPicImage;
  20.  
  21. @property(nonatomic ,strong)UIButton * leftVideoButton;
  22. @property(nonatomic, strong)UIButton * rightVideoButton;
  23.  
  24. //@property(nonatomic,weak)id <CellSelectIndex> delegate;
  25.  
  26. // 不能用名字相同的属性
  27. // 记住自动的时候,讲一下 weak and strong
  28.  
  29. @end
  30.  
  31. .m
  32. #import "ChatCell.h"
  33.  
  34. @implementation ChatCell
  35.  
  36. - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
  37. {
  38. self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  39. if (self) {
  40. // Initialization code
  41. [self makeView];
  42. }
  43. return self;
  44. }
  45. -(void)makeView
  46. {
  47. UIImage * leftImgae = [UIImage imageNamed:@"ReceiverTextNodeBkg.png"];
  48. UIImage * rightImage = [UIImage imageNamed:@"SenderTextNodeBkg.png"];
  49.  
  50. //这里设定一行一像素 当图片拉伸的时候,只放大两个像素
  51.  
  52. leftImgae = [leftImgae stretchableImageWithLeftCapWidth:30 topCapHeight:35];
  53. // 找一行一列的像素
  54. rightImage = [rightImage stretchableImageWithLeftCapWidth:30 topCapHeight:35];
  55. // 设定完了后生成了一个新的image;
  56.  
  57. //----------------------------------------------------------------------------------------//
  58. // 左边头像
  59. self.leftHeadImage = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 30, 30)];
  60. self.leftHeadImage.layer.masksToBounds = YES;
  61. self.leftHeadImage.layer.cornerRadius = 12;
  62. self.leftHeadImage.image = [UIImage imageNamed:@"f-pCert.png"];
  63. [self.contentView addSubview:self.leftHeadImage];
  64.  
  65. //左边气泡
  66. self.leftVideoButton = [UIButton buttonWithType:UIButtonTypeCustom];
  67. self.leftVideoButton.frame = CGRectMake(40, 5, 35, 35);
  68. // [self.leftVideoButton addTarget:self action:@selector(recordTheVoice) forControlEvents:UIControlEventTouchUpInside];
  69. [self.contentView addSubview:self.leftVideoButton];
  70.  
  71. self.leftPicImage = [[UIImageView alloc] initWithFrame:CGRectMake(40, 5, 66, 30)];
  72. [self.contentView addSubview:self.leftPicImage];
  73.  
  74. self.lefeView = [[UIImageView alloc] initWithFrame:CGRectMake(40, 5, 66, 30)];
  75. self.lefeView.image = leftImgae;
  76. // 这里不是一个小像素的图片??
  77. [self.contentView addSubview:self.lefeView];
  78.  
  79. self.leftLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 5, 1, 1)];
  80. self.leftLabel.font = [UIFont systemFontOfSize:14];
  81.  
  82. self.leftLabel.numberOfLines = 0; // 换行
  83.  
  84. self.leftLabel.backgroundColor = [UIColor clearColor];// 设置透明的
  85.  
  86. [self.lefeView addSubview:self.leftLabel];
  87.  
  88. //----------------------------------------------------------------------------------------//
  89.  
  90. self.rightHeadImage = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.size.width - 35, 5, 30, 30)];
  91. self.rightHeadImage.layer.masksToBounds = YES;
  92. self.rightHeadImage.layer.cornerRadius = 12;
  93. self.rightHeadImage.image = [UIImage imageNamed:@"f-plove.png"];
  94. [self.contentView addSubview:self.rightHeadImage];
  95.  
  96. self.rightVideoButton = [UIButton buttonWithType:UIButtonTypeCustom];
  97. self.rightVideoButton.frame = CGRectMake(self.frame.size.width - 45 - 40, 5, 35, 35);
  98. // [self.rightVideoButton addTarget:self action:@selector(recordTheVoice) forControlEvents:UIControlEventTouchUpInside];
  99. [self.contentView addSubview:self.rightVideoButton];
  100.  
  101. self.rightPicImage = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.size.width - 45 - 30, 5, 30, 30)];
  102. [self.contentView addSubview:self.rightPicImage];
  103.  
  104. // 右边
  105. self.rightView = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.size.width - (66+40), 5, 66, 30)];
  106. self.rightView.image = rightImage;
  107. [self.contentView addSubview:self.rightView];
  108.  
  109. self.rightLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 5, 1, 1)];
  110. self.rightLabel.font = [UIFont systemFontOfSize:14];
  111. self.rightLabel.backgroundColor = [UIColor clearColor];
  112. self.rightLabel.numberOfLines = 0;
  113. [self.rightView addSubview:self.rightLabel];
  114.  
  115. }
  116.  
  117. //- (void)recordTheVoice
  118. //{
  119. // [self.delegate cellSelectIndex];
  120. //}

每日更新关注:http://weibo.com/hanjunqiang 
新浪微博

基本知识必须了解的

MCAdvertiserAssistant   //可以接收,并处理用户请求连接的响应。没有回调,会弹出默认的提示框,并处理连接。

MCNearbyServiceAdvertiser //可以接收,并处理用户请求连接的响应。但是,这个类会有回调,告知有用户要与您的设备连接,然后可以自定义提示框,以及自定义连接处理。

MCNearbyServiceBrowser  //用于搜索附近的用户,并可以对搜索到的用户发出邀请加入某个会话中。

MCPeerID //这表明是一个用户

MCSession //启用和管理Multipeer连接会话中的所有人之间的沟通。 通过Sesion,给别人发送数据。

建一个类用来写所有方法:

  1. .h
  2. #import <Foundation/Foundation.h>
  3.  
  4. @import MultipeerConnectivity;
  5.  
  6. @interface BlueSessionManager : NSObject
  7.  
  8. @property(strong, nonatomic, readonly) NSArray *connectedPeers;
  9.  
  10. @property(strong, nonatomic, readonly) MCSession *session;
  11.  
  12. @property(nonatomic, readonly, getter = isConnected) BOOL connected;
  13.  
  14. @property(strong, nonatomic) NSDictionary *discoveryInfo; // 发现设备的特征
  15.  
  16. @property(strong, nonatomic, readonly) MCPeerID *firstPeer; // 第一个连接的 用户
  17.  
  18. /**
  19. * The service type provided for browsing and advertising.
  20. * This should be a short text string that describes the
  21. * app's networking protocol. Should be something
  22. * in the form of `tjl_appname`.
  23. */
  24. @property(strong, nonatomic) NSString *serviceType;
  25.  
  26. /*
  27. 初始化一个 假设的用户名字
  28. */
  29. - (instancetype)__attribute__((nonnull(1)))initWithDisplayName:(NSString *)displayName;
  30.  
  31. - (void)browseForProgrammaticDiscovery;
  32.  
  33. - (void)advertiseForBrowserViewController;
  34.  
  35. - (void)advertiseForProgrammaticDiscovery;
  36.  
  37. - (void)didReceiveInvitationFromPeer:(void (^)(MCPeerID *peer, NSData *context))invite;
  38.  
  39. - (NSError *)sendDataToAllPeers:(NSData *)data;
  40. - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers;
  41.  
  42. - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers withMode:(MCSessionSendDataMode)mode;
  43.  
  44. - (void)receiveDataOnMainQueue:(BOOL)mainQueue block:(void (^)(NSData *data, MCPeerID *peer))dataBlock;
  45.  
  46. - (NSProgress *)sendResourceWithName:(NSString *)name atURL:(NSURL *)url toPeer:(MCPeerID *)peer complete:(void (^)(NSError *error))compelete;
  47.  
  48. - (void)receiveFinalResourceOnMainQueue:(BOOL)mainQueue complete:(void (^)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error))block;
  49.  
  50. - (void)startReceivingResourceOnMainQueue:(BOOL)mainQueue block:(void (^)(NSString *name, MCPeerID *peer, NSProgress *progress))block;
  51.  
  52. - (NSOutputStream *)streamWithName:(NSString *)name toPeer:(MCPeerID *)peerID error:(NSError * __autoreleasing *)error;
  53.  
  54. - (void)didReceiveStreamFromPeer:(void (^)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName))streamBlock;
  55.  
  56. - (void)peerConnectionStatusOnMainQueue:(BOOL)mainQueue block:(void (^)(MCPeerID *peer, MCSessionState state))status;
  57.  
  58. - (void)browseWithControllerInViewController:(UIViewController *)controller connected:(void (^)(void))connected canceled:(void (^)(void))cancelled;
  59.  
  60. - (void)didFindPeerWithInfo:(void (^)(MCPeerID *peer, NSDictionary *info))found;
  61. - (void)connectToPeer:(BOOL)connect;
  62.  
  63. // 邀请其他设备连接
  64. - (void)invitePeerToConnect:(MCPeerID *)peer connected:(void (^)(void))connected;
  65. - (void)disconnectSession;
  66. // 停止打开自己
  67. - (void)stopAdvertising;
  68.  
  69. // 停止扫描
  70. - (void)stopBrowsing;
  71.  
  72. @end
  73.  
  74. .m
  75. #import "BlueSessionManager.h"
  76.  
  77. #define ServiceType @"MyService"
  78.  
  79. /*
  80. MCAdvertiserAssistant //可以接收,并处理用户请求连接的响应。没有回调,会弹出默认的提示框,并处理连接。
  81. MCNearbyServiceAdvertiser //可以接收,并处理用户请求连接的响应。但是,这个类会有回调,告知有用户要与您的设备连接,然后可以自定义提示框,以及自定义连接处理。
  82. MCNearbyServiceBrowser //用于搜索附近的用户,并可以对搜索到的用户发出邀请加入某个会话中。
  83. MCPeerID //这表明是一个用户
  84. MCSession //启用和管理Multipeer连接会话中的所有人之间的沟通。 通过Sesion,给别人发送数据。
  85. */
  86. @interface BlueSessionManager ()<MCAdvertiserAssistantDelegate, MCNearbyServiceBrowserDelegate, MCSessionDelegate, MCBrowserViewControllerDelegate, MCNearbyServiceAdvertiserDelegate, UIAlertViewDelegate>
  87. @property(strong, nonatomic) MCSession *currentSession; // 当前会议
  88. @property(strong, nonatomic) MCAdvertiserAssistant *advertisingAssistant; // 宣传助手
  89. @property(strong, nonatomic) MCNearbyServiceAdvertiser *advertiser; // 服务助手
  90. @property(strong, nonatomic) MCNearbyServiceBrowser *browser; // 搜索蓝牙者
  91. @property(strong, nonatomic) MCPeerID *peerID; // 用户
  92.  
  93. // 以下都是用到的block
  94. @property(nonatomic, copy) void(^receiveDataBlock)(NSData *data, MCPeerID *peer);
  95. @property(nonatomic, copy) void(^receiveResourceBlock)(MCPeerID *peer, NSURL *url);
  96. @property(nonatomic, copy) void(^connectionStatus)(MCPeerID *peer, MCSessionState state);
  97. @property(nonatomic, copy) void(^browserConnected)(void);
  98. @property(nonatomic, copy) void(^browserCancelled)(void);
  99. @property(nonatomic, copy) void(^didFindPeer)(MCPeerID *peer, NSDictionary *info);
  100. @property(nonatomic, copy) void(^invitationHandler)(BOOL connect, MCSession *session);
  101. @property(nonatomic, copy) void(^inviteBlock)(MCPeerID *peer, NSData *context);
  102. @property(nonatomic, copy) void(^didStartReceivingResource)(NSString *name, MCPeerID *peer, NSProgress *progress);
  103. @property(nonatomic, copy) void(^finalResource)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error);
  104. @property(nonatomic, copy) void(^streamBlock)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName);
  105. // 各种判断
  106. @property(nonatomic, assign) BOOL receiveOnMainQueue;
  107. @property(nonatomic, assign) BOOL statusOnMainQueue;
  108. @property(nonatomic, assign) BOOL resourceFinalOnMainQueue;
  109. @property(nonatomic, assign) BOOL resourceStart;
  110. @end
  111. @implementation BlueSessionManager
  112. #pragma mark 初始化自己
  113. - (instancetype)initWithDisplayName:(NSString *)displayName
  114. {
  115. return [self initWithDisplayName:displayName securityIdentity:nil encryptionPreferences:MCEncryptionNone serviceType:ServiceType];
  116. }
  117. // 为上面自定义 用户
  118. - (instancetype)initWithDisplayName:(NSString *)displayName securityIdentity:(NSArray *)security encryptionPreferences:(MCEncryptionPreference)preference serviceType:(NSString *)type
  119. {
  120. self = [super init];
  121. if(!self) {
  122. return nil;
  123. }
  124. self.peerID = [[MCPeerID alloc]initWithDisplayName:displayName];
  125. self.currentSession = [[MCSession alloc]initWithPeer:self.peerID securityIdentity:security encryptionPreference:preference];
  126. self.session.delegate = self;
  127. self.serviceType = type;
  128. return self;
  129. }
  130. #pragma mark 宣传自己
  131. - (void)advertiseForBrowserViewController
  132. {
  133. [self advertiseForBrowserViewControllerWithDiscoveryInfo:nil];
  134. }
  135.  
  136. - (void)advertiseForBrowserViewControllerWithDiscoveryInfo:(NSDictionary *)info
  137. {
  138. //
  139. self.advertiser = [[MCNearbyServiceAdvertiser alloc]initWithPeer:self.peerID discoveryInfo:info serviceType:self.serviceType];
  140. self.advertiser.delegate = self;
  141. [self.advertiser startAdvertisingPeer];
  142. }
  143.  
  144. - (void)advertiseForProgrammaticDiscovery
  145. {
  146. [self advertiseForProgrammaticDiscoveryWithDiscoveryInfo:nil];
  147. }
  148.  
  149. - (void)advertiseForProgrammaticDiscoveryWithDiscoveryInfo:(NSDictionary *)info
  150. {
  151. //自定义自己,为了让其他设备搜索到自己
  152. self.advertisingAssistant = [[MCAdvertiserAssistant alloc]initWithServiceType:self.serviceType discoveryInfo:info session:self.session];
  153. self.advertisingAssistant.delegate = self;
  154. [self.advertisingAssistant start];
  155. }
  156.  
  157. - (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession *))invitationHandler {
  158. self.invitationHandler = [invitationHandler copy];
  159. if(self.inviteBlock) self.inviteBlock(peerID, context);
  160. }
  161.  
  162. #pragma mark 下面是MCAdvertiserAssistant的两个代理
  163. - (void)advertiserAssistantDidDismissInvitation:(MCAdvertiserAssistant *)advertiserAssistant
  164. {
  165. //TODO implement
  166. }
  167.  
  168. - (void)advertiserAssitantWillPresentInvitation:(MCAdvertiserAssistant *)advertiserAssistant {
  169. //TODO implement
  170. }
  171.  
  172. #pragma mark 扫描其他的设备
  173. - (void)browseForProgrammaticDiscovery
  174. {
  175.  
  176. self.browser = [[MCNearbyServiceBrowser alloc]initWithPeer:self.peerID serviceType:self.serviceType];
  177. self.browser.delegate = self;
  178. [self.browser startBrowsingForPeers];
  179.  
  180. }
  181.  
  182. #pragma mark MCNearbyServiceBrowserDelegate
  183. - (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID
  184. {
  185. //TODO implement
  186. }
  187.  
  188. - (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info
  189. {
  190. if(self.didFindPeer)
  191. {
  192. self.didFindPeer(peerID, info);
  193. }
  194. }
  195.  
  196. - (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error
  197. {
  198. //TODO implement
  199. }
  200.  
  201. #pragma mark 参加会议 也是会议的代理
  202. // 也是 ----- MCSessionDelegate
  203.  
  204. // 这是完成会议的结果···
  205. - (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error
  206. {
  207. if(self.resourceFinalOnMainQueue)
  208. {
  209. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  210. if(self.finalResource)
  211. {
  212. self.finalResource(resourceName, peerID, localURL, error);
  213. }
  214. }];
  215. }
  216. else
  217. {
  218. if(self.finalResource)
  219. {
  220. self.finalResource(resourceName, peerID, localURL, error);
  221. }
  222. }
  223. }
  224. // 这是参加 普通数据的会议
  225. - (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID
  226. {
  227. if(self.receiveOnMainQueue)
  228. {
  229. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  230. if(self.receiveDataBlock){
  231. self.receiveDataBlock(data, peerID);
  232. }
  233. }];
  234. }
  235. else
  236. {
  237. if(self.receiveDataBlock)
  238. {
  239. self.receiveDataBlock(data, peerID);
  240. }
  241. }
  242. }
  243. // 这是参加普通流的会议
  244. - (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID
  245. {
  246. if(self.streamBlock)
  247. {
  248. self.streamBlock(stream, peerID, streamName);
  249. }
  250. }
  251. // 这是参加图片资源的会议
  252. - (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress
  253. {
  254. if(self.resourceStart)
  255. {
  256.  
  257. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  258. if(self.didStartReceivingResource)
  259. {
  260. self.didStartReceivingResource(resourceName, peerID, progress);
  261. }
  262. }];
  263. }
  264. else
  265. {
  266.  
  267. if(self.didStartReceivingResource)
  268. {
  269. self.didStartReceivingResource(resourceName, peerID, progress);
  270. }
  271. }
  272. }
  273.  
  274. // 这是不同数据,系那是不同会议时候的状态
  275.  
  276. - (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state
  277. {
  278. // 这个地方是当两个蓝牙设备一旦连接起来,就会形成的一个会议
  279. if(self.statusOnMainQueue)
  280. {
  281. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  282. if(self.connectionStatus)
  283. {
  284. self.connectionStatus(peerID, state);
  285. }
  286. }];
  287. }
  288. else
  289. {
  290. if(self.connectionStatus)
  291. {
  292. self.connectionStatus(peerID, state);
  293. }
  294. }
  295. }
  296.  
  297. #pragma mark send And receive
  298. //--------------------------------------------------------------------------------------------//
  299. // 发送消息
  300. // 用户多个
  301. - (NSError *)sendDataToAllPeers:(NSData *)data
  302. {
  303. // 普通数据的发送
  304. return [self sendDataToAllPeers:data withMode:MCSessionSendDataReliable];
  305. }
  306.  
  307. - (NSError *)sendDataToAllPeers:(NSData *)data withMode:(MCSessionSendDataMode)mode
  308. {
  309. // 确实进入会议
  310. NSError *error;
  311. [self.session sendData:data toPeers:self.session.connectedPeers withMode:mode error:&error];
  312. return error;
  313. }
  314.  
  315. // 用户确定一个
  316. - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers
  317. {
  318. return [self sendData:data toPeers:peers withMode:MCSessionSendDataReliable];
  319. }
  320.  
  321. - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers withMode:(MCSessionSendDataMode)mode
  322. {
  323. NSError *error;
  324. [self.session sendData:data toPeers:peers withMode:mode error:&error];
  325. return error;
  326. }
  327.  
  328. // 接收消息
  329. - (void)receiveDataOnMainQueue:(BOOL)mainQueue block:(void (^)(NSData *data, MCPeerID *peer))dataBlock
  330. {
  331.  
  332. self.receiveDataBlock = [dataBlock copy];
  333. self.receiveOnMainQueue = mainQueue;
  334. }
  335. //--------------------------------------------------------------------------------------------//
  336.  
  337. - (NSProgress *)sendResourceWithName:(NSString *)name atURL:(NSURL *)url toPeer:(MCPeerID *)peer complete:(void (^)(NSError *error))compelete
  338. {
  339. // 图片资源数据的发送
  340.  
  341. return [self.session sendResourceAtURL:url withName:name toPeer:peer withCompletionHandler:compelete];
  342. }
  343.  
  344. // 用户连接确定
  345. - (void)peerConnectionStatusOnMainQueue:(BOOL)mainQueue block:(void (^)(MCPeerID *peer, MCSessionState state))status
  346. {
  347. self.connectionStatus = [status copy];
  348. self.statusOnMainQueue = mainQueue;
  349. }
  350.  
  351. #pragma mark 自带的MCBrowserViewController
  352. - (void)browserViewControllerDidFinish:(MCBrowserViewController *)browserViewController
  353. {
  354. [browserViewController dismissViewControllerAnimated:YES completion:^{
  355. if(self.browserConnected) self.browserConnected();
  356. }];
  357. }
  358.  
  359. - (void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController
  360. {
  361. [browserViewController dismissViewControllerAnimated:YES completion:^{
  362. if(self.browserCancelled) self.browserCancelled();
  363. }];
  364. }
  365.  
  366. - (void)browseWithControllerInViewController:(UIViewController *)controller connected:(void (^)(void))connected canceled:(void (^)(void))cancelled
  367. {
  368. self.browserConnected = [connected copy];
  369. self.browserCancelled = [cancelled copy];
  370. // 注意这个自带的类
  371. MCBrowserViewController *browser = [[MCBrowserViewController alloc]initWithServiceType:self.serviceType session:self.session];
  372. browser.delegate = self;
  373. [controller presentViewController:browser animated:YES completion:nil];
  374. }
  375.  
  376. - (NSArray *)connectedPeers
  377. {
  378. return self.session.connectedPeers;
  379. }
  380.  
  381. // 邀请某某连接
  382. - (void)didReceiveInvitationFromPeer:(void (^)(MCPeerID *peer, NSData *context))invite;
  383. {
  384. self.inviteBlock = [invite copy];
  385. }
  386.  
  387. - (void)invitePeerToConnect:(MCPeerID *)peer connected:(void (^)(void))connected
  388. {
  389. [self.browser invitePeer:peer toSession:self.session withContext:nil timeout:30];
  390. }
  391.  
  392. - (void)startReceivingResourceOnMainQueue:(BOOL)mainQueue block:(void (^)(NSString *name, MCPeerID *peer, NSProgress *progress))block
  393. {
  394.  
  395. self.didStartReceivingResource = [block copy];
  396. self.resourceStart = mainQueue;
  397. }
  398. // 接收某某资源
  399. - (void)receiveFinalResourceOnMainQueue:(BOOL)mainQueue complete:(void (^)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error))block
  400. {
  401. self.finalResource = [block copy];
  402. self.resourceFinalOnMainQueue = mainQueue;
  403. }
  404. - (NSOutputStream *)streamWithName:(NSString *)name toPeer:(MCPeerID *)peerID error:(NSError * __autoreleasing *)error
  405. {
  406. return [self.session startStreamWithName:name toPeer:peerID error:error];
  407. }
  408. // 开始转化为流
  409. - (void)didReceiveStreamFromPeer:(void (^)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName))streamBlock
  410. {
  411. self.streamBlock = [streamBlock copy];
  412. }
  413. - (void)didFindPeerWithInfo:(void (^)(MCPeerID *peer, NSDictionary *info))found
  414. {
  415. self.didFindPeer = [found copy];
  416. }
  417. #pragma mark 一些断开的情况
  418. - (void)disconnectSession
  419. {
  420. [self.session disconnect];
  421. }
  422.  
  423. - (void)stopAdvertising
  424. {
  425. [self.advertiser stopAdvertisingPeer];
  426. [self.advertisingAssistant stop];
  427. }
  428.  
  429. - (void)stopBrowsing
  430. {
  431. [self.browser stopBrowsingForPeers];
  432. }
  433.  
  434. - (BOOL)isConnected {
  435. return self.session.connectedPeers && self.session.connectedPeers.count > 0;
  436. }
  437.  
  438. // 是否连接 它
  439. - (void)connectToPeer:(BOOL)connect {
  440. self.invitationHandler(connect, self.session);
  441. }
  442.  
  443. - (MCSession *)session {
  444. return self.currentSession;
  445. }
  446.  
  447. - (MCPeerID *)firstPeer {
  448. return self.session.connectedPeers.firstObject;
  449. }
  450.  
  451. @end
  452.  
  453. #import "BlueSessionManager.h"
  454.  
  455. #define ServiceType @"MyService"
  456.  
  457. /*
  458. MCAdvertiserAssistant //可以接收,并处理用户请求连接的响应。没有回调,会弹出默认的提示框,并处理连接。
  459. MCNearbyServiceAdvertiser //可以接收,并处理用户请求连接的响应。但是,这个类会有回调,告知有用户要与您的设备连接,然后可以自定义提示框,以及自定义连接处理。
  460. MCNearbyServiceBrowser //用于搜索附近的用户,并可以对搜索到的用户发出邀请加入某个会话中。
  461. MCPeerID //这表明是一个用户
  462. MCSession //启用和管理Multipeer连接会话中的所有人之间的沟通。 通过Sesion,给别人发送数据。
  463. */
  464. @interface BlueSessionManager ()<MCAdvertiserAssistantDelegate, MCNearbyServiceBrowserDelegate, MCSessionDelegate, MCBrowserViewControllerDelegate, MCNearbyServiceAdvertiserDelegate, UIAlertViewDelegate>
  465. @property(strong, nonatomic) MCSession *currentSession; // 当前会议
  466. @property(strong, nonatomic) MCAdvertiserAssistant *advertisingAssistant; // 宣传助手
  467. @property(strong, nonatomic) MCNearbyServiceAdvertiser *advertiser; // 服务助手
  468. @property(strong, nonatomic) MCNearbyServiceBrowser *browser; // 搜索蓝牙者
  469. @property(strong, nonatomic) MCPeerID *peerID; // 用户
  470.  
  471. // 以下都是用到的block
  472. @property(nonatomic, copy) void(^receiveDataBlock)(NSData *data, MCPeerID *peer);
  473. @property(nonatomic, copy) void(^receiveResourceBlock)(MCPeerID *peer, NSURL *url);
  474. @property(nonatomic, copy) void(^connectionStatus)(MCPeerID *peer, MCSessionState state);
  475. @property(nonatomic, copy) void(^browserConnected)(void);
  476. @property(nonatomic, copy) void(^browserCancelled)(void);
  477. @property(nonatomic, copy) void(^didFindPeer)(MCPeerID *peer, NSDictionary *info);
  478. @property(nonatomic, copy) void(^invitationHandler)(BOOL connect, MCSession *session);
  479. @property(nonatomic, copy) void(^inviteBlock)(MCPeerID *peer, NSData *context);
  480. @property(nonatomic, copy) void(^didStartReceivingResource)(NSString *name, MCPeerID *peer, NSProgress *progress);
  481. @property(nonatomic, copy) void(^finalResource)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error);
  482. @property(nonatomic, copy) void(^streamBlock)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName);
  483. // 各种判断
  484. @property(nonatomic, assign) BOOL receiveOnMainQueue;
  485. @property(nonatomic, assign) BOOL statusOnMainQueue;
  486. @property(nonatomic, assign) BOOL resourceFinalOnMainQueue;
  487. @property(nonatomic, assign) BOOL resourceStart;
  488. @end
  489. @implementation BlueSessionManager
  490. #pragma mark 初始化自己
  491. - (instancetype)initWithDisplayName:(NSString *)displayName
  492. {
  493. return [self initWithDisplayName:displayName securityIdentity:nil encryptionPreferences:MCEncryptionNone serviceType:ServiceType];
  494. }
  495. // 为上面自定义 用户
  496. - (instancetype)initWithDisplayName:(NSString *)displayName securityIdentity:(NSArray *)security encryptionPreferences:(MCEncryptionPreference)preference serviceType:(NSString *)type
  497. {
  498. self = [super init];
  499. if(!self) {
  500. return nil;
  501. }
  502. self.peerID = [[MCPeerID alloc]initWithDisplayName:displayName];
  503. self.currentSession = [[MCSession alloc]initWithPeer:self.peerID securityIdentity:security encryptionPreference:preference];
  504. self.session.delegate = self;
  505. self.serviceType = type;
  506. return self;
  507. }
  508. #pragma mark 宣传自己
  509. - (void)advertiseForBrowserViewController
  510. {
  511. [self advertiseForBrowserViewControllerWithDiscoveryInfo:nil];
  512. }
  513.  
  514. - (void)advertiseForBrowserViewControllerWithDiscoveryInfo:(NSDictionary *)info
  515. {
  516. //
  517. self.advertiser = [[MCNearbyServiceAdvertiser alloc]initWithPeer:self.peerID discoveryInfo:info serviceType:self.serviceType];
  518. self.advertiser.delegate = self;
  519. [self.advertiser startAdvertisingPeer];
  520. }
  521.  
  522. - (void)advertiseForProgrammaticDiscovery
  523. {
  524. [self advertiseForProgrammaticDiscoveryWithDiscoveryInfo:nil];
  525. }
  526.  
  527. - (void)advertiseForProgrammaticDiscoveryWithDiscoveryInfo:(NSDictionary *)info
  528. {
  529. //自定义自己,为了让其他设备搜索到自己
  530. self.advertisingAssistant = [[MCAdvertiserAssistant alloc]initWithServiceType:self.serviceType discoveryInfo:info session:self.session];
  531. self.advertisingAssistant.delegate = self;
  532. [self.advertisingAssistant start];
  533. }
  534.  
  535. - (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession *))invitationHandler {
  536. self.invitationHandler = [invitationHandler copy];
  537. if(self.inviteBlock) self.inviteBlock(peerID, context);
  538. }
  539.  
  540. #pragma mark 下面是MCAdvertiserAssistant的两个代理
  541. - (void)advertiserAssistantDidDismissInvitation:(MCAdvertiserAssistant *)advertiserAssistant
  542. {
  543. //TODO implement
  544. }
  545.  
  546. - (void)advertiserAssitantWillPresentInvitation:(MCAdvertiserAssistant *)advertiserAssistant {
  547. //TODO implement
  548. }
  549.  
  550. #pragma mark 扫描其他的设备
  551. - (void)browseForProgrammaticDiscovery
  552. {
  553.  
  554. self.browser = [[MCNearbyServiceBrowser alloc]initWithPeer:self.peerID serviceType:self.serviceType];
  555. self.browser.delegate = self;
  556. [self.browser startBrowsingForPeers];
  557.  
  558. }
  559.  
  560. #pragma mark MCNearbyServiceBrowserDelegate
  561. - (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID
  562. {
  563. //TODO implement
  564. }
  565.  
  566. - (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info
  567. {
  568. if(self.didFindPeer)
  569. {
  570. self.didFindPeer(peerID, info);
  571. }
  572. }
  573.  
  574. - (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error
  575. {
  576. //TODO implement
  577. }
  578.  
  579. #pragma mark 参加会议 也是会议的代理
  580. // 也是 ----- MCSessionDelegate
  581.  
  582. // 这是完成会议的结果···
  583. - (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error
  584. {
  585. if(self.resourceFinalOnMainQueue)
  586. {
  587. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  588. if(self.finalResource)
  589. {
  590. self.finalResource(resourceName, peerID, localURL, error);
  591. }
  592. }];
  593. }
  594. else
  595. {
  596. if(self.finalResource)
  597. {
  598. self.finalResource(resourceName, peerID, localURL, error);
  599. }
  600. }
  601. }
  602. // 这是参加 普通数据的会议
  603. - (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID
  604. {
  605. if(self.receiveOnMainQueue)
  606. {
  607. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  608. if(self.receiveDataBlock){
  609. self.receiveDataBlock(data, peerID);
  610. }
  611. }];
  612. }
  613. else
  614. {
  615. if(self.receiveDataBlock)
  616. {
  617. self.receiveDataBlock(data, peerID);
  618. }
  619. }
  620. }
  621. // 这是参加普通流的会议
  622. - (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID
  623. {
  624. if(self.streamBlock)
  625. {
  626. self.streamBlock(stream, peerID, streamName);
  627. }
  628. }
  629. // 这是参加图片资源的会议
  630. - (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress
  631. {
  632. if(self.resourceStart)
  633. {
  634.  
  635. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  636. if(self.didStartReceivingResource)
  637. {
  638. self.didStartReceivingResource(resourceName, peerID, progress);
  639. }
  640. }];
  641. }
  642. else
  643. {
  644.  
  645. if(self.didStartReceivingResource)
  646. {
  647. self.didStartReceivingResource(resourceName, peerID, progress);
  648. }
  649. }
  650. }
  651.  
  652. // 这是不同数据,系那是不同会议时候的状态
  653.  
  654. - (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state
  655. {
  656. // 这个地方是当两个蓝牙设备一旦连接起来,就会形成的一个会议
  657. if(self.statusOnMainQueue)
  658. {
  659. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  660. if(self.connectionStatus)
  661. {
  662. self.connectionStatus(peerID, state);
  663. }
  664. }];
  665. }
  666. else
  667. {
  668. if(self.connectionStatus)
  669. {
  670. self.connectionStatus(peerID, state);
  671. }
  672. }
  673. }
  674.  
  675. #pragma mark send And receive
  676. //--------------------------------------------------------------------------------------------//
  677. // 发送消息
  678. // 用户多个
  679. - (NSError *)sendDataToAllPeers:(NSData *)data
  680. {
  681. // 普通数据的发送
  682. return [self sendDataToAllPeers:data withMode:MCSessionSendDataReliable];
  683. }
  684.  
  685. - (NSError *)sendDataToAllPeers:(NSData *)data withMode:(MCSessionSendDataMode)mode
  686. {
  687. // 确实进入会议
  688. NSError *error;
  689. [self.session sendData:data toPeers:self.session.connectedPeers withMode:mode error:&error];
  690. return error;
  691. }
  692.  
  693. // 用户确定一个
  694. - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers
  695. {
  696. return [self sendData:data toPeers:peers withMode:MCSessionSendDataReliable];
  697. }
  698.  
  699. - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers withMode:(MCSessionSendDataMode)mode
  700. {
  701. NSError *error;
  702. [self.session sendData:data toPeers:peers withMode:mode error:&error];
  703. return error;
  704. }
  705.  
  706. // 接收消息
  707. - (void)receiveDataOnMainQueue:(BOOL)mainQueue block:(void (^)(NSData *data, MCPeerID *peer))dataBlock
  708. {
  709.  
  710. self.receiveDataBlock = [dataBlock copy];
  711. self.receiveOnMainQueue = mainQueue;
  712. }
  713. //--------------------------------------------------------------------------------------------//
  714.  
  715. - (NSProgress *)sendResourceWithName:(NSString *)name atURL:(NSURL *)url toPeer:(MCPeerID *)peer complete:(void (^)(NSError *error))compelete
  716. {
  717. // 图片资源数据的发送
  718.  
  719. return [self.session sendResourceAtURL:url withName:name toPeer:peer withCompletionHandler:compelete];
  720. }
  721.  
  722. // 用户连接确定
  723. - (void)peerConnectionStatusOnMainQueue:(BOOL)mainQueue block:(void (^)(MCPeerID *peer, MCSessionState state))status
  724. {
  725. self.connectionStatus = [status copy];
  726. self.statusOnMainQueue = mainQueue;
  727. }
  728.  
  729. #pragma mark 自带的MCBrowserViewController
  730. - (void)browserViewControllerDidFinish:(MCBrowserViewController *)browserViewController
  731. {
  732. [browserViewController dismissViewControllerAnimated:YES completion:^{
  733. if(self.browserConnected) self.browserConnected();
  734. }];
  735. }
  736.  
  737. - (void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController
  738. {
  739. [browserViewController dismissViewControllerAnimated:YES completion:^{
  740. if(self.browserCancelled) self.browserCancelled();
  741. }];
  742. }
  743.  
  744. - (void)browseWithControllerInViewController:(UIViewController *)controller connected:(void (^)(void))connected canceled:(void (^)(void))cancelled
  745. {
  746. self.browserConnected = [connected copy];
  747. self.browserCancelled = [cancelled copy];
  748. // 注意这个自带的类
  749. MCBrowserViewController *browser = [[MCBrowserViewController alloc]initWithServiceType:self.serviceType session:self.session];
  750. browser.delegate = self;
  751. [controller presentViewController:browser animated:YES completion:nil];
  752. }
  753.  
  754. - (NSArray *)connectedPeers
  755. {
  756. return self.session.connectedPeers;
  757. }
  758.  
  759. // 邀请某某连接
  760. - (void)didReceiveInvitationFromPeer:(void (^)(MCPeerID *peer, NSData *context))invite;
  761. {
  762. self.inviteBlock = [invite copy];
  763. }
  764.  
  765. - (void)invitePeerToConnect:(MCPeerID *)peer connected:(void (^)(void))connected
  766. {
  767. [self.browser invitePeer:peer toSession:self.session withContext:nil timeout:30];
  768. }
  769.  
  770. - (void)startReceivingResourceOnMainQueue:(BOOL)mainQueue block:(void (^)(NSString *name, MCPeerID *peer, NSProgress *progress))block
  771. {
  772.  
  773. self.didStartReceivingResource = [block copy];
  774. self.resourceStart = mainQueue;
  775. }
  776. // 接收某某资源
  777. - (void)receiveFinalResourceOnMainQueue:(BOOL)mainQueue complete:(void (^)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error))block
  778. {
  779. self.finalResource = [block copy];
  780. self.resourceFinalOnMainQueue = mainQueue;
  781. }
  782. - (NSOutputStream *)streamWithName:(NSString *)name toPeer:(MCPeerID *)peerID error:(NSError * __autoreleasing *)error
  783. {
  784. return [self.session startStreamWithName:name toPeer:peerID error:error];
  785. }
  786. // 开始转化为流
  787. - (void)didReceiveStreamFromPeer:(void (^)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName))streamBlock
  788. {
  789. self.streamBlock = [streamBlock copy];
  790. }
  791. - (void)didFindPeerWithInfo:(void (^)(MCPeerID *peer, NSDictionary *info))found
  792. {
  793. self.didFindPeer = [found copy];
  794. }
  795. #pragma mark 一些断开的情况
  796. - (void)disconnectSession
  797. {
  798. [self.session disconnect];
  799. }
  800.  
  801. - (void)stopAdvertising
  802. {
  803. [self.advertiser stopAdvertisingPeer];
  804. [self.advertisingAssistant stop];
  805. }
  806.  
  807. - (void)stopBrowsing
  808. {
  809. [self.browser stopBrowsingForPeers];
  810. }
  811.  
  812. - (BOOL)isConnected {
  813. return self.session.connectedPeers && self.session.connectedPeers.count > 0;
  814. }
  815.  
  816. // 是否连接 它
  817. - (void)connectToPeer:(BOOL)connect {
  818. self.invitationHandler(connect, self.session);
  819. }
  820.  
  821. - (MCSession *)session {
  822. return self.currentSession;
  823. }
  824.  
  825. - (MCPeerID *)firstPeer {
  826. return self.session.connectedPeers.firstObject;
  827. }
  828.  
  829. @end
  830.  
  831. #import "BlueSessionManager.h"
  832.  
  833. #define ServiceType @"MyService"
  834.  
  835. /*
  836. MCAdvertiserAssistant //可以接收,并处理用户请求连接的响应。没有回调,会弹出默认的提示框,并处理连接。
  837. MCNearbyServiceAdvertiser //可以接收,并处理用户请求连接的响应。但是,这个类会有回调,告知有用户要与您的设备连接,然后可以自定义提示框,以及自定义连接处理。
  838. MCNearbyServiceBrowser //用于搜索附近的用户,并可以对搜索到的用户发出邀请加入某个会话中。
  839. MCPeerID //这表明是一个用户
  840. MCSession //启用和管理Multipeer连接会话中的所有人之间的沟通。 通过Sesion,给别人发送数据。
  841. */
  842. @interface BlueSessionManager ()<MCAdvertiserAssistantDelegate, MCNearbyServiceBrowserDelegate, MCSessionDelegate, MCBrowserViewControllerDelegate, MCNearbyServiceAdvertiserDelegate, UIAlertViewDelegate>
  843. @property(strong, nonatomic) MCSession *currentSession; // 当前会议
  844. @property(strong, nonatomic) MCAdvertiserAssistant *advertisingAssistant; // 宣传助手
  845. @property(strong, nonatomic) MCNearbyServiceAdvertiser *advertiser; // 服务助手
  846. @property(strong, nonatomic) MCNearbyServiceBrowser *browser; // 搜索蓝牙者
  847. @property(strong, nonatomic) MCPeerID *peerID; // 用户
  848.  
  849. // 以下都是用到的block
  850. @property(nonatomic, copy) void(^receiveDataBlock)(NSData *data, MCPeerID *peer);
  851. @property(nonatomic, copy) void(^receiveResourceBlock)(MCPeerID *peer, NSURL *url);
  852. @property(nonatomic, copy) void(^connectionStatus)(MCPeerID *peer, MCSessionState state);
  853. @property(nonatomic, copy) void(^browserConnected)(void);
  854. @property(nonatomic, copy) void(^browserCancelled)(void);
  855. @property(nonatomic, copy) void(^didFindPeer)(MCPeerID *peer, NSDictionary *info);
  856. @property(nonatomic, copy) void(^invitationHandler)(BOOL connect, MCSession *session);
  857. @property(nonatomic, copy) void(^inviteBlock)(MCPeerID *peer, NSData *context);
  858. @property(nonatomic, copy) void(^didStartReceivingResource)(NSString *name, MCPeerID *peer, NSProgress *progress);
  859. @property(nonatomic, copy) void(^finalResource)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error);
  860. @property(nonatomic, copy) void(^streamBlock)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName);
  861. // 各种判断
  862. @property(nonatomic, assign) BOOL receiveOnMainQueue;
  863. @property(nonatomic, assign) BOOL statusOnMainQueue;
  864. @property(nonatomic, assign) BOOL resourceFinalOnMainQueue;
  865. @property(nonatomic, assign) BOOL resourceStart;
  866. @end
  867. @implementation BlueSessionManager
  868. #pragma mark 初始化自己
  869. - (instancetype)initWithDisplayName:(NSString *)displayName
  870. {
  871. return [self initWithDisplayName:displayName securityIdentity:nil encryptionPreferences:MCEncryptionNone serviceType:ServiceType];
  872. }
  873. // 为上面自定义 用户
  874. - (instancetype)initWithDisplayName:(NSString *)displayName securityIdentity:(NSArray *)security encryptionPreferences:(MCEncryptionPreference)preference serviceType:(NSString *)type
  875. {
  876. self = [super init];
  877. if(!self) {
  878. return nil;
  879. }
  880. self.peerID = [[MCPeerID alloc]initWithDisplayName:displayName];
  881. self.currentSession = [[MCSession alloc]initWithPeer:self.peerID securityIdentity:security encryptionPreference:preference];
  882. self.session.delegate = self;
  883. self.serviceType = type;
  884. return self;
  885. }
  886. #pragma mark 宣传自己
  887. - (void)advertiseForBrowserViewController
  888. {
  889. [self advertiseForBrowserViewControllerWithDiscoveryInfo:nil];
  890. }
  891.  
  892. - (void)advertiseForBrowserViewControllerWithDiscoveryInfo:(NSDictionary *)info
  893. {
  894. //
  895. self.advertiser = [[MCNearbyServiceAdvertiser alloc]initWithPeer:self.peerID discoveryInfo:info serviceType:self.serviceType];
  896. self.advertiser.delegate = self;
  897. [self.advertiser startAdvertisingPeer];
  898. }
  899.  
  900. - (void)advertiseForProgrammaticDiscovery
  901. {
  902. [self advertiseForProgrammaticDiscoveryWithDiscoveryInfo:nil];
  903. }
  904.  
  905. - (void)advertiseForProgrammaticDiscoveryWithDiscoveryInfo:(NSDictionary *)info
  906. {
  907. //自定义自己,为了让其他设备搜索到自己
  908. self.advertisingAssistant = [[MCAdvertiserAssistant alloc]initWithServiceType:self.serviceType discoveryInfo:info session:self.session];
  909. self.advertisingAssistant.delegate = self;
  910. [self.advertisingAssistant start];
  911. }
  912.  
  913. - (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession *))invitationHandler {
  914. self.invitationHandler = [invitationHandler copy];
  915. if(self.inviteBlock) self.inviteBlock(peerID, context);
  916. }
  917.  
  918. #pragma mark 下面是MCAdvertiserAssistant的两个代理
  919. - (void)advertiserAssistantDidDismissInvitation:(MCAdvertiserAssistant *)advertiserAssistant
  920. {
  921. //TODO implement
  922. }
  923.  
  924. - (void)advertiserAssitantWillPresentInvitation:(MCAdvertiserAssistant *)advertiserAssistant {
  925. //TODO implement
  926. }
  927.  
  928. #pragma mark 扫描其他的设备
  929. - (void)browseForProgrammaticDiscovery
  930. {
  931.  
  932. self.browser = [[MCNearbyServiceBrowser alloc]initWithPeer:self.peerID serviceType:self.serviceType];
  933. self.browser.delegate = self;
  934. [self.browser startBrowsingForPeers];
  935.  
  936. }
  937.  
  938. #pragma mark MCNearbyServiceBrowserDelegate
  939. - (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID
  940. {
  941. //TODO implement
  942. }
  943.  
  944. - (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info
  945. {
  946. if(self.didFindPeer)
  947. {
  948. self.didFindPeer(peerID, info);
  949. }
  950. }
  951.  
  952. - (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error
  953. {
  954. //TODO implement
  955. }
  956.  
  957. #pragma mark 参加会议 也是会议的代理
  958. // 也是 ----- MCSessionDelegate
  959.  
  960. // 这是完成会议的结果···
  961. - (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error
  962. {
  963. if(self.resourceFinalOnMainQueue)
  964. {
  965. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  966. if(self.finalResource)
  967. {
  968. self.finalResource(resourceName, peerID, localURL, error);
  969. }
  970. }];
  971. }
  972. else
  973. {
  974. if(self.finalResource)
  975. {
  976. self.finalResource(resourceName, peerID, localURL, error);
  977. }
  978. }
  979. }
  980. // 这是参加 普通数据的会议
  981. - (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID
  982. {
  983. if(self.receiveOnMainQueue)
  984. {
  985. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  986. if(self.receiveDataBlock){
  987. self.receiveDataBlock(data, peerID);
  988. }
  989. }];
  990. }
  991. else
  992. {
  993. if(self.receiveDataBlock)
  994. {
  995. self.receiveDataBlock(data, peerID);
  996. }
  997. }
  998. }
  999. // 这是参加普通流的会议
  1000. - (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID
  1001. {
  1002. if(self.streamBlock)
  1003. {
  1004. self.streamBlock(stream, peerID, streamName);
  1005. }
  1006. }
  1007. // 这是参加图片资源的会议
  1008. - (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress
  1009. {
  1010. if(self.resourceStart)
  1011. {
  1012.  
  1013. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  1014. if(self.didStartReceivingResource)
  1015. {
  1016. self.didStartReceivingResource(resourceName, peerID, progress);
  1017. }
  1018. }];
  1019. }
  1020. else
  1021. {
  1022.  
  1023. if(self.didStartReceivingResource)
  1024. {
  1025. self.didStartReceivingResource(resourceName, peerID, progress);
  1026. }
  1027. }
  1028. }
  1029.  
  1030. // 这是不同数据,系那是不同会议时候的状态
  1031.  
  1032. - (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state
  1033. {
  1034. // 这个地方是当两个蓝牙设备一旦连接起来,就会形成的一个会议
  1035. if(self.statusOnMainQueue)
  1036. {
  1037. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  1038. if(self.connectionStatus)
  1039. {
  1040. self.connectionStatus(peerID, state);
  1041. }
  1042. }];
  1043. }
  1044. else
  1045. {
  1046. if(self.connectionStatus)
  1047. {
  1048. self.connectionStatus(peerID, state);
  1049. }
  1050. }
  1051. }
  1052.  
  1053. #pragma mark send And receive
  1054. //--------------------------------------------------------------------------------------------//
  1055. // 发送消息
  1056. // 用户多个
  1057. - (NSError *)sendDataToAllPeers:(NSData *)data
  1058. {
  1059. // 普通数据的发送
  1060. return [self sendDataToAllPeers:data withMode:MCSessionSendDataReliable];
  1061. }
  1062.  
  1063. - (NSError *)sendDataToAllPeers:(NSData *)data withMode:(MCSessionSendDataMode)mode
  1064. {
  1065. // 确实进入会议
  1066. NSError *error;
  1067. [self.session sendData:data toPeers:self.session.connectedPeers withMode:mode error:&error];
  1068. return error;
  1069. }
  1070.  
  1071. // 用户确定一个
  1072. - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers
  1073. {
  1074. return [self sendData:data toPeers:peers withMode:MCSessionSendDataReliable];
  1075. }
  1076.  
  1077. - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers withMode:(MCSessionSendDataMode)mode
  1078. {
  1079. NSError *error;
  1080. [self.session sendData:data toPeers:peers withMode:mode error:&error];
  1081. return error;
  1082. }
  1083.  
  1084. // 接收消息
  1085. - (void)receiveDataOnMainQueue:(BOOL)mainQueue block:(void (^)(NSData *data, MCPeerID *peer))dataBlock
  1086. {
  1087.  
  1088. self.receiveDataBlock = [dataBlock copy];
  1089. self.receiveOnMainQueue = mainQueue;
  1090. }
  1091. //--------------------------------------------------------------------------------------------//
  1092.  
  1093. - (NSProgress *)sendResourceWithName:(NSString *)name atURL:(NSURL *)url toPeer:(MCPeerID *)peer complete:(void (^)(NSError *error))compelete
  1094. {
  1095. // 图片资源数据的发送
  1096.  
  1097. return [self.session sendResourceAtURL:url withName:name toPeer:peer withCompletionHandler:compelete];
  1098. }
  1099.  
  1100. // 用户连接确定
  1101. - (void)peerConnectionStatusOnMainQueue:(BOOL)mainQueue block:(void (^)(MCPeerID *peer, MCSessionState state))status
  1102. {
  1103. self.connectionStatus = [status copy];
  1104. self.statusOnMainQueue = mainQueue;
  1105. }
  1106.  
  1107. #pragma mark 自带的MCBrowserViewController
  1108. - (void)browserViewControllerDidFinish:(MCBrowserViewController *)browserViewController
  1109. {
  1110. [browserViewController dismissViewControllerAnimated:YES completion:^{
  1111. if(self.browserConnected) self.browserConnected();
  1112. }];
  1113. }
  1114.  
  1115. - (void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController
  1116. {
  1117. [browserViewController dismissViewControllerAnimated:YES completion:^{
  1118. if(self.browserCancelled) self.browserCancelled();
  1119. }];
  1120. }
  1121.  
  1122. - (void)browseWithControllerInViewController:(UIViewController *)controller connected:(void (^)(void))connected canceled:(void (^)(void))cancelled
  1123. {
  1124. self.browserConnected = [connected copy];
  1125. self.browserCancelled = [cancelled copy];
  1126. // 注意这个自带的类
  1127. MCBrowserViewController *browser = [[MCBrowserViewController alloc]initWithServiceType:self.serviceType session:self.session];
  1128. browser.delegate = self;
  1129. [controller presentViewController:browser animated:YES completion:nil];
  1130. }
  1131.  
  1132. - (NSArray *)connectedPeers
  1133. {
  1134. return self.session.connectedPeers;
  1135. }
  1136.  
  1137. // 邀请某某连接
  1138. - (void)didReceiveInvitationFromPeer:(void (^)(MCPeerID *peer, NSData *context))invite;
  1139. {
  1140. self.inviteBlock = [invite copy];
  1141. }
  1142.  
  1143. - (void)invitePeerToConnect:(MCPeerID *)peer connected:(void (^)(void))connected
  1144. {
  1145. [self.browser invitePeer:peer toSession:self.session withContext:nil timeout:30];
  1146. }
  1147.  
  1148. - (void)startReceivingResourceOnMainQueue:(BOOL)mainQueue block:(void (^)(NSString *name, MCPeerID *peer, NSProgress *progress))block
  1149. {
  1150.  
  1151. self.didStartReceivingResource = [block copy];
  1152. self.resourceStart = mainQueue;
  1153. }
  1154. // 接收某某资源
  1155. - (void)receiveFinalResourceOnMainQueue:(BOOL)mainQueue complete:(void (^)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error))block
  1156. {
  1157. self.finalResource = [block copy];
  1158. self.resourceFinalOnMainQueue = mainQueue;
  1159. }
  1160. - (NSOutputStream *)streamWithName:(NSString *)name toPeer:(MCPeerID *)peerID error:(NSError * __autoreleasing *)error
  1161. {
  1162. return [self.session startStreamWithName:name toPeer:peerID error:error];
  1163. }
  1164. // 开始转化为流
  1165. - (void)didReceiveStreamFromPeer:(void (^)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName))streamBlock
  1166. {
  1167. self.streamBlock = [streamBlock copy];
  1168. }
  1169. - (void)didFindPeerWithInfo:(void (^)(MCPeerID *peer, NSDictionary *info))found
  1170. {
  1171. self.didFindPeer = [found copy];
  1172. }
  1173. #pragma mark 一些断开的情况
  1174. - (void)disconnectSession
  1175. {
  1176. [self.session disconnect];
  1177. }
  1178.  
  1179. - (void)stopAdvertising
  1180. {
  1181. [self.advertiser stopAdvertisingPeer];
  1182. [self.advertisingAssistant stop];
  1183. }
  1184.  
  1185. - (void)stopBrowsing
  1186. {
  1187. [self.browser stopBrowsingForPeers];
  1188. }
  1189.  
  1190. - (BOOL)isConnected {
  1191. return self.session.connectedPeers && self.session.connectedPeers.count > 0;
  1192. }
  1193.  
  1194. // 是否连接 它
  1195. - (void)connectToPeer:(BOOL)connect {
  1196. self.invitationHandler(connect, self.session);
  1197. }
  1198.  
  1199. - (MCSession *)session {
  1200. return self.currentSession;
  1201. }
  1202.  
  1203. - (MCPeerID *)firstPeer {
  1204. return self.session.connectedPeers.firstObject;
  1205. }
  1206.  
  1207. @end
  1208.  
  1209. .m
  1210.  
  1211. #import "BlueSessionManager.h"
  1212.  
  1213. #define ServiceType @"MyService"
  1214.  
  1215. /*
  1216. MCAdvertiserAssistant //可以接收,并处理用户请求连接的响应。没有回调,会弹出默认的提示框,并处理连接。
  1217. MCNearbyServiceAdvertiser //可以接收,并处理用户请求连接的响应。但是,这个类会有回调,告知有用户要与您的设备连接,然后可以自定义提示框,以及自定义连接处理。
  1218. MCNearbyServiceBrowser //用于搜索附近的用户,并可以对搜索到的用户发出邀请加入某个会话中。
  1219. MCPeerID //这表明是一个用户
  1220. MCSession //启用和管理Multipeer连接会话中的所有人之间的沟通。 通过Sesion,给别人发送数据。
  1221. */
  1222. @interface BlueSessionManager ()<MCAdvertiserAssistantDelegate, MCNearbyServiceBrowserDelegate, MCSessionDelegate, MCBrowserViewControllerDelegate, MCNearbyServiceAdvertiserDelegate, UIAlertViewDelegate>
  1223. @property(strong, nonatomic) MCSession *currentSession; // 当前会议
  1224. @property(strong, nonatomic) MCAdvertiserAssistant *advertisingAssistant; // 宣传助手
  1225. @property(strong, nonatomic) MCNearbyServiceAdvertiser *advertiser; // 服务助手
  1226. @property(strong, nonatomic) MCNearbyServiceBrowser *browser; // 搜索蓝牙者
  1227. @property(strong, nonatomic) MCPeerID *peerID; // 用户
  1228.  
  1229. // 以下都是用到的block
  1230. @property(nonatomic, copy) void(^receiveDataBlock)(NSData *data, MCPeerID *peer);
  1231. @property(nonatomic, copy) void(^receiveResourceBlock)(MCPeerID *peer, NSURL *url);
  1232. @property(nonatomic, copy) void(^connectionStatus)(MCPeerID *peer, MCSessionState state);
  1233. @property(nonatomic, copy) void(^browserConnected)(void);
  1234. @property(nonatomic, copy) void(^browserCancelled)(void);
  1235. @property(nonatomic, copy) void(^didFindPeer)(MCPeerID *peer, NSDictionary *info);
  1236. @property(nonatomic, copy) void(^invitationHandler)(BOOL connect, MCSession *session);
  1237. @property(nonatomic, copy) void(^inviteBlock)(MCPeerID *peer, NSData *context);
  1238. @property(nonatomic, copy) void(^didStartReceivingResource)(NSString *name, MCPeerID *peer, NSProgress *progress);
  1239. @property(nonatomic, copy) void(^finalResource)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error);
  1240. @property(nonatomic, copy) void(^streamBlock)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName);
  1241. // 各种判断
  1242. @property(nonatomic, assign) BOOL receiveOnMainQueue;
  1243. @property(nonatomic, assign) BOOL statusOnMainQueue;
  1244. @property(nonatomic, assign) BOOL resourceFinalOnMainQueue;
  1245. @property(nonatomic, assign) BOOL resourceStart;
  1246. @end
  1247. @implementation BlueSessionManager
  1248. #pragma mark 初始化自己
  1249. - (instancetype)initWithDisplayName:(NSString *)displayName
  1250. {
  1251. return [self initWithDisplayName:displayName securityIdentity:nil encryptionPreferences:MCEncryptionNone serviceType:ServiceType];
  1252. }
  1253. // 为上面自定义 用户
  1254. - (instancetype)initWithDisplayName:(NSString *)displayName securityIdentity:(NSArray *)security encryptionPreferences:(MCEncryptionPreference)preference serviceType:(NSString *)type
  1255. {
  1256. self = [super init];
  1257. if(!self) {
  1258. return nil;
  1259. }
  1260. self.peerID = [[MCPeerID alloc]initWithDisplayName:displayName];
  1261. self.currentSession = [[MCSession alloc]initWithPeer:self.peerID securityIdentity:security encryptionPreference:preference];
  1262. self.session.delegate = self;
  1263. self.serviceType = type;
  1264. return self;
  1265. }
  1266. #pragma mark 宣传自己
  1267. - (void)advertiseForBrowserViewController
  1268. {
  1269. [self advertiseForBrowserViewControllerWithDiscoveryInfo:nil];
  1270. }
  1271.  
  1272. - (void)advertiseForBrowserViewControllerWithDiscoveryInfo:(NSDictionary *)info
  1273. {
  1274. //
  1275. self.advertiser = [[MCNearbyServiceAdvertiser alloc]initWithPeer:self.peerID discoveryInfo:info serviceType:self.serviceType];
  1276. self.advertiser.delegate = self;
  1277. [self.advertiser startAdvertisingPeer];
  1278. }
  1279.  
  1280. - (void)advertiseForProgrammaticDiscovery
  1281. {
  1282. [self advertiseForProgrammaticDiscoveryWithDiscoveryInfo:nil];
  1283. }
  1284.  
  1285. - (void)advertiseForProgrammaticDiscoveryWithDiscoveryInfo:(NSDictionary *)info
  1286. {
  1287. //自定义自己,为了让其他设备搜索到自己
  1288. self.advertisingAssistant = [[MCAdvertiserAssistant alloc]initWithServiceType:self.serviceType discoveryInfo:info session:self.session];
  1289. self.advertisingAssistant.delegate = self;
  1290. [self.advertisingAssistant start];
  1291. }
  1292.  
  1293. - (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession *))invitationHandler {
  1294. self.invitationHandler = [invitationHandler copy];
  1295. if(self.inviteBlock) self.inviteBlock(peerID, context);
  1296. }
  1297.  
  1298. #pragma mark 下面是MCAdvertiserAssistant的两个代理
  1299. - (void)advertiserAssistantDidDismissInvitation:(MCAdvertiserAssistant *)advertiserAssistant
  1300. {
  1301. //TODO implement
  1302. }
  1303.  
  1304. - (void)advertiserAssitantWillPresentInvitation:(MCAdvertiserAssistant *)advertiserAssistant {
  1305. //TODO implement
  1306. }
  1307.  
  1308. #pragma mark 扫描其他的设备
  1309. - (void)browseForProgrammaticDiscovery
  1310. {
  1311.  
  1312. self.browser = [[MCNearbyServiceBrowser alloc]initWithPeer:self.peerID serviceType:self.serviceType];
  1313. self.browser.delegate = self;
  1314. [self.browser startBrowsingForPeers];
  1315.  
  1316. }
  1317.  
  1318. #pragma mark MCNearbyServiceBrowserDelegate
  1319. - (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID
  1320. {
  1321. //TODO implement
  1322. }
  1323.  
  1324. - (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info
  1325. {
  1326. if(self.didFindPeer)
  1327. {
  1328. self.didFindPeer(peerID, info);
  1329. }
  1330. }
  1331.  
  1332. - (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error
  1333. {
  1334. //TODO implement
  1335. }
  1336.  
  1337. #pragma mark 参加会议 也是会议的代理
  1338. // 也是 ----- MCSessionDelegate
  1339.  
  1340. // 这是完成会议的结果···
  1341. - (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error
  1342. {
  1343. if(self.resourceFinalOnMainQueue)
  1344. {
  1345. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  1346. if(self.finalResource)
  1347. {
  1348. self.finalResource(resourceName, peerID, localURL, error);
  1349. }
  1350. }];
  1351. }
  1352. else
  1353. {
  1354. if(self.finalResource)
  1355. {
  1356. self.finalResource(resourceName, peerID, localURL, error);
  1357. }
  1358. }
  1359. }
  1360. // 这是参加 普通数据的会议
  1361. - (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID
  1362. {
  1363. if(self.receiveOnMainQueue)
  1364. {
  1365. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  1366. if(self.receiveDataBlock){
  1367. self.receiveDataBlock(data, peerID);
  1368. }
  1369. }];
  1370. }
  1371. else
  1372. {
  1373. if(self.receiveDataBlock)
  1374. {
  1375. self.receiveDataBlock(data, peerID);
  1376. }
  1377. }
  1378. }
  1379. // 这是参加普通流的会议
  1380. - (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID
  1381. {
  1382. if(self.streamBlock)
  1383. {
  1384. self.streamBlock(stream, peerID, streamName);
  1385. }
  1386. }
  1387. // 这是参加图片资源的会议
  1388. - (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress
  1389. {
  1390. if(self.resourceStart)
  1391. {
  1392.  
  1393. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  1394. if(self.didStartReceivingResource)
  1395. {
  1396. self.didStartReceivingResource(resourceName, peerID, progress);
  1397. }
  1398. }];
  1399. }
  1400. else
  1401. {
  1402.  
  1403. if(self.didStartReceivingResource)
  1404. {
  1405. self.didStartReceivingResource(resourceName, peerID, progress);
  1406. }
  1407. }
  1408. }
  1409.  
  1410. // 这是不同数据,系那是不同会议时候的状态
  1411.  
  1412. - (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state
  1413. {
  1414. // 这个地方是当两个蓝牙设备一旦连接起来,就会形成的一个会议
  1415. if(self.statusOnMainQueue)
  1416. {
  1417. [[NSOperationQueue mainQueue]addOperationWithBlock:^{
  1418. if(self.connectionStatus)
  1419. {
  1420. self.connectionStatus(peerID, state);
  1421. }
  1422. }];
  1423. }
  1424. else
  1425. {
  1426. if(self.connectionStatus)
  1427. {
  1428. self.connectionStatus(peerID, state);
  1429. }
  1430. }
  1431. }
  1432.  
  1433. #pragma mark send And receive
  1434. //--------------------------------------------------------------------------------------------//
  1435. // 发送消息
  1436. // 用户多个
  1437. - (NSError *)sendDataToAllPeers:(NSData *)data
  1438. {
  1439. // 普通数据的发送
  1440. return [self sendDataToAllPeers:data withMode:MCSessionSendDataReliable];
  1441. }
  1442.  
  1443. - (NSError *)sendDataToAllPeers:(NSData *)data withMode:(MCSessionSendDataMode)mode
  1444. {
  1445. // 确实进入会议
  1446. NSError *error;
  1447. [self.session sendData:data toPeers:self.session.connectedPeers withMode:mode error:&error];
  1448. return error;
  1449. }
  1450.  
  1451. // 用户确定一个
  1452. - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers
  1453. {
  1454. return [self sendData:data toPeers:peers withMode:MCSessionSendDataReliable];
  1455. }
  1456.  
  1457. - (NSError *)sendData:(NSData *)data toPeers:(NSArray *)peers withMode:(MCSessionSendDataMode)mode
  1458. {
  1459. NSError *error;
  1460. [self.session sendData:data toPeers:peers withMode:mode error:&error];
  1461. return error;
  1462. }
  1463.  
  1464. // 接收消息
  1465. - (void)receiveDataOnMainQueue:(BOOL)mainQueue block:(void (^)(NSData *data, MCPeerID *peer))dataBlock
  1466. {
  1467.  
  1468. self.receiveDataBlock = [dataBlock copy];
  1469. self.receiveOnMainQueue = mainQueue;
  1470. }
  1471. //--------------------------------------------------------------------------------------------//
  1472.  
  1473. - (NSProgress *)sendResourceWithName:(NSString *)name atURL:(NSURL *)url toPeer:(MCPeerID *)peer complete:(void (^)(NSError *error))compelete
  1474. {
  1475. // 图片资源数据的发送
  1476.  
  1477. return [self.session sendResourceAtURL:url withName:name toPeer:peer withCompletionHandler:compelete];
  1478. }
  1479.  
  1480. // 用户连接确定
  1481. - (void)peerConnectionStatusOnMainQueue:(BOOL)mainQueue block:(void (^)(MCPeerID *peer, MCSessionState state))status
  1482. {
  1483. self.connectionStatus = [status copy];
  1484. self.statusOnMainQueue = mainQueue;
  1485. }
  1486.  
  1487. #pragma mark 自带的MCBrowserViewController
  1488. - (void)browserViewControllerDidFinish:(MCBrowserViewController *)browserViewController
  1489. {
  1490. [browserViewController dismissViewControllerAnimated:YES completion:^{
  1491. if(self.browserConnected) self.browserConnected();
  1492. }];
  1493. }
  1494.  
  1495. - (void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController
  1496. {
  1497. [browserViewController dismissViewControllerAnimated:YES completion:^{
  1498. if(self.browserCancelled) self.browserCancelled();
  1499. }];
  1500. }
  1501.  
  1502. - (void)browseWithControllerInViewController:(UIViewController *)controller connected:(void (^)(void))connected canceled:(void (^)(void))cancelled
  1503. {
  1504. self.browserConnected = [connected copy];
  1505. self.browserCancelled = [cancelled copy];
  1506. // 注意这个自带的类
  1507. MCBrowserViewController *browser = [[MCBrowserViewController alloc]initWithServiceType:self.serviceType session:self.session];
  1508. browser.delegate = self;
  1509. [controller presentViewController:browser animated:YES completion:nil];
  1510. }
  1511.  
  1512. - (NSArray *)connectedPeers
  1513. {
  1514. return self.session.connectedPeers;
  1515. }
  1516.  
  1517. // 邀请某某连接
  1518. - (void)didReceiveInvitationFromPeer:(void (^)(MCPeerID *peer, NSData *context))invite;
  1519. {
  1520. self.inviteBlock = [invite copy];
  1521. }
  1522.  
  1523. - (void)invitePeerToConnect:(MCPeerID *)peer connected:(void (^)(void))connected
  1524. {
  1525. [self.browser invitePeer:peer toSession:self.session withContext:nil timeout:30];
  1526. }
  1527.  
  1528. - (void)startReceivingResourceOnMainQueue:(BOOL)mainQueue block:(void (^)(NSString *name, MCPeerID *peer, NSProgress *progress))block
  1529. {
  1530.  
  1531. self.didStartReceivingResource = [block copy];
  1532. self.resourceStart = mainQueue;
  1533. }
  1534. // 接收某某资源
  1535. - (void)receiveFinalResourceOnMainQueue:(BOOL)mainQueue complete:(void (^)(NSString *name, MCPeerID *peer, NSURL *url, NSError *error))block
  1536. {
  1537. self.finalResource = [block copy];
  1538. self.resourceFinalOnMainQueue = mainQueue;
  1539. }
  1540. - (NSOutputStream *)streamWithName:(NSString *)name toPeer:(MCPeerID *)peerID error:(NSError * __autoreleasing *)error
  1541. {
  1542. return [self.session startStreamWithName:name toPeer:peerID error:error];
  1543. }
  1544. // 开始转化为流
  1545. - (void)didReceiveStreamFromPeer:(void (^)(NSInputStream *inputStream, MCPeerID *peer, NSString *streamName))streamBlock
  1546. {
  1547. self.streamBlock = [streamBlock copy];
  1548. }
  1549. - (void)didFindPeerWithInfo:(void (^)(MCPeerID *peer, NSDictionary *info))found
  1550. {
  1551. self.didFindPeer = [found copy];
  1552. }
  1553. #pragma mark 一些断开的情况
  1554. - (void)disconnectSession
  1555. {
  1556. [self.session disconnect];
  1557. }
  1558.  
  1559. - (void)stopAdvertising
  1560. {
  1561. [self.advertiser stopAdvertisingPeer];
  1562. [self.advertisingAssistant stop];
  1563. }
  1564.  
  1565. - (void)stopBrowsing
  1566. {
  1567. [self.browser stopBrowsingForPeers];
  1568. }
  1569.  
  1570. - (BOOL)isConnected {
  1571. return self.session.connectedPeers && self.session.connectedPeers.count > 0;
  1572. }
  1573.  
  1574. // 是否连接 它
  1575. - (void)connectToPeer:(BOOL)connect {
  1576. self.invitationHandler(connect, self.session);
  1577. }
  1578.  
  1579. - (MCSession *)session {
  1580. return self.currentSession;
  1581. }
  1582.  
  1583. - (MCPeerID *)firstPeer {
  1584. return self.session.connectedPeers.firstObject;
  1585. }
  1586.  
  1587. @end

最终效果只能真机测试,所以这里不做演示!

每日更新关注:http://weibo.com/hanjunqiang 
新浪微博

iOS中 蓝牙2.0详解/ios蓝牙设备详解 韩俊强的博客的更多相关文章

  1. iOS中 按钮和标题完美各种排列/完美教程 韩俊强的博客

    每日更新关注:http://weibo.com/hanjunqiang  新浪微博! 前言:最近常常用到按钮和相应标题的组合,当按钮设置图片加标题时,触发范围较小,不易触发,最重要的是还要调试偏移量, ...

  2. iOS中 如何将自己的框架更新到cocopods上 韩俊强的博客

    每日更新关注:http://weibo.com/hanjunqiang  新浪微博! 为了更方便的集成第三方框架有了cocopods 的, 当我们有了相对比较好的框架的时候如何更新到cocopods ...

  3. iOS中 语音识别功能/语音转文字教程具体解释 韩俊强的博客

    原文地址:http://blog.csdn.net/qq_31810357/article/details/51111702 前言:近期研究了一下语音识别,从百度语音识别到讯飞语音识别:首先说一下个人 ...

  4. iOS中 扫描二维码/生成二维码详解 韩俊强的博客

    最近大家总是问我有没有关于二维码的demo,为了满足大家的需求,特此研究了一番,希望能帮到大家! 每日更新关注:http://weibo.com/hanjunqiang  新浪微博 指示根视图: se ...

  5. iOS中 HTTP/Socket/TCP/IP通信协议详解 韩俊强的博客

    每日更新关注:http://weibo.com/hanjunqiang  新浪微博 简单介绍: // OSI(开放式系统互联), 由ISO(国际化标准组织)制定 // 1. 应用层 // 2. 表示层 ...

  6. iOS中 扫描二维码/生成二维码具体解释 韩俊强的博客

    近期大家总是问我有没有关于二维码的demo,为了满足大家的需求,特此研究了一番,希望能帮到大家! 每日更新关注:http://weibo.com/hanjunqiang  新浪微博 指示根视图: se ...

  7. iOS中 HTTP/Socket/TCP/IP通信协议具体解释 韩俊强的博客

    简介: // OSI(开放式系统互联), 由ISO(国际化标准组织)制定 // 1. 应用层 // 2. 表示层 // 3. 会话层 // 4. 传输层 // 5. 网络层 // 6. 数据链接层 / ...

  8. iOS中 本地通知/本地通知详解 韩俊强的博客

    布局如下:(重点讲本地通知) iOS开发者交流QQ群: 446310206 每日更新关注:http://weibo.com/hanjunqiang  新浪微博 Notification是智能手机应用编 ...

  9. iOS中 Realm的学习与使用 韩俊强的博客

    每日更新关注:http://weibo.com/hanjunqiang  新浪微博! 有问题或技术交流可以咨询!欢迎加入! 这篇直接搬了一份官方文档过来看的 由于之前没用markdown搞的乱七八糟的 ...

  10. iOS中 最新支付宝支付(AliPay) 韩俊强的博客

    每日更新关注:http://weibo.com/hanjunqiang  新浪微博 现在的支付方式一般有三种, 支付宝, 微信, 网银. 个人觉得最简单易用的还是支付宝, 微信虽然看起来币支付宝要简单 ...

随机推荐

  1. git修改远程仓库地址

    方法有三种: 1.修改命令 git remote set-url origin [url] 例如: git remote set-url origin gitlab@gitlab.chumob.com ...

  2. Linux中Mysql root用户看不到mysql库问题解决方式

    第一种方式: 1.首先停止MySQL服务:service mysqld stop2.加参数启动mysql:/usr/bin/mysqld_safe --skip-grant-tables &  ...

  3. 对闭包的理解(closure)

    什么是闭包: 当你声明一个局部变量时,这个局部变量有作用域,通常局部变量值只存在于你定义的Block or Function中: function() { var a = 1; console.log ...

  4. ES6(es2015)新增实用方法汇总

    Array 1.map() [1,2,3,4].map(function(item, index, array){ return  item * 2; }) 对数组中的每一项执行一次回调函数,三个参数 ...

  5. python学习之路基础篇(第七篇)

    一.模块 configparser configparser用于处理特定格式的文件,其本质是利用open来对文件进行操作 [section1] # 节点 k1 = v1 # 值 k2:v2 # 值 [ ...

  6. sorted函数返回一个新的列表就安全了吗?

    arr=[[1,2,3],[4,2,3],[5,2,3]] x2=sorted(arr) print 'sorted',x2 print '-'*20 for ar in arr: ar.append ...

  7. Udemy上免费的angualr2视频教程分享

    福利大分享 本文作者:苏生米沿 本文地址:http://blog.csdn.net/sushengmiyan/article/details/52592518 一晚上听了10几节课程,整体感觉很不错的 ...

  8. Dynamics CRM Trigger plugin for N:N relationships

    博客原文:https://demystifyingcrm.wordpress.com/2014/12/17/trigger-plugin-for-nn-relationships-in-dynamic ...

  9. 如何在控制台切换Xcode的版本

    打开控制台,输入 xcode-select -p 你可以看到当前Xcode所使用的版本路径,比如本猫的输出为: /Applications/Xcode-beta.app/Contents/Develo ...

  10. TensoFlow实现条件语句

    import tensorflow as tf a = tf.constant(20) b = tf.constant(10) result1 = tf.cond(a > b, lambda: ...