Конфликт анимации didEnterRegion и didExitRegion противоречив

Я создаю приложение, которое использует GeoFencing. Мои методы didEnterRegion и didExitRegion вызываются так, как они должны, по-видимому, в правильном порядке, но не могут соответствующим образом обновить пользовательский интерфейс.

Что работает:

  • Пользователь входит в регион
  • didEnterRegion вызывается
  • Пользовательский интерфейс корректно обновляется в рамках этого метода

Что не работает:

  • Пользователь вводит регион
  • Пользователь идет прямо из одного региона в другой
  • didExitRegion называется
  • Обновления пользовательского интерфейса
  • didEnterRegion вызывается
  • NSLogs показывают, что все выполняется в правильном порядке
  • Пользовательский интерфейс не обновляется. Обновления пользовательского интерфейса, которые были сделаны в didExitRegion, остаются.

Мои методы:

Пользовательская функция для обновления метки (вызывается из didEnterRegion и didExitRegion):

-(void)updateCurrentLocationLabelAndImage:(NSString *)locationText subLocationText:(NSString *)subLocationText;
{
// Clear existing animations before we begin
[self.locationLabel.layer removeAllAnimations];
[self.subLocationLabel.layer removeAllAnimations];
[self.ovalImageView.layer removeAllAnimations];
if (![locationText isEqual:@""])
{
    // Only animate if the text changes
    if (![self.locationLabel.text isEqualToString:locationText])
    {
        // Update the ovalImageView
        CGSize maxLabelSize = CGSizeMake(([UIScreen mainScreen].bounds.size.width - (([UIScreen mainScreen].bounds.size.width * 0.0267) * 2)), 64); // maximum label size
        float expectedLabelWidth = [locationText boundingRectWithSize:maxLabelSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{ NSFontAttributeName:self.locationLabel.font } context:nil].size.width; // get expected width

        CGFloat xValueForImage = ((([UIScreen mainScreen].bounds.size.width - expectedLabelWidth) / 2) - 25); // Calcuate the x-coordinate for the ovalImageView
        if (xValueForImage < 15)
        {
            xValueForImage = 15; // we don't want it to display off-screen
        }
        self.ovalImageViewLeadingConstraint.constant = xValueForImage;
        [self.view setNeedsUpdateConstraints];
        [self changeLabelText:self.subLocationLabel string:subLocationText];
// Update the subLocationLabel
        [UIView animateWithDuration:.15 animations:^{
            // Animate
            self.locationLabel.alpha = 0;
            self.ovalImageView.alpha = 1;
         [self.view layoutIfNeeded]; // update the UI
        }completion:^(BOOL finished) {
            // Set the text
            self.locationLabel.text = locationText;
            self.locationLabel.adjustsFontSizeToFitWidth = YES;
            [UIView animateWithDuration:.15 animations:^{
                // Animate
                self.locationLabel.alpha = 1;
            }completion:^(BOOL finished) {
                // Complete
            }];
        }];
    }
} else if ([locationText isEqual:@""])
{
    // Move it to the center
    self.ovalImageViewLeadingConstraint.constant = (([UIScreen mainScreen].bounds.size.width / 2) - 9); // Default center calculation for this image
    [self.view setNeedsUpdateConstraints];
    [self changeLabelText:self.subLocationLabel string:subLocationText]; // Update the subLocationLabel
    [UIView animateWithDuration:.15 animations:^{
        self.locationLabel.alpha = 0;
        self.ovalImageView.alpha = 1;
        [self.view layoutIfNeeded];
    }completion:^(BOOL finished) {
        // Complete
        self.locationLabel.text = @"";
    }];
}
}

Но метка остается прежней, хотя в ней записывается правильный порядок выполнения, и все выглядит хорошо. Есть идеи? Я действительно застрял..

Спасибо!

3 ответа

Я бы порекомендовал вам поместить свой код обновления пользовательского интерфейса в блок отправки, чтобы убедиться, что обновление пользовательского интерфейса будет выполнено в основном потоке.

dispatch_async(dispatch_get_main_queue(), ^{
    //... UI Update Code
});

