I encountered a weird bug (or I'm really stupid...): I wanna display a custom callout in an MKAnnotationView. What I'm doing is adding a custom view as a subview of the MKAnnotationView. I'm having problems with the adding animation.
I have a MKMapView with my view controller as delegate
- (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 is a custom view controller that creates a view with bubble shape (just adding round corners and white background).
- (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);
}
}
}
The problem is: - If I select one pin (MKAnnotationView) on the map it animates correctly showing the callout bubble I created. - After that I tap a different pin (while the bubble was still showing in the last one). - Then with option 1) the callout bubble just jumps from one pin to another, without animating its scale. - With option 2) it works just fine the way it is supposed to.
I wanna understand why option 1) doesn't work. I don't think it should make any difference, since there is no animation going on when I tap the second pin.
Thanks in advance.
EDIT: Just to simplify, I've created a project with the minimum amount of code necessary to reproduce my problem:
@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
Just like before, the first time you tap one annotation, it works fine, but when you tap the second annotation the animation doesn't happen. If you uncomment the commented code, it works fine.