【问题标题】:UIView animation not working without UIViewAnimationOptionBeginFromCurrentState没有 UIViewAnimationOptionBeginFromCurrentState 的 UIView 动画不起作用
【发布时间】:2014-03-20 19:39:35
【问题描述】:

我遇到了一个奇怪的错误(或者我真的很愚蠢......): 我想在 MKAnnotationView 中显示自定义标注。 我正在做的是添加一个自定义视图作为 MKAnnotationView 的子视图。 我在添加动画时遇到问题。

我有一个 MKMapView,我的视图控制器作为委托

- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView
{
    [self.calloutViewController setCalloutState:CalloutStateNormal
                                       animated:YES
                                 annotationView:annotationView
                                     completion:NULL];
}

- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)annotationView
{
    [self.calloutViewController setCalloutState:CalloutStateHidden
                                       animated:YES
                                 annotationView:annotationView
                                     completion:NULL];
}

self.calloutViewController 是一个自定义视图控制器,它创建一个气泡形状的视图(只是添加圆角和白色背景)。

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

问题是: - 如果我在地图上选择一个图钉 (MKAnnotationView),它会正确显示我创建的标注气泡。 - 之后,我点击了一个不同的图钉(而气泡仍然显示在最后一个图钉中)。 - 然后使用选项 1) 标注气泡只是从一个引脚跳到另一个引脚,而不会为其缩放设置动画。 - 使用选项 2) 它可以正常工作。

我想了解为什么选项 1) 不起作用。 我不认为它应该有任何区别,因为当我点击第二个图钉时没有动画发生。

提前致谢。

编辑: 为简化起见,我创建了一个项目,其中包含重现我的问题所需的最少代码量:

@interface TestViewController () <MKMapViewDelegate>

@property (weak, nonatomic) IBOutlet MKMapView *mapView;
@property (strong, nonatomic) UIView *customCallout;

@end

@implementation TestViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.customCallout = [[UIView alloc] initWithFrame:CGRectMake(-50, -50, 100, 50)];
    self.customCallout.backgroundColor = [UIColor colorWithWhite:1 alpha:0.7];

    self.mapView.delegate = self;

    CustomAnnotation *annoation1 = [CustomAnnotation new];
    annoation1.coordinate = CLLocationCoordinate2DMake(40, -100);
    [self.mapView addAnnotation:annoation1];

    CustomAnnotation *annoation2 = [CustomAnnotation new];
    annoation2.coordinate = CLLocationCoordinate2DMake(40, -90);
    [self.mapView addAnnotation:annoation2];
}

- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
    [self.customCallout removeFromSuperview];
    self.customCallout.transform = CGAffineTransformMakeScale(0, 0);
    [view addSubview:self.customCallout];

//    [UIView animateWithDuration:0
//                          delay:0
//                        options:UIViewAnimationOptionBeginFromCurrentState
//                     animations:NULL
//                     completion:^(BOOL finished) {
                         [UIView animateWithDuration:1
                                               delay:0
                                             options:UIViewAnimationOptionBeginFromCurrentState
                                          animations:^{
                                              self.customCallout.transform = CGAffineTransformIdentity;
                                          }
                                          completion:NULL];
//                     }];
}

@end

就像以前一样,第一次点击一个注释时,它可以正常工作,但是当您点击第二个注释时,动画不会发生。如果您取消注释注释的代码,它可以正常工作。

【问题讨论】:

    标签: ios iphone objective-c animation uiview


    【解决方案1】:

    UIViewAnimationOptionBeginFromCurrentState 从当前状态在屏幕上绘制开始动画,而不是从属性的当前值开始。因此,即使视图的transform 属性的值为Scale(0, 0),它还没有将其绘制到屏幕上。额外的动画块产生影响的原因是,即使持续时间为 0,完成也不会立即执行,而是在下一次通过运行循环时执行,这给了视图足够的时间来绘制自己。

    如果您删除 UIViewAnimationOptionBeginFromCurrentState 选项,动画将按预期工作:

    - (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
    {
        [self.customCallout removeFromSuperview];
        self.customCallout.transform = CGAffineTransformMakeScale(0, 0);
        [view addSubview:self.customCallout];
        [UIView animateWithDuration:1 animations:^{
            self.customCallout.transform = CGAffineTransformIdentity;
        }];
    }
    

    【讨论】:

    • 但是我需要 UIViewAnimationOptionBeginFromCurrentState 以防其他动画已经发生,例如,选择一个引脚,取消选择,然后再次选择同一个引脚。
    • 我实际上只 removeFromSuperview 并将转换设置为 Scale(0, 0) 如果 customCallout 正在更改引脚,在这种情况下,我想立即将其从所选引脚中删除,并显示它正在选择的新图钉的动画。
    • 顺便说一句,谢谢你的解释,我想我明白了原因。无论如何让变换 Scale(0, 0) 在屏幕上绘制自己?我的意思是,反映 UIViewAnimationOptionBeginFromCurrentState ?
    • 我刚试了你说的没用=/。公平地说,我确实在我的实际代码中改变了框架(和中心),而不仅仅是比例,它以一种奇怪的方式动画,而不是来自我在动画之前设置的属性。
    • 我正在使用您的简化示例,并且它有效。设置转换后,frame 的值未定义,因此设置它可能无法按预期工作。尝试设置boundscenter
    猜你喜欢
    • 2011-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多