Как определить комбинированные события входа/выхода для CLCircularRegion и CLBeaconRegion?

avatar
MiRin
8 апреля 2018 в 07:44
40
1
0

Я отслеживаю как iBeacons, так и круглые области в iOS. Если я попытаюсь определить их отдельно:

- (void) locationManager:(CLLocationManager *)manager didExitRegion:(CLCircularRegion *)region {}
- (void) locationManager:(CLLocationManager *)manager didExitRegion:(CLBeaconRegion *)region {}

Очевидно, что это дублирующая ошибка объявления. Этот ответ SO хорошо указывает, как отличить, какой регион сработал. Чтобы зайти так далеко, я пытаюсь выяснить, как определить событие выхода из общего региона, которым все были бы довольны. Если я объединю их с помощью CLRegion, будут разные предупреждения:

- (void) locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
    if (region.class == CLCircularRegion.class) {
        CLLocationCoordinate2D coordinate = [region center];  // Deprecation warning, use CLCircularRegion instead
        // etc...
    }
    if (region.class == CLBeaconRegion.class) {
        [locationManager stopRangingBeaconsInRegion:region]; // Incompatible pointer types warning
        // etc...
    }
}

Изначально я определил обработку маяка с помощью didDetermineState, но это приводит к некоторым дополнительным событиям. Например. CLRegionStateOutside изначально срабатывает для всех контролируемых регионов, что вполне уместно, но мне не нужно это знать, поэтому вместо этого я бы предпочел использовать didExitRegion.

Есть ли правильный способ написать чистый комбинированный didExitRegion, чтобы он мог обрабатывать как круговые, так и маяковые области без ошибок или предупреждений?

Источник

Ответы (1)

avatar
Paulw11
8 апреля 2018 в 08:54
3

Сигнатура метода определяется протоколом CLLocationManagerDelegate и имеет следующий вид:

- (void)locationManager:(CLLocationManager *)manager 
     didExitRegion:(CLRegion *)region;

Вы должны использовать эту подпись, если хотите, чтобы метод вызывался.

Вы на правильном пути, проверив тип региона, который вы передали. Немного, чего вам не хватает, - это использовать приведение для назначения region переменной соответствующего подкласса, чтобы вы не получали предупреждений/ошибок при попытке выполнить действия, специфичные для подкласса.

Обратите внимание, что безопаснее использовать isKindOfClass:, чем сравнивать классы напрямую. isKindOfClass возвращает YES, если объект является экземпляром указанного класса или подклассом указанного класса. Таким образом, если Apple внесет некоторые внутренние изменения и начнет присылать вам объекты CLNewBeaconRegion, которые являются подклассом CLBeaconRegion, ваш код все равно будет работать.

- (void) locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
    if ([region isKindOfClass:CLCircularRegion.class]) {
        CLCircularRegion *circularRegion = (CLCircularRegion *)region;
        CLLocationCoordinate2D coordinate = circularRegion.center;
        // etc...
    } else if ([region isKindOfClass:CLBeaconRegion.class]) {
        CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;
        [locationManager stopRangingBeaconsInRegion:beaconRegion];
        // etc...
    }
}
MiRin
8 апреля 2018 в 10:10
0

Спасибо, работает отлично! Я думал, что кастинг подойдет, но после прочтения этого запутался: «Вы не можете просто привести суперкласс к его подклассу. На самом деле он не реализует какие-либо из ваших добавленных переменных/свойств или методов». Мне нужно было некоторое подтверждение того, что это действительно правильный путь.

Paulw11
8 апреля 2018 в 10:13
1

Вы не можете привести экземпляр суперкласса к подклассу, но в этом случае у вас есть экземпляр подкласса (который вы проверили с помощью isKindOfClass), поэтому вы можете выполнить преобразование.