IOS 蓝牙(GameKit、Core Bluetooth)

摘要:
GameKit的蓝牙开发注意●只能用于iOS设备之间的连接●只能用于同一个应用程序之间的连接●最好别利用蓝牙发送比较大的数据/*关于蓝牙的数据传输1.一次性传送,没有中间方法,所谓中间方法值得是,传输进度比例对于用户而言,选择了传输,就需要等待传输完成,或者传输以失败告终这就意味着,在实际开发过程中,最好不要用蓝牙传输太大的文件在实际应用中,蓝牙通常用于传递游戏数据模型,用于联机对战,譬如点对点的棋牌类游戏。
GameKit的蓝牙开发注意
只能用于iOS设备之间的连接
只能用于同一个应用程序之间的连接
最好别利用蓝牙发送比较大的数据

/*
关于蓝牙的数据传输

1. 一次性传送,没有中间方法,所谓中间方法值得是,传输进度比例
对于用户而言,选择了传输,就需要等待传输完成,或者传输以失败告终
这就意味着,在实际开发过程中,最好不要用蓝牙传输太大的文件

在实际应用中,蓝牙通常用于传递游戏数据模型,用于联机对战,譬如点对点的棋牌类游戏。
*/

iOS中蓝牙的实现方案
iOS中提供了4个框架用于实现蓝牙连接
GameKit.framework(用法简单)
只能用于iOS设备之间的连接,多用于游戏(比如五子棋对战),iOS7开始过期

MultipeerConnectivity.framework
只能用于iOS设备之间的连接,iOS7开始引入,主要用于文件共享(仅限于沙盒的文

件)

ExternalAccessory.framework
可用于第三方蓝牙设备交互,但是蓝牙设备必须经过苹果MFi认证(国内较少)

CoreBluetooth.framework(时下热门)
可用于第三方蓝牙设备交互,必须要支持蓝牙4.0
硬件至少是4s,系统至少是iOS6
蓝牙4.0以低功耗著称,一般也叫BLE(Bluetooth Low Energy)

目前应用比较多的案例:运动手坏、嵌入式设备、智能家居

GameKit的蓝牙开发步骤
显示可以连接的蓝牙设备列表
GKPeerPickerController *ppc = [[GKPeerPickerController alloc] init]; ppc.delegate = self;
[ppc show];

在代理方法中监控蓝牙的连接
- (void)peerPickerController:(GKPeerPickerController *)picker

didConnectPeer:(NSString *)peerID toSession:(GKSession *)session { NSLog(@"连接到设备:%@", peerID);
// 关闭蓝牙设备显示界面
[picker dismiss];

// 设置接收到蓝牙数据后的监听器
[session setDataReceiveHandler:self withContext:nil]; // 保存session
self.session = session;

}

处理接收到的蓝牙数据
- (void) receiveData:(NSData *)data fromPeer:(NSString *)peer

inSession: (GKSession *)session context:(void *)context {
}

利用GKSession给其他设备发送数据 给指定的连接设备发送数据

- (BOOL)sendData:(NSData *) data toPeers:(NSArray *)peers
withDataMode:(GKSendDataMode)mode error:(NSError **)error;

给所有连接的设备发送数据
- (BOOL)sendDataToAllPeers:(NSData *) data withDataMode:

(GKSendDataMode)mode error:(NSError **)error;

实例1:

#import "ViewController.h"#include <GameKit/GameKit.h>

@interface ViewController ()<UINavigationControllerDelegate, UIImagePickerControllerDelegate, GKPeerPickerControllerDelegate>
/**
 *  连接
 */
-(IBAction)connect;
/**
 *  选择图片
 */
-(IBAction)selectedPhoto;
/**
 *  发送
 */
-(IBAction)send;

@property (weak, nonatomic) IBOutlet UIImageView *customIV;
/**
 *  会话
 */@property (nonatomic, strong) GKSession *session;
@end

@implementationViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //Do any additional setup after loading the view, typically from a nib.
}