Я думаю, что главное - это ваша самая большая проблема. Для меня код выглядит немного странно в следующих моментах:

  • вы смешиваете разные типы данных (float, CGFloat, int)

обратитесь к CocoaTouch64BitGuide, это может быть проблемой, но я думаю, что это не решит проблему с вашим макетом. Более критичны магические числа в целом.

  • Ваш код автоматического размещения может быть проблемой

Я считаю, что нет необходимости перемещать ovalImageView, манипулируя LeadingConstraint. Просто установите contentMode в центр / влево / вправо, и autolayout сделает сброс. Если вам действительно нужно изменить ограничение, сделайте это в методе [yourView updateConstraints]. Если вы хотите ширину надписей, попросите об этом: [label intrinsicContentsize] и используйте setPreferredMaxLayoutWidth, если вам это нужно. Если ваш код авторазметки правильный, никакой вид не будет отображаться "вне экрана".

  • ваша анимация может быть немного нервной

Сначала вы используете removeAllAnimations. В принципе верно, но если вы ожидаете частых обновлений, я бы порекомендовал просто изменить содержимое ярлыков / изображений во время предыдущей анимации. В противном случае ваше изображение отскочит назад и запустится новая анимация.

Вы можете попробовать это...

            static NSString *currentText;
            currentText = locationText;

            [UIView animateWithDuration:.15 animations: ^{
                // Animate
                self.locationLabel.alpha = 0;
                self.ovalImageView.alpha = 1;
                [self.view layoutIfNeeded]; // update the UI
            } completion: ^(BOOL finished) {
                // Set the text

                if (![currentText isEqualToString:locationText]) {
                    return;
                }

                self.locationLabel.text = locationText;
                self.locationLabel.adjustsFontSizeToFitWidth = YES;
                [UIView animateWithDuration:.15 animations: ^{
                    // Animate
                    self.locationLabel.alpha = 1;
                } completion: ^(BOOL finished) {
                    // Complete
                }];
            }];

Чтобы объяснить подробно,

Давайте рассмотрим сценарий выхода пользователя из региона1 и прямого входа в регион2.

Шаг 1:

Итак, событие завершается, выполняются следующие строки:

    NSLog(@"changing text from: %@, to: %@", label.text, string);
    // Only animate if the text changes
    if (![label.text isEqualToString:string])

При этом, если флажок установлен, текст метки "внутри региона" (для региона 1). Поскольку метод вызывается из метода выхода, параметр string будет иметь значение "За пределами региона". Оба значения не одинаковы, и, следовательно, контроль входит в эту область.

Шаг 2: В этот момент вы запустили анимацию продолжительностью 0,15, однако значение метки не изменится, пока анимация не будет завершена, поскольку код находится в блоке завершения.

   // Complete
    label.text = string;

Шаг 3: Событие действительно вошло в регион 2. Текст метки еще не обновлен, поэтому он возбуждает нижнюю строку

NSLog(@"changing text from: %@, to: %@", label.text, string);

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

if (![label.text isEqualToString:string])

Проверять:

  1. Передавайте настраиваемое строковое значение при вызове текста метки изменения, делая текст специфичным: "внутри области 1", "выход из области 1", "внутри области 2" и т. Д. Таким образом, значение будет другим и будет обновлено.

  2. Установите значение метки непосредственно перед анимацией.

  3. Присвойте значение свойству атомарной строки и проверьте свойство Value.

например:

@property (atomic, strong) NSString * currentLabelText;



-(void)changeLabelText:(UILabel *)label string:(NSString *)string
{
    NSLog(@"changing text from: %@, to: %@", label.text, string);
    // Only animate if the text changes

    if (![self. currentLabelText  isEqualToString:string])
    {
        self. currentLabelText = string; //Ultimately this would be our value
        [UIView animateWithDuration:.15 animations:^{

        // Animate
        label.alpha = 0;
    }completion:^(BOOL finished) {
       // Complete
    label.text = self. currentLabelText;
    [UIView animateWithDuration:.15 animations:^{
        // Animate
        label.alpha = 1;
    }completion:^(BOOL finished) {
        // Complete
    }];
}];
}
}

Или по-другому, так как это соответствует вашим требованиям.

Другие вопросы по тегам