使用 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

不过,请注意,CLBeaconproximity 字段可能不会像您希望的那样快速更新。该字段源自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

是否有任何方法可以在我的应用程序中不使用 CLLocationManager 委托来获取当前位置

iPhone - CLLocationManager 在尝试获取当前位置时停止更新