-(IBAction)connect {
    
    //1.创建选择其他蓝牙设备的控制器
    GKPeerPickerController *peerPk =[[GKPeerPickerController alloc] init];
    //2.成为该控制器的代理
    peerPk.delegate =self;
    //3.显示蓝牙控制器
[peerPk show];
}
#pragma mark - GKPeerPickerControllerDelegate
//4.实现dialing方法/**
 *  当蓝牙设备连接成功就会调用
 *
 *  @param picker  触发时间的控制器
 *  @param peerID  连接蓝牙设备的ID
 *  @param session 连接蓝牙的会话(可用通讯), 以后只要拿到session就可以传输数据
 */
- (void)peerPickerController:(GKPeerPickerController *)picker didConnectPeer:(NSString *)peerID toSession:(GKSession *)session
{
    NSLog(@"%@", peerID);
    //1.保存会话
    self.session =session;
    
    //2.设置监听接收传递过来的数据
    /*Handler: 谁来处理接收到得数据
     withContext: 传递数据
     */[self.session setDataReceiveHandler:self withContext:nil];
    
    
    //2.关闭显示蓝牙设备控制器
[picker dismiss];
}
/**
 *  接收到其它设备传递过来的数据就会调用
 *
 *  @param data    传递过来的数据
 *  @param peer    传递数据设备的ID
 *  @param session 会话
 *  @param context 注册监听时传递的数据
 */
- (void) receiveData:(NSData *)data fromPeer:(NSString *)peer inSession: (GKSession *)session context:(void *)context
{
//NSLog(@"%s", __func__);
    //1.将传递过来的数据转换为图片(注意: 因为发送的时图片, 所以才需要转换为图片)
    UIImage *image =[UIImage imageWithData:data];
    self.customIV.image =image;
}


- (void)peerPickerControllerDidCancel:(GKPeerPickerController *)picker
{
    
}


-(IBAction)send {
    //利用session发送图片数据即可
    //1.取出customImageView上得图片, 转换为二进制
    UIImage *image =self.customIV.image;
    NSData *data =UIImagePNGRepresentation(image);
    
    /*GKSendDataReliable, 数据安全的发送模式, 慢
     GKSendDataUnreliable, 数据不安全的发送模式, 快
     */
    
    /*data: 需要发送的数据
     DataReliable: 是否安全的发送数据(发送数据的模式)
     error: 是否监听发送错误
     */[self.session sendDataToAllPeers:data withDataMode:GKSendDataReliable error:nil];
}


-(IBAction)selectedPhoto
{
    
    //1.创建图片选择控制器
    UIImagePickerController *imagePk =[[UIImagePickerController alloc] init];
    //2.判断图库是否可用打开
    if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum])
    {
        //3.设置打开图库的类型
        imagePk.sourceType =UIImagePickerControllerSourceTypeSavedPhotosAlbum;
        
        imagePk.delegate =self;
        
        //4.打开图片选择控制器
[self presentViewController:imagePk animated:YES completion:nil];
    }
}
#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
//NSLog(@"%@", info);
    self.customIV.image =info[UIImagePickerControllerOriginalImage];
    
    [picker dismissViewControllerAnimated:YES completion:nil];
}

@end
View Code

实例2:

/*> 讲解相片选择器
 *先不设置代理,创建后直接运行
 *从选择相片后需要关闭控制器引出代理
 *实现代理方法展示数据
 *PPT讲解蓝牙连接(不是所有应用都可以用蓝牙传输东西,必须相同应用事先写好如何处理. 苹果蓝牙传输非常满, 而且传输过程中没有进度)
 *在storyboard中添加按钮, 点击按钮后创建对端选择控制器, 设置代理,显示视图控制器
 *在代理方法中打印peerID, 讲解session用途查看头文件引出利用传递数据
 *定义属性保存session , 在storyboard中添加按钮监听按钮点击利用session传递图片
 *讲解传递数据方法两种模式区别
 *设置数据处理者, 讲解如何找到数据处理方法,在数据处理方法中打印LOG运行验证
 *将接收到的数据转换为图片后现实在界面上
 *总结蓝牙传输
 */

#import "CZViewController.h"
#import <GameKit/GameKit.h>

@interface CZViewController ()<UIImagePickerControllerDelegate, UINavigationControllerDelegate, GKPeerPickerControllerDelegate>

/**
 * 现实相片
 */
