ios - 在 UIView 动画 block 中调用 layoutIfNeeded 会导致 subview 更早移动

标签 ios objective-c animation uiview cgaffinetransformscale

我正在尝试创建自定义标注气泡。 我有一个动画 block 设置气泡 UIView 的比例变换:

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

我要么像 (1) 中那样以比例 (0, 0) 开始气泡 View ,然后在动画 block 内像 (2) 中那样对 Identity 进行动画处理,或者相反,从 (2) 到 (1)。

如果我的动画 block 中没有 [self.view layoutIfNeeded];,从 (1) 到 (2) 可以正常工作,如下所示:

enter image description here

但是当没有 [self.view layoutIfNeeded]; 从 (2) 返回到 (1) 时, subview 在动画完成之前跳到左边:

enter image description here

现在,如果我确实添加了 [self.view layoutIfNeeded];, subview 会随着气泡 View 而动画化,但会有一些延迟:

从 (1) 到 (2):

enter image description here

或者从(2)到(1):

enter image description here

我已经尝试用中心约束替换所有顶部和前导 subview 的约束,如 How do I adjust the anchor point of a CALayer, when Auto Layout is being used?并且还尝试了层变换解决方案(但是这个解决方案打破了布局约束,说它不能满足所有约束)。

关于如何解决我的动画问题有什么想法吗?

提前致谢。

更新: 我正在用我调用的实际方法更新问题

- (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);
        }
    }
}

最佳答案

这可能会有所帮助。根据 Apple 文档 -layoutIfNeeded 强制提前布局

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

所以,你可以试试setNeedsLayout。这应该满足您提到的第一种情况。

关于ios - 在 UIView 动画 block 中调用 layoutIfNeeded 会导致 subview 更早移动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22601541/

相关文章:

ios - 动画 block 在更新文本后重置为原始位置

ios - Facebook SDK 3.1 : Feed Dialog vs Native dialog

objective-c - Testflight : The APS environment in your embedded. mobileprovision 上的无效 IPA 错误与您的二进制文件不匹配

java - 动态壁纸,帧动画,滞后

html - 多行的动画文本下划线(从左到右绘制动画)

iphone - 如何验证实体的 NSMutableArray 包含具有特定属性的对象

iOS 7 UINavigationBar Retina 图像大小

ios - NSDateComponents 在 2012 年第一周表现异常

objective-c - UIResponder 类和第一响应者角色

objective-c - 根据 UISwitch 的状态移除或添加 UITableViewCell