Как я могу создать собственную анимацию с помощью MKAnnotationView?
У меня есть экземпляр MKMapView
и хотел бы использовать пользовательские значки аннотаций вместо стандартных значков контактов, предоставленных MKPinAnnotationView. Итак, я настроил подкласс MKAnnotationView с именем CustomMapAnnotation и переопределяю -(void)drawRect:
нарисовать CGImage. Это работает.
Проблема возникает, когда я пытаюсь повторить .animatesDrop
функциональность предоставлена MKPinAnnotationView; Я хотел бы, чтобы мои значки появлялись постепенно, опускались сверху и слева направо, когда аннотации добавляются к MKMapView
пример.
Вот -(void)drawRect: для CustomMapAnnotation, который работает, когда вы просто рисуете UIImage (что и делает вторая строка):
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
[((Incident *)self.annotation).smallIcon drawInRect:rect];
if (newAnnotation) {
[self animateDrop];
newAnnotation = NO;
}
}
Проблема приходит, когда вы добавляете animateDrop
метод:
-(void)animateDrop {
CGContextRef myContext = UIGraphicsGetCurrentContext();
CGPoint finalPos = self.center;
CGPoint startPos = CGPointMake(self.center.x, self.center.y-480.0);
self.layer.position = startPos;
CABasicAnimation *theAnimation;
theAnimation=[CABasicAnimation animationWithKeyPath:@"position"];
theAnimation.fromValue=[NSValue valueWithCGPoint:startPos];
theAnimation.toValue=[NSValue valueWithCGPoint:finalPos];
theAnimation.removedOnCompletion = NO;
theAnimation.fillMode = kCAFillModeForwards;
theAnimation.delegate = self;
theAnimation.beginTime = 5.0 * (self.center.x/320.0);
theAnimation.duration = 1.0;
[self.layer addAnimation:theAnimation forKey:@""];
}
Это просто не работает, и может быть много причин, почему. Я не буду вдаваться во все из них сейчас. Главное, что я хотел бы знать, это то, что подход является правильным или я должен попробовать что-то совершенно другое.
Я также попытался упаковать все это в транзакцию анимации, чтобы параметр beginTime мог действительно работать; это, казалось, ничего не делало вообще. Я не знаю, так ли это, потому что мне не хватает какой-то ключевой точки или потому, что MapKit каким-то образом уничтожает мою анимацию.
// Does nothing
[CATransaction begin];
[map addAnnotations:list];
[CATransaction commit];
Если у кого-то есть опыт работы с анимированными аннотациями MKMap, подобными этому, я хотел бы получить несколько советов, в противном случае, если вы можете предложить совет CAAnimation по подходу, это тоже было бы здорово.
5 ответов
Одной из проблем с кодом Пола Шапиро является то, что он не работает, когда вы добавляете аннотации ниже, куда пользователь смотрит в данный момент. Эти аннотации будут плавать в воздухе перед падением, потому что они перемещаются в видимый прямоугольник карты пользователя.
Другое заключается в том, что он также опускает местоположение пользователя синей точкой. С помощью приведенного ниже кода вы обрабатываете как пользовательское местоположение, так и большое количество аннотаций карты вне экрана. Я также добавил хороший отскок;)
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
MKAnnotationView *aV;
for (aV in views) {
// Don't pin drop if annotation is user location
if ([aV.annotation isKindOfClass:[MKUserLocation class]]) {
continue;
}
// Check if current annotation is inside visible map rect, else go to next one
MKMapPoint point = MKMapPointForCoordinate(aV.annotation.coordinate);
if (!MKMapRectContainsPoint(self.mapView.visibleMapRect, point)) {
continue;
}
CGRect endFrame = aV.frame;
// Move annotation out of view
aV.frame = CGRectMake(aV.frame.origin.x, aV.frame.origin.y - self.view.frame.size.height, aV.frame.size.width, aV.frame.size.height);
// Animate drop
[UIView animateWithDuration:0.5 delay:0.04*[views indexOfObject:aV] options:UIViewAnimationCurveLinear animations:^{
aV.frame = endFrame;
// Animate squash
}completion:^(BOOL finished){
if (finished) {
[UIView animateWithDuration:0.05 animations:^{
aV.transform = CGAffineTransformMakeScale(1.0, 0.8);
}completion:^(BOOL finished){
[UIView animateWithDuration:0.1 animations:^{
aV.transform = CGAffineTransformIdentity;
}];
}];
}
}];
}
}
Во-первых, вам нужно заставить свой контроллер представления реализовать MKMapViewDelegate, если это еще не сделано.
Затем реализуйте этот метод:
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
MKAnnotationView *aV;
for (aV in views) {
CGRect endFrame = aV.frame;
aV.frame = CGRectMake(aV.frame.origin.x, aV.frame.origin.y - 230.0, aV.frame.size.width, aV.frame.size.height);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.45];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[aV setFrame:endFrame];
[UIView commitAnimations];
}
}
Добавьте свои аннотации в MapView, и когда они будут добавлены, будет вызван этот метод делегата, который будет анимировать контакты сверху вниз по мере их добавления.
Значения времени и положения можно немного изменить, но я настроил его так, чтобы он выглядел лучше / ближе к традиционному падению (насколько я тестировал). Надеюсь это поможет!
В качестве альтернативы, если вы создаете подкласс MKAnnotationView, вы можете использовать didMoveToSuperview
для запуска анимации. Следующее делает падение, которое заканчивается легким эффектом
#define kDropCompressAmount 0.1
@implementation MyAnnotationViewSubclass
...
- (void)didMoveToSuperview {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
animation.duration = 0.4;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, -400, 0)];
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"transform"];
animation2.duration = 0.10;
animation2.beginTime = animation.duration;
animation2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
animation2.toValue = [NSValue valueWithCATransform3D:CATransform3DScale(CATransform3DMakeTranslation(0, self.layer.frame.size.height*kDropCompressAmount, 0), 1.0, 1.0-kDropCompressAmount, 1.0)];
animation2.fillMode = kCAFillModeForwards;
CABasicAnimation *animation3 = [CABasicAnimation animationWithKeyPath:@"transform"];
animation3.duration = 0.15;
animation3.beginTime = animation.duration+animation2.duration;
animation3.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
animation3.toValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
animation3.fillMode = kCAFillModeForwards;
CAAnimationGroup *group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:animation, animation2, animation3, nil];
group.duration = animation.duration+animation2.duration+animation3.duration;
group.fillMode = kCAFillModeForwards;
[self.layer addAnimation:group forKey:nil];
}
Для ответа Майкла Тайсона (я пока не могу комментировать везде) я предлагаю вставить следующий код в didMoveToSuperview для правильного повторного использования MKAnnotationView, чтобы он снова выполнял анимацию и затем имитировал последовательное добавление аннотаций.
Играйте с делителями и множителями для получения разных визуальных результатов...
- (void)didMoveToSuperview {
//necessary so it doesn't add another animation when moved to superview = nil
//and to remove the previous animations if they were not finished!
if (!self.superview) {
[self.layer removeAllAnimations];
return;
}
float xOriginDivider = 20.;
float pos = 0;
UIView *mySuperview = self.superview;
while (mySuperview && ![mySuperview isKindOfClass:[MKMapView class]])
mySuperview = mySuperview.superview;
if ([mySuperview isKindOfClass:[MKMapView class]])
//given the position in the array
// pos = [((MKMapView *) mySuperview).annotations indexOfObject:self.annotation];
// left to right sequence;
pos = [((MKMapView *) mySuperview) convertCoordinate:self.annotation.coordinate toPointToView:mySuperview].x / xOriginDivider;
float yOffsetMultiplier = 20.;
float timeOffsetMultiplier = 0.05;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
animation.duration = 0.4 + timeOffsetMultiplier * pos;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, -400 - yOffsetMultiplier * pos, 0)];
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
// rest of animation group...
}
Я думаю, что лучший вариант - установить для animatesDrop значение YES. Это свойство MKPinAnnotationView.