-(IBAction)selectPhoto;

@property (weak, nonatomic) IBOutlet UIImageView *imageView;

/**
 * 蓝牙连接
 */
-(IBAction)connectBtnClick;

/**
 * 会话对象
 */@property (nonatomic, strong)GKSession *session;

/**
 * 发送相片
 */
-(IBAction)sendPhoto;

@end

@implementationCZViewController

#pragma mark 选择照片
/*照片源类型
 
 UIImagePickerControllerSourceTypeCamera            照相机
 UIImagePickerControllerSourceTypePhotoLibrary      照片库(通过同步存放的,用户不能删除)
 UIImagePickerControllerSourceTypeSavedPhotosAlbum  保存的照片(通过拍照或者截屏保存的,用户可以删除)
 */
-(IBAction)selectPhoto
{
    
    
    //1.判断照片源是否可用
    if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
        //1.1实例化控制器
        UIImagePickerController *picker =[[UIImagePickerController alloc] init];
        //1.2设置照片源
[picker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
        //1.3设置允许修改
        picker.allowsEditing =YES;
        //1.4设置代理
        picker.delegate =self;
        //1.5显示控制器
        [self presentViewController:picker animated:YES completion:^{
            
        }];
    }else{
        //2.照片源不可用
        NSLog(@"照片源不可用");
    }
}

#pragma mark - imagePicker代理方法
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
//NSLog(@"%@", info);
    //获取相片
    UIImage *image = info[@"UIImagePickerControllerEditedImage"];
    //设置相片
    self.imageView.image =image;
    
    //关闭相片选择器
    [self dismissViewControllerAnimated:YES completion:^{
        
    }];
    
    
}

#pragma mark - 蓝牙连接
-(IBAction)connectBtnClick
{
    //1.创建对端选择控制器
    GKPeerPickerController *picker =[[GKPeerPickerController alloc] init];
    
    //2.设置代理
    picker.delegate =self;
    
    //3.显示试图控制器
[picker show];
    
}

#pragma mark - GKPeerPickerControllerDelegate
//完成对端连接
//GKSession对象用于表现两个蓝牙设备之间连接的一个会话,你也可以使用它在两个设备之间发送和接收数据。
- (void)peerPickerController:(GKPeerPickerController *)picker didConnectPeer:(NSString *)peerID toSession:(GKSession *)session
{
    NSLog(@"连接成功 %@", peerID);
    //保存会话
    self.session =session;
    
     //设置数据处理对象,类似于delegate
[self.session setDataReceiveHandler:self withContext:nil];
    
    //关闭对端选择控制器
[picker dismiss];
}

//取消对端选择控制器
- (void)peerPickerControllerDidCancel:(GKPeerPickerController *)picker
{
    NSLog(@"取消蓝牙选择器");
}

//发送相片
-(IBAction)sendPhoto {
    
    NSData *imageData =UIImagePNGRepresentation(self.imageView.image);
    
    //利用session发送相片
//self.session sendData:<#(NSData *)#> toPeers:<#(NSArray *)#> withDataMode:<#(GKSendDataMode)#> error:<#(NSError *__autoreleasing *)#>
    /*TCP协议、UDP协议
     
     1. 要发送的数据(二进制的)
     2. 数据发送模式
     GKSendDataReliable      :确保数据发送成功(TCP协议,对网络压力大)
     GKSendDataUnReliable    :只管发送不管成功(UDP协议,对数据完整性要求不高,对网络压力下)
     */[self.session sendDataToAllPeers:imageData withDataMode:GKSendDataReliable error:nil];
    
}


//数据接受处理方法,此方法需要从文档中粘贴出来,没有智能提示
- (void)receiveData:(NSData *)data fromPeer:(NSString *)peer inSession: (GKSession *)session context:(void *)context
{
    NSLog(@"接收到数据");
     //将NSData转换成UIImage
    UIImage *image =[UIImage imageWithData:data];
    self.imageView.image =image;
}

/*关于蓝牙的数据传输
 
 1. 一次性传送,没有中间方法,所谓中间方法值得是,传输进度比例
 对于用户而言,选择了传输,就需要等待传输完成,或者传输以失败告终
 这就意味着,在实际开发过程中,最好不要用蓝牙传输太大的文件
 
 在实际应用中,蓝牙通常用于传递游戏数据模型,用于联机对战,譬如点对点的棋牌类游戏。
 */

