iBeacon的第一篇(基于Swift实现)

低功耗蓝牙技术现在几乎是只能手机的标配。随着这一技术的发展,苹果在2013年WWDC大会上,苹果推出iBeacon技术。该技术允许开发人员开发能够使用iBeacon硬件传感器的iOS应用程序,来为相应的应用程序提供更加精准的位置信息。2014年WWDC大会上,苹果表示,对iBeacon技术进行了改善,借助该技术,应用程序现在能够跟踪到用户所在的楼层的精确位置信息。

iBeacon的工作方式是Transmitter-Receiver,即基站-接收机模式的。基站?这个时候不要想到移动、联通的那些大铁塔。这个基站可以是一个运行着Bluetooth 4.0 LE的设备,也可以是经过配置的iPhone、iPad。iPhone4S和之后的iPhone、iPad3或之后的iPad,包括iPad mini都可以配置成iBeacon基站。

这里列举个iBeacon的使用场景:在房屋中介中使用。米国一家技术公司把 iBeacon 安装在要出售的房屋前,当用户开车至此,不用下车就可以用中介的 APP 获得此房屋所有相关信息和照片,不用打印及搜索。据说,效果还是很不错的,大约有一半左右的用户打开手机查看了相关信息。甚至于还有一个老外用iBeacon做了一个实景的经营游戏,点击这里观看。

一个基站主要有三部分标识:

1. UUID,形如:206A2476-D4DB-42F0-BF73-030236F2C756。用来标识某一个公司。比如,某个房地产公司的的全部的基站都用同一个UUID。

2. major,用来标识某一类的beacon。比如这个房地产公司的北京的房子都设定为1,上海的都设定为2。

3. minor,用来标识某一个特定的beacon。比如某栋楼的某个基站。

用这个房地产商开发的app就可以获取到UUID,和后面的major值和minor值。当app进入beacon的区域,探测到UUID:”206A2476-D4DB-42F0-BF73-030236F2C756“,major为1,minor为20。那么就表明这个用户在这个房地产商的北京楼盘的编号20的楼盘。这栋楼的说明文字、图片或者视频等就可以展现在用户面前。

下面就进入我们的教程。不过前提是你要是苹果的开发者,因为这个app需要在真机运行。然后要有2台设备,一个当基站,一个当接收机。最后,还得有一台能运行Xcode的电脑。

首先创建一个SingleView的项目,起个名字叫MyBeacon。然后把我们需要用到的Framework加进来,把CoreBluethooth.framework和CoreLocation.framework加到项目中。

创建一个Storybard,并在视图中添加如下的UIController:

(这是Xcode6创建的storyboard)

关于storyboard的细节就不多说了。下面新建两个文件:TrackViewController和ConfigViewController。这时候会自动生成一个ViewControler类。分别把他们按照上图所示对应到stroyboard的几个ViewController中,并添加图中所示的UILabel。然后把需要现实APP运行结果的UILabel作为IBOutlet。

TrackViewController的IBOutlet是这样的:(以下的两个文件都用的是Swift语言,如果你需要的话可以自行修改为OC语言)

ConfigViewController的IBOutlet是这样的:

先搞定基站部分

在ConfigViewController中添加如下的属性:

第一个CLBeaconRegin属性,用来设置基站需要的proximityUUID,major和minor的给你信息。

第二个NSDictionary的属性,用来获取外设的数据。

第三个CBPeripheralManager的属性,用来开启基站的数据传输。

这些属性后面的感叹号是Swift里的一种语法。变量或者属性的定义,在类型的后面可以是问号后者是感叹号。他们都表示这个变量或者属性可以是有值的也可以是空值nil(注意这里的nil和OC里的nil是两回事)。但是使用问号的变量或者属性需要在使用前判断是否有值,如果有的话通过感叹号取值。使用感叹号定义的变量或者属性则可以直接取值并使用。这里使用感叹号定义属性,这样在使用的时候可以直接取值。详细的使用会在代码中体现。

要让iOS设备开始传输信号还需要做些其他的事情。在viewDidLoad中调用几个方法才能让基站工作起来。首先调用的是initBeacon方法,之后是要把数据现实出来的setLabels方法。

