当前位置: 首页 > 科技观察

iBeacon的第一篇文章:基于Swift实现

时间:2023-03-21 13:53:15 科技观察

蓝牙低功耗技术现在几乎已经是智能手机的标配了。随着这项技术的发展,苹果公司在2013年的WWDC大会上推出了iBeacon技术。该技术允许开发者开发iOS应用程序,可以使用iBeacon硬件传感器为相应的应用程序提供更准确的位置信息。在2014年的WWDC大会上,苹果表示改进了iBeacon技术,借助这项技术,应用程序现在可以追踪用户所在楼层的精确位置信息。iBeacon的工作模式是Transmitter-Receiver,即基站-接收模式。基站?这个时候就别想中国移动和中国联通那些大铁塔了。这个基站可以是运行蓝牙4.0LE的设备,也可以是配置好的iPhone或iPad。iPhone4S及之后的iPhone、iPad3及以后的iPad,包括iPadmini都可以配置为iBeacon基站。这里列出了iBeacon的使用场景:用于房屋中介。美国一家科技公司在待售房屋前安装了iBeacon。当用户驾车来到这里时,无需打印或搜索,即可通过中介APP获取房屋的所有相关信息和照片。据说效果还是很好的,大约有一半的用户打开手机查看了相关信息。甚至还有老外用iBeacon做了一款实景商务游戏,点此观看。一个基站主要有三部分标识:1.UUID,如:206A2476-D4DB-42F0-BF73-030236F2C756。用于识别公司。例如,一家房地产公司的所有基站都使用相同的UUID。2.major,用于标识某一种beacon。比如北京这个房地产公司的房子设置为1,上海的房子设置为2。3.minor,用来标识一个特定的beacon。比如某栋楼的某个基站。使用此房地产开发商开发的应用程序,您可以获得UUID,以及以下主要和次要值。当app进入beacon区域时,检测到UUID:“206A2476-D4DB-42F0-BF73-030236F2C756”,major为1,minor为20。然后就显示这个用户在No20房地产经纪人的北京房地产。可以将建筑物的说明文字、图片或视频展示在用户面前。在下面输入我们的教程。但是前提是如果你是苹果开发者,因为这个app需要在真机上运行。那么必须有2个设备,一个作为基站,一个作为接收器。最后,您需要一台可以运行Xcode的计算机。首先创建一个SingleView项目,命名为MyBeacon。然后添加我们需要使用的Framework,在工程中添加CoreBluethooth.framework和CoreLocation.framework。创建一个Storybar,在视图中添加如下UIController:Storyboard的细节我就不多说了。在下面创建两个新文件:TrackViewController和ConfigViewController。这时候会自动生成一个ViewControler类。将它们对应到上图所示的stroyboard的几个ViewControllers,并添加如图所示的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方法来显示数据。这里的NSUUID实例是通过uuid字符串设置的。可以使用uuidgen命令在终端中生成uuid字符串。也可以通过代码生成,但这里没有必要。然后其他值major和minor都设置为1,identifier设置为我们例子的名字。接下来我们将考虑传输问题。用户点击传输按钮后,开始使用设备的蓝牙外围设备发送信号。方法如下。在该方法中,首先通过我们设置的beaconRegion属性获取相关外设数据。此数据是NSDictionary类型。需要注意的是,这个类型在Swift中是可以使用的,但是不兼容Swift自带的Dictionary。之后,初始化CBPeripheralManager属性。这里简单处理,将queue和options设置为nil。为了将委托设置为自我,有必要实施CBPeripheralManagerDelegate协议。实现这个协议后,需要实现这个协议的一个方法。OC中需要这个方法,所以必须实现。我们需要处理两种外设状态。一种是PoweredOn,一种是PoweredOff。当它打开时,让peripheralManager开始发送信号。Off停止发送信号。因此,如果在transmitAction中直接调用代码startAdvertising向设备发送信号,则会报错。控制台将打印“CBPeripheralManager未启动”。所以我们在上面的代码中开始调用PwoeredOn时发送信号的代码。发送信号时,我们将从beaconRegion获取的NSDictionary数据传递给广告方法。这也是接收器用来识别基站的数据。然后我们在界面上显示数据。这个方法很简单,调用viewDidLoad中的setLabels方法即可。基站中设置的所有数据都将显示在界面上。其中包括uuid、major和minor,最后是我们设置的标识符。如果您足够熟悉Swift,用Swift编写代码比使用OC更少。比如在获取一个值的字符值时就不需要使用[NSStringstringWithFormat:"..."]这样麻烦的写法了。我们的应用程序已准备好用作基站。但是如果没有接收器,该应用程序一点也不好玩。让我们开始研究下面的接收器部分。接收iBeacon信息CoreLocationFramework中已经实现了接收iBeacon信号的底层功能。在iOS7中,它可以自动识别你是否进入了某个区域以及距离等其他信息。以下是在TrackViewController中处理的。首先添加CoreLocation库的头文件。然后添加以下两个属性:CLBeaconRegion属性用于定义我们要查找的信标。此应用程序只会接收来自具有相同UUID的发射器的信号。CLLocationManager属性用于设置位置服务和搜索信标。在viewDidLoad方法中添加如下代码:首先初始化CLLocationManager,然后设置代理为self。当然在设置代理之前我们需要先实现CLLocationManagerDelegate协议,这里就不详细写出来了。然后调用initRegion方法,后面会详细介绍。首先创建UUID实例。用于初始化该实例的uuid字符串必须与基站的uuid字符串相同,否则将无法相互找到。然后初始化beaconRegion,proximityUUID是之前创建的UUID实例,identifier是基站中使用的标识符字符串。然后开始监听之前初始化的beaconRegion。调用代码self.trackLocationManager.startMonitoringForRegion(self.beaconRegion)开始检测。接下来,我们需要监听应用程序进入和离开区域的事件。代码如下:第一个方法是进入,这个方法会在你收到基站信号时开始执行(当然,你必须离基站足够近)。然后调用locationManager的startRagingBeaconsInRegion方法,传入我们之前定义的beaconRegion属性。第二种方法很简单,就是离开这个区域就停止执行,调用locationManager的stopRagingBeaconsInRegion。下面是didRangeBeacons方法:首先判断该方法传入的beacons数组的元素个数。如果有0,什么也不做。如果有一个或多个,这里的简单处理只取最后一个beacon进行处理。以下代码只是从beacon中取出UUID、major、minor、accuracy、RSSI等数据显示在界面上。这些值总是会随着接收器和基站之间的距离以及基站的设置而变化。特别是,信标使用精度和RSSI来实现距离。其中,proximity会显示四个值:Unknown、Immediate、Near和Far。Immediate是半米以内,Near是更远,Far是更远。RSSI是信号强度。运行程序现在应用程序已准备好运行。但是,它需要在两台兼容低功耗蓝牙的设备上运行。一个是基站,另一个是接收器。当你从足够远的地方(10-20米)往基站方向走时,你的“didEnterRange”的didRangeBeacons方法会被调用,你可以从方法中传入的信标数组中获取值。现实在界面上。但是需要注意的是,我们处理的基站只有一个,所以每次都取出信标阵列的最后一个。如果有多个信标基站,则需要循环遍历信标阵列的每个元素,以确定该元素的邻近度是Imdiate、Near还是Far。每次测试都进出一个区域太麻烦了。可以在viewDidLoad方法中添加一行代码:然后添加如下方法:调用didStartMonitoringForRegion,在该方法中调用trackLocationManager的startRangingBeaconsInRegion方法。它并不完美,但足以进行测试。下面是代码列表:ConfigViewController:importUIKitimportCoreLocationimportCoreBluetoothclassConfigViewController:UIViewController,CBPeripheralManagerDelegate{//properties@IBOutletvaruuidLabel:UILabel@IBOutletvarmajorLabel:UILabel@IBOutletvarminorLabel:UILabel@IBOutletvaridentityLabel:UILabel@IBOutletvartransmitButton:UIButtonvarbeaconRegion:CLBeaconRegion!varbeaconPeripheralData:NSDictionary!varperipheralManager:CBPeripheralManager!overridefuncviewDidLoad(){super.viewDidLoad()self.initBeacon()self.setLabels()}funcinitBeacon(){self.beaconRegion=CLBeaconRegion(proximityUUID:NSUUID(UUIDString:"206A2476-D4DB-42F0-BF73-030236F2C756"),主要:1,minor:1,identifier:"com.mybeacon.region")}funcsetLabels(){self.uuidLabel.text=self.beaconRegion.proximityUUID.UUIDStringself.majorLabel.text=self.beaconRegion.major.stringValueself.minorLabel.text=self.beaconRegion.minor.stringValueself.identityLabel.text=self.beaconRegion.identifier}overridefuncdidReceiveMemoryWarning(){super.didReceiveMemoryWarning()//Disposeofanyresourcesthatcanberecreated.}@IBActionfunctransmitAction(sender:UIButton){self.beaconPeripheralData=self.beaconRegion.peripheralDataWithMeasuredPower(nil)self.peripheralManager=CBPeripheralManager(delegate:self,queue:nil,options):nil)}//委托方法funcperipheralManagerDidUpdateState(peripheral:CBPeripheralManager!){println("Poweredoff")self.peripheralManager.stopAdvertising()}}}TrackViewController:importUIKitimportCoreLocationclassTrackViewController:UIViewController,CLLocationManagerDelegate{@IBOutletvarbeaconLabel:UILabel@IBOutletvaruuidLabel:UILabel@IBOutletvarmajorLabel:UILabel@IBOutletvarminorLabel:UILabel@IBOutletvaraccuracyLabel:UILabel@IBOutletvaraccuracyLabeleLabel:UILabel@IBOutletvarrssiLabel:UILabelvarbeaconRegion:CLBeaconRegion!vartrackLocationManager:CLLocationManager!overridefuncviewDidLoad(){super.viewDidLoad()self.trackLocationManager=CLLocationManager();self.trackLocationManager.delegate=self;self.initRegion()self.locationManager(self.trackLocationManager,didStartMonitoringForRegion:self.beaconRegion)}funcinitRegion(){varuuid=NSUUID(UUIDString:"206A2476-D4DB-42F0-BF73-030236F2C756")self.beaconRegion=CLBeaconRegion(proximityUUID:uuid,identifier:"com.region.mybe")self.trackLocationManager.startMonitoringForRegion(self.beaconRegion)}funclocationManager(manager:CLLocationManager!,didStartMonitoringForRegionregion:CLRegion!){self.trackLocationManager.startRangingBeaconsInRegion(self.beaconRegion)}overridefuncdidReceiveMemoryWarning(){super.didReceiveMemoryresource.DiscreatedThatresource()/funclocationManager(经理:CLLocationManager!,didEnterRegionregion:CLRegion!){self.trackLocationManager.startRangingBeaconsInRegion(self.beaconRegion)}funclocationManager(manager:CLLocationManager!,didExitRegionregion:CLRegion!){self.trackLocationManager.stopRangingBeaconsInRegion(self.beaconRegion)self.beaconLabel.text="No"}funclocationManager(经理:CLLocationManager!,didRangeBeaconsbeacons:[AnyObject]!,inRegionregion:CLBeaconRegion!){println("beaconscount"+String(beacons.count))ifbeacons.count<=0{return}varbeacon:AnyObject=beacons[beacons.count-1]self.beaconLabel.text="Yes"self.uuidLabel.text=beacon.proximityUUID?beacon.proximityUUID!.UUIDString:""self.majorLabel.text=beacon.major?beacon.major!.stringValue:""self.minorLabel.text=beacon.minor?beacon.minor!.stringValue:""self.accuracyLabel.text=beacon.accuracy?String(beacon.accuracy):""ifbeacon.proximity{switch(beacon.proximity!){case.Unknown:self.distanceLabel.text="Unknownproximity"caseCLProximity.Immediate:self.distanceLabel.text="Immediate"caseCLProximity.Near:self.distanceLabel.text="Near"caseCLProximity.Far:self.distanceLabel.text="Far"默认值:}}self.rssiLabel.text=beacon.rssi?beacon.rssi!.description:""}}