@end
View Code
Core Bluetooth
● Core Bluetooth测试比较麻烦,正常情况下,得至少有2台真实的蓝牙4.0设备
● 如何让iOS模拟器也能测试蓝牙4.0程序?● 买一个CSR蓝牙4.0USB适配器,插在Mac上
● 在终端输入sudo nvram bluetoothHostControllerSwitchBehavior="never"● 重启Mac
● 用Xcode 4.6调试代码,将程序跑在iOS 6.1的模拟器上 (苹果把iOS 7.0模拟器对BLE的支持移除掉了)
● Core Bluetooth的使用场景
● 运动手环、智能家居、嵌入式设备等等(金融刷卡器、心电测量器)
Core Bluetooth的基本常识
● 每个蓝牙4.0设备都是通过服务(Service)和特征(Characteristic)来展示自己 的
● 一个设备必然包含一个或多个服务,每个服务下面又包含若干个特征 ● 特征是与外界交互的最小单位
• 比如说,一台蓝牙4.0设备,用特征A来描述自己的出厂信息,用特征B来收发 数据
 ● 服务和特征都是用UUID来唯一标识的,通过UUID就能区别不同的服务和特征 ● 设备里面各个服务(service)和特征(characteristic)的功能,均由蓝牙设备硬件厂
商提供,比如哪些是用来交互(读写),哪些可获取模块信息(只读)等
Core Bluetooth的开发步骤
● 建立中心设备
● 扫描外设(Discover Peripheral)
● 连接外设(Connect Peripheral)
● 扫描外设中的服务和特征(Discover Services And Characteristics)
● 利用特征与外设做数据交互(Explore And Interact)
● 断开连接(Disconnect)
实例:
#import "ViewController.h"
#import <CoreBluetooth/CoreBluetooth.h>

@interface ViewController ()<CBCentralManagerDelegate, CBPeripheralDelegate>
/**
 *  外设
 */@property (nonatomic, strong) NSMutableArray *peripherals;
/**
 *  中心管理者
 */@property (nonatomic, strong) CBCentralManager *mgr;
@end

@implementationViewController

- (NSMutableArray *)peripherals
{
    if (!_peripherals) {
        _peripherals =[NSMutableArray array];
    }
    return_peripherals;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    //Do any additional setup after loading the view, typically from a nib.
    
    //1.创建中心设备
    CBCentralManager *mgr =[[CBCentralManager alloc] init];
    self.mgr =mgr;
    
    
    //设置代理
    mgr.delegate =self;
    
    //2.利用中心设备扫描外部设备
    /*如果指定数组代表只扫描指定的设备
     */[mgr scanForPeripheralsWithServices:nil options:nil];
}
#pragma mark - CBCentralManagerDelegate
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{

    //保存扫描到得外部设备
    //判断如果数组中不包含当前扫描到得外部设置才保存
    if (![self.peripherals containsObject:peripheral]) {
        
        peripheral.delegate =self;
        [self.peripherals addObject:peripheral];
    }
}

/**
 *  模拟点击, 然后连接所有的外设
 */
- (void)start
{
    for (CBPeripheral *peripheral inself.peripherals) {
        /**
         *  连接外设
         */[self.mgr connectPeripheral:peripheral options:nil];
    }
}
/**
 *  连接外设成功调用
 */
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    //扫描外设中得服务
[peripheral discoverServices:nil];
}
/**
 *  连接外设失败调用
 */
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    
}