在这里通过uuid字符串设定NSUUID实例。uuid字符串可以通过uuidgen命令在终端里生成。也可以通过代码生成,但是这里没有这个必要。然后其他的值major和minor分别设定为1,identifier设定为我们示例的名字。

下面我们就要考虑传输的问题了。在用户点击了传输按钮之后开始使用设备的蓝牙外设发出信号。方法如下:

在这个方法里,首先通过我们设定的beaconRegion属性获得相关的外设数据。这个数据是NSDictionary类型的。需要说明的是,这个类型可以在Swift中使用,但是和Swift本身的Dictionary是不兼容的。

之后,初始化了CBPeripheralManager属性。这里简单处理,设定queue和options为nil。为了设定代理为self需要实现CBPeripheralManagerDelegate protocol。

实现了这个protocol之后,还需要实现这个protocol的一个方法。这个方法在OC里来讲是required的,所以必须实现。

外设的状态中有两个需要我们处理的。一个是PoweredOn一个PoweredOff。On了就让peripheralManager开始对外发出信号。Off了就停止发出信号。所以呢,如果直接在transmitAction中调用代码startAdvertising是设备对外发出信号的话,会报错。Console会打印出”CBPeripheralManager is not Powered on“。所以我们在以上的代码中在Pwoered On的时候再开始调用代码发出信号。发出信号的时候呢,我们给了advertising方法传入了从beaconRegion取出来的NSDictionary数据。这也是接收机辨识基站用到的数据。

之后我们把数据显示在界面上。

这个方法很简单,只要在viewDidLoad里调用setLabels方法就可以。全部设定在基站里的数据都会显示在界面上了。其中包括uuid、major和minor,最后是我们设定的identifier。用Swift比用OC写代码是会少一些量,如果你对Swift足够熟的话。比如,取得一个值的字符窜值的时候就不用[NSString stringWithFormat:"..."]这么麻烦的写法了。

我们的app已经可以作为基站使用了。但是没有接收机的话这个app可是一点都不好玩。在下面就开始处理接收机的部分。

接受iBeacon信息

接收iBeacon信号的底层功能已经在Core Location Framework里实现了。在iOS7里,可以自动识别你是否进入了一个区域以及其他的距离之类的信息。

下面在TrackViewController中处理,首先把CoreLocation库的头文件加进来。然后增加下面的两个属性:

CLBeaconRegion属性是用来定义我们要寻找的beacon的。这个app只会接收到有同样的UUID的的发射机发射的信号。

CLLocationManager属性是用来建立位置服务并搜索beacon的。

在viewDidLoad方法中添加如下代码:

首先,要初始化CLLocationManager,然后把代理设置为self。当然设定代理之前我么需要实现CLLocationManagerDelegate protocol,具体的就不写出来了。然后调用initRegion方法,这个会在后面给出详情。

首先创建UUID实例。用来初始化这个实例的的uuid字符串必须和基站的uuid字符串是一样的,要不互相找不见。

然后初始化beaconRegion,proximityUUID就是前面创建的UUID实例,identitifer就是在基站中用到的identifier字符串。然后开始监测前面初始化出来的beaconRegion。调用代码self.trackLocationManager.startMonitoringForRegion(self.beaconRegion)开始检测。

接下来,我们需要监测这个app进入和离开区域的事件,代码如下:

第一个方法是进入的,当你接收到基站信号的时候这个方法就开始执行(当然你要离基站足够近)。然后调用locationManager的startRagingBeaconsInRegion方法,病传入我们之前定义好的beaconRegion属性。

第二个方法很简单,就是在离开这个区域的时候就停止执行,调用locationManager的stopRagingBeaconsInRegion。

下面是didRangeBeacons方法:

首先判断方法传入的beacons数组的元素有多少。如果有0个的时候什么都不做。如果有一个或以上的话,这里简单处理只取最后一个beacon来处理。

之后的代码都只是从beacon中取出数据来现实在界面上,比如UUID,major,minor,accuracy和RSSI。这些值会随着接收机和基站的距离不同以及基站的设置不同而一直改变。尤其accuracy和RSSI都是beacon用来现实距离的。其中proximity会显示四个值:Unknown、Immediate、Near和Far。Immediate是半米以内,Near远一些,Far更远。RSSI是信号强度。

