使用 CLLocationManager 获取 iBeacon 邻近度更新
Posted
技术标签:
【中文标题】使用 CLLocationManager 获取 iBeacon 邻近度更新【英文标题】:Get iBeacon proximity updates using CLLocationManager 【发布时间】:2017-01-30 18:28:25 【问题描述】:我正在尝试编写一个应用程序,该应用程序会定期接收有关与当前找到的 iBeacons 的接近程度的更新。我有两个要跟踪的 iBeacon,它们是我自己的,我硬编码了 UUID 以便跟踪到我的代码中。 iBeacon 本身具有相同的主要 ID,但次要 ID 不同。我的问题是关于如何定期更新有关使用预设 UUID 找到的 iBeacons 的接近程度。我知道这需要使用 CLLocationManager 来完成,因为它实现了 iBeacon 测距。
我目前对 CLLocationManager 的实验:
import UIKit;
import CoreLocation;
import HomeKit;
class FirstViewController: UIViewController, CLLocationManagerDelegate, HMHomeManagerDelegate, UIPickerViewDelegate, UIPickerViewDataSource
let homeManager = HMHomeManager();
let locationManager = CLLocationManager();
var beaconsInProximity: [CLBeacon] = [];
let beaconUUID: String = "11984894-7042-9801-839A-ADECCDFEDFF0";
let beaconMajor = 0x1;
let beaconMinor: [Int] = [0x1, 0x7];
let homeName = "Home";
var lamps = [HMAccessory]();
var lampNames = [String]();
var pickerNames = [String: HMAccessory]();
var selectedLamp: HMAccessory!;
var lightHome: HMHome!;
var firstLight: HMAccessory!;
var secondLight: HMAccessory!;
var thirdTestLight: HMAccessory!;
let beaconRegion: CLBeaconRegion = CLBeaconRegion(proximityUUID: NSUUID(uuidString:"11984894-7042-9801-839A-ADECCDFEDFF0")as! UUID, identifier: "Beaconons");
@IBOutlet weak var lampSwitch: UISwitch!
@IBOutlet weak var configureLampButton: UIButton!
@IBOutlet weak var lampPicker: UIPickerView!
@IBOutlet weak var identifyLampButton: UIButton!
@IBOutlet weak var lampSelectedLabel: UILabel!
@IBOutlet weak var lampStatusLabel: UILabel!
@IBOutlet weak var beaconStatusLabel: UILabel!
override func viewDidLoad()
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if(selectedLamp != nil)
updateLampLabel(selectedLamp);
updateLampStatusLabel(selectedLamp);
else
updateLampLabelNoLamp();
updateLampStatusLabelNoStatus();
homeManager.delegate = self;
lampPicker.delegate = self;
lampPicker.dataSource = self;
locationManager.delegate = self;
locationManager.requestAlwaysAuthorization();
locationManager.startMonitoring(for: beaconRegion);
locationManager.startRangingBeacons(in: beaconRegion);
locationManager.requestState(for: beaconRegion);
override func didReceiveMemoryWarning()
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
func homeManagerDidUpdateHomes(_ manager: HMHomeManager)
for home in homeManager.homes
for accessory in home.accessories
if(accessory.name.contains("lamp"))
lamps.append(accessory);
pickerNames[String(describing: accessory.services[1].characteristics[0].value)] = accessory;
print("Added accessory " + String(describing: accessory.services[1].characteristics[0].value) + " to lamp list");
if(lamps.count != 0)
for index in 0...(lamps.count - 1)
lampNames.append(String(describing: lamps[index].services[1].characteristics[0].value));
func locationManager(_: CLLocationManager, didRangeBeacons: [CLBeacon], in: CLBeaconRegion)
for beacon in didRangeBeacons
if(beacon.proximity == CLProximity.immediate)
beaconsInProximity.append(beacon);
else
if(beaconsInProximity.contains(beacon))
for index in 0...beaconsInProximity.count
if(beaconsInProximity[index] == beacon)
beaconsInProximity.remove(at: index);
for beacon in beaconsInProximity
if(beacon.major.intValue == beaconMajor)
if(beacon.minor.intValue == 0x3)
if(selectedLamp != nil)
switchLamp(selectedLamp, true);
beaconStatusLabel.text = "region detected";
else
if(selectedLamp != nil)
switchLamp(selectedLamp, false);
beaconStatusLabel.text = "lamping all the lamps that have ever lamped";
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion)
let bRegion = region as! CLBeaconRegion;
if(bRegion.proximityUUID == beaconRegion.proximityUUID)
print("Correct region");
beaconStatusLabel.text = "Entered beacon region";
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion)
let bRegion = region as! CLBeaconRegion;
if(bRegion.proximityUUID == beaconRegion.proximityUUID)
print("Exited correct region");
beaconStatusLabel.text = "Beacon region left";
@IBAction func configureLampButtonPressed(_ sender: AnyObject)
if(pickerNames[lampNames[lampPicker.selectedRow(inComponent: 0)]] != nil)
selectedLamp = pickerNames[lampNames[lampPicker.selectedRow(inComponent: 0)]];
if(selectedLamp != nil)
updateLampLabel(selectedLamp);
updateLampStatusLabel(selectedLamp);
else
updateLampLabelNoLamp();
updateLampStatusLabelNoStatus();
@IBAction func identifyLampButtonPressed(_ sender: AnyObject)
if(selectedLamp != nil)
identifyLamp(selectedLamp);
@IBAction func lampSwitchFlipped(_ sender: AnyObject)
if(selectedLamp != nil)
switchLamp(selectedLamp, lampSwitch.isOn);
updateLampStatusLabel(selectedLamp);
else
lampSwitch.setOn(!lampSwitch.isOn, animated: true);
updateLampStatusLabelNoStatus();
func updateLampLabelNoLamp()
lampSelectedLabel.text = "No lamp selected";
func updateLampLabel(_ lamp: HMAccessory)
lampSelectedLabel.text = "Selected lamp: " + lamp.name;
func numberOfComponents(in pickerView: UIPickerView) -> Int
return 1;
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
return lampNames.count;
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
return lampNames[row];
func booleanToInt(_ value: Bool) -> Int
if(value)
return 1;
else
return 0;
func sendLocalNotificationWithMessage(_ message: String!)
func switchLamp(_ lamp: HMAccessory, _ value: Bool)
lamp.services[1].characteristics[1].writeValue(booleanToInt(value), completionHandler:
error in
if let error = error
print("Something went wrong! \(error)");
)
func identifyLamp(_ lamp: HMAccessory)
lamp.services[0].characteristics[3].writeValue(1, completionHandler:
error in
if let error = error
print("Something went wrong! \(error)");
)
func updateLampStatusLabel(_ lamp: HMAccessory)
lampStatusLabel.text = "Lamp status: " + String(describing: selectedLamp.services[1].characteristics[1].value);
func updateLampStatusLabelNoStatus()
lampStatusLabel.text = "Select a lamp";
应用本身的目标是根据用户是否在附近来切换灯泡。
【问题讨论】:
问题是什么?你的代码现在在做什么?你有什么问题? 您需要为要监控的信标添加CLBeaconRegion
实例。如果可以同时看到两个信标,则需要注册两个区域。
@davidgyoung 我更新了我的问题并将代码更改为我的整个 FirstViewController.swift 文件的内容。我有一个 iBeacon,我想在检测到它后立即接近它。一旦检测到它,我想定期更新接近度,以便我知道何时切换我的灯。我不知道如何按照这些思路实现。
【参考方案1】:
beacon.proximity 字段将在每次回调到func locationManager(_: CLLocationManager, didRangeBeacons: [CLBeacon], in: CLBeaconRegion)
时更新。这些回调将每秒发生一次,其中包含具有此字段的 CLBeacon 对象数组。所以每 1 秒你就会得到一个更新。
根据代码的设置方式,如果信标在附近,beaconsInProximity
似乎将正确更新,并且稍后将在回调方法中调用switchLamp
。
不过,请注意,CLBeacon
的 proximity
字段可能不会像您希望的那样快速更新。该字段源自accuracy
字段,它是以米为单位的距离估计值。如果距离估计 accuracy 将设置为立即。如果距离估计值 > 0.5 米,它将被设置为另一个值。
accuracy
中的距离估计基于 RSSI 测量值的 20 秒运行平均值。因此,如果您从 5 米外快速移动到信标发射器旁边,accuracy
字段将在 20 秒内从 ~5.0 缓慢变为 ~0.0。 proximity
字段显示 immediate
需要将近 20 秒。
此外,重要的是要了解,必须使用在信标内设置的测量功率值正确校准信标,以便尽可能准确地估计距离。如果您在accuracy
中得到非常不准确的距离估计,即使在静止不动 20 秒后,您可能需要进行此校准。
【讨论】:
当我尝试我的设置时,看起来func locationManager(_: CLLocationManager, didRangeBeacons: [CLBeacon], in: CLBeaconRegion)
甚至没有被调用。我只在控制台中收到func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion)
和func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion)
的消息。我的if(beacon.proximity == CLProximity.immediate)
声明似乎有问题。我做错了什么?
哦,哇,我认为您的回调方法签名不正确。这是 Swift 3.0 的正确签名:func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion)
我把函数改成了func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion)
,if(beacon.proximity == CLProximity.immediate)
还是不行。我看不出我做错了什么。
我不知道是什么修复了它,但是今天尝试后它起作用了。现在完美运行。以上是关于使用 CLLocationManager 获取 iBeacon 邻近度更新的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 CLLocationManager-Swift 获取当前的经度和纬度
使用 CoreLocation CLLocationManager 获取过去的位置
飞行模式开启时 CLLocationManager 是如何获取位置的
CLLocationManager 获取位置很慢,Swift