#pragma makr - CBPeripheralDelegate
/**
 *  只要扫描到服务就会调用
 *
 *  @param peripheral 服务所在的外设
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    
    //获取外设中所有扫描到得服务
    NSArray *services =peripheral.services;
    for (CBService *service inservices) {
        //拿到需要的服务
        if ([service.UUID.UUIDString isEqualToString:@"123"])
        {
            //从需要的服务中查找需要的特征
            //从peripheral中得service中扫描特征
[peripheral discoverCharacteristics:nil forService:service];
        }
    }
}

/**
 *  只要扫描到特征就会调用
 *
 *  @param peripheral 特征所属的外设
 *  @param service    特征所属的服务
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    
    //拿到服务中所有的特诊
    NSArray *characteristics =service.characteristics;
    //遍历特征, 拿到需要的特征处理
    for (CBCharacteristic * characteristic incharacteristics) {
        if ([characteristic.UUID.UUIDString isEqualToString:@"8888"]) {
            NSLog(@"设置闹钟");

        }
    }
}
@end
View Code
蓝牙的现状
● 绝大多数智能手机支持蓝牙 4.0(BLE)
● 蓝牙芯片发展迅速,在性能和效率方面都有很大提高,且不断变得更小更便宜
● iBeacon +蓝牙,前景一片光明
● 应用之一:室内导航
● Estimote公司为iBeacon提供基站
● 3个iBeacon基站的预购价格为99美元(约合人民币610元)
● Estimote公司推出的iBeacon基站的最远传输距离为50m,但是他们推荐在10m 范围内的使用效果最好
● 一块纽扣电池就能为一个iBeacon基站提供长达 2 年的使用寿命,而且是在设 备不断对外发射信号的情况下

免责声明:文章转载自《IOS 蓝牙(GameKit、Core Bluetooth)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇mysql(4)—— 表连接查询与where后使用子查询的性能分析。koa 搭建模块化路由/层级路由下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

NRF52805低功耗小体积BLE5.0系统级芯片

  nRF52805是NORDIC广受欢迎且经过验证的nRF52系列的第七款产品,支持蓝牙5.0,相当于nRF52810的costdown版本。  nRF52805带有具有出色能效(65 CoreMark/mA)并且功能强大(144 CoreMark)的64MHz 32位Arm® Cortex®-M4处理器,以及192KB闪存和24KB RAM。其多协议(...

【SSH网上商城项目实战19】订单信息的级联入库以及页面的缓存问题

        转自: https://blog.csdn.net/eson_15/article/details/51433247        购物车这一块还剩最后两个问题,就是订单信息的级联入库和页面缓存,这里的信息是指购物车和购物项,即我们将购物车的信息存入数据库的同时,也存入每个购物项的信息,而且外键都关联好,这涉及到了Hiberna...

聊聊 Python 数据处理全家桶(Sqlite篇)

1. 前言 上篇文章 聊到 Python 处理 Mysql 数据库最常见的两种方式,本篇文章继续说另外一种比较常用的数据库:Sqlite Sqlite 是一种 嵌入式数据库,数据库就是一个文件,体积很小,底层由 C 语言编写,经常被集成到移动应用程序中 事实上,Python 内置了 sqlite3 模块,不需要安装任何依赖,就可以直接操作 Sqlite...

会话管理(Cookie/Session技术)

什么是会话:用户打开浏览器,点击多个超链接,访问服务器的多个web资源,然后关闭浏览器,整个过程就称为一个会话; 会话过程需要解决的问题:每个用户在使用浏览器与服务器进行会话的过程中,都可能会产生一些数据,这些输入如何来进行保存?比如用户在购物网站浏览的商品记录,用户添加购物车的记录等等这些信息如何进行存储?在程序中会话跟踪是一件非常重要的事情,一个用户的...

PHP利用MySQL保存session(php5.4之前的处理)

简介 使用MySQL保存session,需要保存三个关键性的数据:session id、session数据、session生命期。 考虑到session的使用方式,没必要使用InnoDB引擎,MyISAM引擎可以获得更好的性能。如果环境允许,可以尝试使用MEMORY引擎。 保存session数据的列,有需要的话,可以使用utf8或utf8mb4字符集;保...

ASP.NET中的状态—基于服务器端的状态管理Session(二)

  前言   我们在前篇“ASP.NET温故而知新学习系列之ASP.NET中的状态—1-状态管理概述”中讲到HTTP协议是个“无状态”的协议,当你从一个页面跳转到另外一个页面,它是不会保存数据的,但是我们需要数据如何从一个页面传递到另一个页面,以及在好多页面当中共享一些数据,为了允许用户通过请求保存状态信息,ASP.NET提供了Session存储机制,这些...