运行程序

现在这个app可以运行了。但是需要分别运行在两台可以兼容低功耗蓝牙的设备上。一台当基站一台当接收机。

当你从足够远(10~20米)的地方想基站的方向走的时候,一个你”didEnterRange”那个didRangeBeacons方法就会被调用,这是就可以从方法中传入的beacon数组中取出值来现实在界面上。

但是有一点需要注意的是,我们处理的基站只有一个,所以每次都取出beacon数组的最后一个。如果有多个beacon基站的话就需要循环beacon数组的每一个元素,判断这个元素的proximity是Immdiate还是Near还是Far等。

每次测试的时候都进入或者离开一个区域太麻烦。可以在viewDidLoad方法中添加一行代码:

然后添加如下方法:

手动调用了didStartMonitoringForRegion,在这个方法中调用了trackLocationManager的startRangingBeaconsInRegion方法。这样虽然不完美,但是足够测试了。

下面是代码列表:

ConfigViewController:

 1 import UIKit
 2 import CoreLocation
 3 import CoreBluetooth
 4
 5 class ConfigViewController: UIViewController, CBPeripheralManagerDelegate {
 6
 7     // properties
 8     @IBOutlet var uuidLabel: UILabel
 9     @IBOutlet var majorLabel: UILabel
10     @IBOutlet var minorLabel: UILabel
11     @IBOutlet var identityLabel: UILabel
12     @IBOutlet var transmitButton: UIButton
13
14     var beaconRegion : CLBeaconRegion!
15     var beaconPeripheralData : NSDictionary!
16     var peripheralManager : CBPeripheralManager!
17
18
19     override func viewDidLoad() {
20         super.viewDidLoad()
21
22         self.initBeacon()
23         self.setLabels()
24     }
25
26     func initBeacon(){
27         self.beaconRegion = CLBeaconRegion(proximityUUID: NSUUID(UUIDString: "206A2476-D4DB-42F0-BF73-030236F2C756")
28             , major: 1, minor: 1, identifier: "com.mybeacon.region")
29     }
30
31     func setLabels(){
32         self.uuidLabel.text = self.beaconRegion.proximityUUID.UUIDString
33         self.majorLabel.text = self.beaconRegion.major.stringValue
34         self.minorLabel.text = self.beaconRegion.minor.stringValue
35         self.identityLabel.text = self.beaconRegion.identifier
36     }
37
38     override func didReceiveMemoryWarning() {
39         super.didReceiveMemoryWarning()
40         // Dispose of any resources that can be recreated.
41     }
42
43     @IBAction func transmitAction(sender: UIButton) {
44         self.beaconPeripheralData = self.beaconRegion.peripheralDataWithMeasuredPower(nil)
45         self.peripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: nil)
46     }
47
48     // delegate methods
49     func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager!){
50         if peripheral.state == CBPeripheralManagerState.PoweredOn {
51             println("Powered on")
52             self.peripheralManager.startAdvertising(self.beaconPeripheralData)
53         }
54         else if peripheral.state == CBPeripheralManagerState.PoweredOff {
55             println("Powered off")
56             self.peripheralManager.stopAdvertising()
57         }
58     }
59 }

