Вызов layoutIfNeeded внутри блока анимации UIView приводит к тому, что подпредставления перемещаются раньше

Я пытаюсь создать собственный пузырь выноски. У меня есть блок анимации, устанавливающий масштабное преобразование пузырька UIView:

self.view.transform = CGAffineTransformMakeScale(0, 0); // (1)
self.view.transform = CGAffineTransformIdentity; // (2)

Я либо запускаю пузырьковое представление с масштабом (0, 0), как в (1), и анимирую в Identity, как в (2) внутри блока анимации, или наоборот, переходя от (2) к (1).

Если у меня нет [self.view layoutIfNeeded]; в моем анимационном блоке переход от (1) к (2) работает нормально, как в:

Но при переходе от (2) к (1) без [self.view layoutIfNeeded];, подпредставления переходят влево до завершения анимации:

Теперь, если я добавлю [self.view layoutIfNeeded]; подпредставления анимируются в виде пузыря, но с некоторой задержкой:

Либо переходя от (1) к (2):

Или из (2) в (1):

Я уже пытался заменить все верхние и ведущие ограничения подпредставления центральными ограничениями, как в разделе Как настроить точку привязки CALayer, когда используется Auto Layout? и также попробовал решение преобразования слоя (но это нарушает ограничения макета, говоря, что это не может удовлетворить все ограничения).

Есть идеи, как решить мою проблему с анимацией?

Заранее спасибо.

ОБНОВЛЕНИЕ: я обновляю вопрос фактическим методом, который я вызываю

- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView

а также

- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)annotationView

представить и отклонить мой пользовательский пузырь выноски (на данный момент забудьте о расширенном состоянии).

- (void)setCalloutState:(CalloutState)calloutState
               animated:(BOOL)animated
         annotationView:(MKAnnotationView *)annotationView
             completion:(void (^)(BOOL finished))completion
{
    if (self.view.superview == annotationView || !annotationView) {

    } else {
//        [self.view.layer removeAllAnimations];
        [self.view removeFromSuperview];

        self.view.transform = CGAffineTransformIdentity;
        self.view.bounds = CGRectMake(0, 0, normalViewWidth, normalViewHeight);
        self.view.transform = CGAffineTransformMakeScale(0, 0);
        self.view.center = CGPointMake(annotationView.bounds.size.width / 2, 0);

        [annotationView addSubview:self.view];
    }

    void (^animationBlock)(void) = ^{
        self.view.transform = CGAffineTransformIdentity;

        switch (calloutState) {
            case CalloutStateHidden:
                self.view.bounds = CGRectMake(0, 0, normalViewWidth, normalViewHeight);
                self.view.transform = CGAffineTransformMakeScale(0, 0);
                break;
            case CalloutStateNormal:
                self.view.bounds = CGRectMake(0, 0, normalViewWidth, normalViewHeight);
                break;
            case CalloutStateExpanded:
                self.view.bounds = CGRectMake(0, 0, expandedViewWidth, expandedViewHeight);
                break;
            default:
                break;
        }

        self.view.center = CGPointMake(annotationView.bounds.size.width / 2, 0);

        [self.view layoutIfNeeded];
    };

    if (animated) {
        // TODO: figure out why the first animateWithDuration is needed in this nested thing
        [UIView animateWithDuration:0
                              delay:0
                            options:UIViewAnimationOptionBeginFromCurrentState
                         animations:NULL
                         completion:^(BOOL finished) {

                             [UIView animateWithDuration:1.3
                                                   delay:0
                                                 options:UIViewAnimationOptionBeginFromCurrentState
                                              animations:animationBlock
                                              completion:^(BOOL finished) {
                                                  if (finished) {
                                                      self.calloutState = calloutState;

                                                      // TODO: figure out how to end UIView animation instantly so we don't need the second condition at the if
                                                      // having a concurrency problem here
                                                      if (calloutState == CalloutStateHidden && self.view.superview == annotationView) {
                                                          [self.view removeFromSuperview];
                                                      }

                                                      self.editingEnabled = calloutState == CalloutStateExpanded;
                                                  }

                                                  if (completion) {
                                                      completion(finished);
                                                  }
                                              }];

                         }];
        // ---------------------------------------------------------------------------------
    } else {
        animationBlock();

        self.calloutState = calloutState;
        if (calloutState == CalloutStateHidden) {
            [self.view removeFromSuperview];
        }

        self.editingEnabled = calloutState == CalloutStateExpanded;

        if (completion) {
            completion(YES);
        }
    }
}

1 ответ

Это может помочь. В соответствии с документацией Apple -layoutIfNeeded заставляет макет рано

- (void)setNeedsLayout;
- (void)layoutIfNeeded;

Итак, вы можете попробовать setNeedsLayout. Это должно удовлетворить 1-й случай, который вы упомянули.

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