TrackViewController:

 1 import UIKit
 2 import CoreLocation
 3
 4 class TrackViewController: UIViewController, CLLocationManagerDelegate {
 5
 6     @IBOutlet var beaconLabel: UILabel
 7     @IBOutlet var uuidLabel: UILabel
 8     @IBOutlet var majorLabel: UILabel
 9     @IBOutlet var minorLabel: UILabel
10     @IBOutlet var accuracyLabel: UILabel
11     @IBOutlet var distanceLabel: UILabel
12     @IBOutlet var rssiLabel: UILabel
13
14     var beaconRegion : CLBeaconRegion!
15     var trackLocationManager : CLLocationManager!
16
17     override func viewDidLoad() {
18         super.viewDidLoad()
19
20         self.trackLocationManager = CLLocationManager();
21         self.trackLocationManager.delegate = self;
22         self.initRegion()
23         self.locationManager(self.trackLocationManager, didStartMonitoringForRegion: self.beaconRegion)
24     }
25
26     func initRegion(){
27         var uuid = NSUUID(UUIDString: "206A2476-D4DB-42F0-BF73-030236F2C756")
28         self.beaconRegion = CLBeaconRegion(proximityUUID: uuid, identifier: "com.mybeacon.region")
29         self.trackLocationManager.startMonitoringForRegion(self.beaconRegion)
30     }
31
32     func locationManager(manager: CLLocationManager!, didStartMonitoringForRegion region: CLRegion!) {
33         self.trackLocationManager.startRangingBeaconsInRegion(self.beaconRegion)
34     }
35
36     override func didReceiveMemoryWarning() {
37         super.didReceiveMemoryWarning()
38         // Dispose of any resources that can be recreated.
39     }
40
41     func locationManager(manager: CLLocationManager!, didEnterRegion region: CLRegion!) {
42         self.trackLocationManager.startRangingBeaconsInRegion(self.beaconRegion)
43     }
44
45     func locationManager(manager: CLLocationManager!, didExitRegion region: CLRegion!) {
46         self.trackLocationManager.stopRangingBeaconsInRegion(self.beaconRegion)
47         self.beaconLabel.text = "No"
48     }
49
50     func locationManager(manager: CLLocationManager!, didRangeBeacons beacons: [AnyObject]!, inRegion region: CLBeaconRegion!) {
51         println("beacons count" + String(beacons.count))
52         if beacons.count <= 0 {
53             return
54         }
55
56         var beacon: AnyObject = beacons[beacons.count - 1]
57
58         self.beaconLabel.text = "Yes"
59         self.uuidLabel.text = beacon.proximityUUID ? beacon.proximityUUID!.UUIDString : ""
60         self.majorLabel.text = beacon.major ? beacon.major!.stringValue : ""
61         self.minorLabel.text = beacon.minor ? beacon.minor!.stringValue : ""
62         self.accuracyLabel.text = beacon.accuracy ? String(beacon.accuracy) : ""
63         if beacon.proximity {
64
65             switch(beacon.proximity!){
66             case .Unknown:
67                 self.distanceLabel.text = "Unknown proximity"
68             case CLProximity.Immediate:
69                 self.distanceLabel.text = "Immediate"
70             case CLProximity.Near:
71                 self.distanceLabel.text = "Near"
72             case CLProximity.Far:
73                 self.distanceLabel.text = "Far"
74             default:
75                 println("default of switch-case")
76             }
77         }
78         self.rssiLabel.text = beacon.rssi ? beacon.rssi!.description : ""
79     }
80 }

转载请注明出处!

iBeacon的第一篇(基于Swift实现),布布扣,bubuko.com

时间: 2024-10-12 04:01:21

iBeacon的第一篇(基于Swift实现)的相关文章

第一篇博客——基于数组的优先队列(java版)

看过园子里和CSND上那么多大牛精彩的博客后,早就按捺不住想亲手写上几篇.奈何每次坐在电脑前准备敲字的时候,立马赶到浑身不自在,无从下手.实在是因为自高考之后,大学以来,本人几乎就再没动笔写过一篇文字,写作水平退化实在严重.今天鼓起勇气开始写作博客,一方面希望通过多写慢慢地找回写作的感觉,一方面也希望通过博客和大家多多交流,共同进步. 既然是第一次试手,就写个简单易懂的内容——优先队列. 话不多说,先上代码. 1 /** 2 * @author Mr Left 3 * @version 1.0

基于C# Winform的简易聊天程序[第一篇-两端通信]

程序简介 本聊天程序支持局域网内部客户端与服务端之间的互相通信. 原理 启动服务端后,服务端通过持续监听客户端发来的请求,一旦监听到客户端传来的信息后,两端便可以互发信息了.服务端需要绑定一个IP,用于客户端在网络中寻找并建立连接.信息发送原理:将手动输入字符串信息转换成机器可以识别的字节数组,然后调用套接字的Send()方法将字节数组发送出去.信息接收原理:调用套接字的Receive()方法,获取对端传来的字节数组,然后将其转换成人可以读懂的字符串信息. 界面设计 - 服务端 IP文本框 na

基于Swift语言开发微信、QQ和微博的SSO授权登录代码分析

前言 Swift 语言,怎么说呢,有一种先接受后排斥,又欢迎的感觉,纵观国外大牛开源框架或项目演示,Swift几乎占据了多半,而国内虽然出现很多相关技术介绍和教程,但是在真正项目开发中使用的占据很少部分,原因一是目前熟练它的开发者并不多,二是版本不太稳定,还需要更成熟可靠的版本支持,但总之未来还是很有前景的,深有体会,不管是代码量还是编译效率,以及语言特性,现代性都优于Object-C,估计后续会被苹果作为官方开发语言,值得期待. 走起 鉴于此,笔者将之前用Object-C写的SSO授权登录:微

第二篇 基于微擎的模块开发—PHP

从陌生到如今能勉强完成第一个微网站模块的实现.也算是一个小小的进步,从设计数据库到,返回数据,前端模版渲染 每一点都是有点难度的.所以我想总结一下,我是如何实现一个微擎模块. 第一,首先得分析某个模块的想实现什么需求,根据需求设计合理的数据库结构. 第二,了解微擎的结构,运行流程,设计模块结构. 第三,重点就是site.php , 完成site.php 需要一定的php的编程能力, 第四,site.php 其中 通过 pdo 从数据库的获取我们想得到数据源. 微擎已封装其路由机制, doWeb

第一篇 SQL Server安全概述

本篇文章是SQL Server安全系列的第一篇,详细内容请参考原文. 面对当今复杂的攻击SQL Server有你需要的一切来保护你的服务器和数据.但在你能有效地使用这些安全功能之前,你需要了解你所面临的威胁和一些基本的安全概念.本系列的第一篇将讲解基础知识,可以充分利用SQL Server中的安全功能而不是浪费时间在不能保护你的数据被威胁的功能上.Relational databases are used in an amazing variety of applications with co

从Hello, world开始认识IL &lt;第一篇&gt;

IL代码分析方法 Hello, world历史 .NET学习方法论 1.引言 1988年Brian W.Kernighan和Dennis M.Ritchie合著了软件史上的经典巨著<The C programming Language>,我推荐所有的程序人都有机会重温这本历史上的经典之作.从那时起,Hello, world示例就作为了几乎所有实践型程序设计书籍的开篇代码,一直延续至今,除了表达对巨人与历史的尊重,本文也以Hello, world示例作为我们扣开IL语言的起点,开始我们循序渐进的

C# 基础笔记(第一篇)

C#基础 概念:.net与c#.net/dontnet:一般指.net framework框架,一种平台,一种技术c#(charp):一种编程语言,可以开发基于.net的应用. *java既是一种技术又是一种编程语言.                           .net都能干什么?开发桌面应用程序   Winforminternet应用程序    Asp.net/webservice C/S:客户机(Client)/服务器模式(Server)B/S:浏览器(Browser)/务器模式(

前端学HTTP之报文系列第一篇——起始行

前面的话 如果说HTTP是因特网的信使,那么HTTP报文就是它用来搬东西的包裹了.HTTP报文是在HTTP应用程序之间发送的简单的格式化数据块,每条报文都包含一条来自客户端的请求,或者一条来自服务器的响应.它们由三个部分组成:由起始行.首部和实体的主体部分.本文是HTTP报文系列第一篇——起始行 报文语法 所有的HTTP报文都可以分为两类:请求报文(request message)和响应报文(response message).请求报文会向Web服务器请求一个动作,响应报文会将请求的结果返回给客

Python全栈开发【第一篇】:初识Python

Python全栈开发[第一篇] 本节内容: Python 的种类 Python 的环境 Python 入门(解释器.编码.变量.input输入.if流程控制与缩进.while循环) if流程控制与while循环练习题 基本数据类型前引 Python 的种类 Cpython Python的官方版本,使用C语言实现,使用最为广泛,CPython实现会将源文件(py文件)转换成字节码文件(pyc文件),然后运行在Python虚拟机上. Jyhton Python的Java实现,Jython会将Pyth