如何为 MKAnnotationView 放置动画?
Posted
技术标签:
【中文标题】如何为 MKAnnotationView 放置动画?【英文标题】:How do I animate MKAnnotationView drop? 【发布时间】:2011-10-12 03:21:15 【问题描述】:我有一个自定义的 MKAnnotationView,我自己在 viewForAnnotation 中设置了我的图像。如何像使用 MKPinAnnotationView 一样为它的下降设置动画?
我的代码是
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation
static NSString *AnnotationViewID = @"annotationViewID";
MKAnnotationView *annotationView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationViewID];
if (annotationView == nil)
annotationView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationViewID] autorelease];
annotationView.image = [UIImage imageNamed:@"blah.png"];
annotationView.annotation = annotation;
return annotationView;
【问题讨论】:
这不是放置动画,只是一个不错的动画***.com/a/49537671/8334818 【参考方案1】:Anna Karenina 上面代码的一个问题是,当您在用户正在查看的下方添加注释时,它无法处理。这些注释会在掉落之前漂浮在半空中,因为它们已移动到用户可见的地图矩形中。
另一个是它还会丢弃用户位置蓝点。使用下面的代码,您可以在屏幕外处理用户位置和大量地图注释。我还添加了一个不错的反弹;)
- (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: UIViewAnimationOptionCurveLinear 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)
if (finished)
[UIView animateWithDuration:0.1 animations:^
aV.transform = CGAffineTransformIdentity;
];
];
];
【讨论】:
感谢@MrAlek,动画效果很好。【参考方案2】:实现didAddAnnotationViews
委托方法并自己做动画:
- (void)mapView:(MKMapView *)mapView
didAddAnnotationViews:(NSArray *)annotationViews
for (MKAnnotationView *annView in annotationViews)
CGRect endFrame = annView.frame;
annView.frame = CGRectOffset(endFrame, 0, -500);
[UIView animateWithDuration:0.5
animations:^ annView.frame = endFrame; ];
【讨论】:
谢谢,成功了!我怎样才能让它反弹而不是从顶部掉下来? 您可以将所有 UIView 动画技术与注释视图(它是 UIView 的子类)一起使用。上面的例子只是改变了 y 坐标,但你可以做其他的变换。搜索诸如“uiview 弹跳动画”之类的示例。这是一个:***.com/questions/2160150/mimic-uialertview-bounce/…。您需要添加 QuartzCore 框架,将其导入,然后将“view”更改为“annView”。 @AnnaKarenina 但是我们如何添加 CustomView 作为我们的 pinView 和 customCallOut 任何引用。【参考方案3】:@MrAlek 对 swift3
的回答optional func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView])
print(#function)
var i = -1;
for view in views
i += 1;
if view.annotation is MKUserLocation
continue;
// Check if current annotation is inside visible map rect, else go to next one
let point:MKMapPoint = MKMapPointForCoordinate(view.annotation!.coordinate);
if (!MKMapRectContainsPoint(self.mapView.visibleMapRect, point))
continue;
let endFrame:CGRect = view.frame;
// Move annotation out of view
view.frame = CGRect(origin: CGPoint(x: view.frame.origin.x,y :view.frame.origin.y-self.view.frame.size.height), size: CGSize(width: view.frame.size.width, height: view.frame.size.height))
// Animate drop
let delay = 0.03 * Double(i)
UIView.animate(withDuration: 0.5, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations:() in
view.frame = endFrame
// Animate squash
, completion:(Bool) in
UIView.animate(withDuration: 0.05, delay: 0.0, options: UIViewAnimationOptions.curveEaseInOut, animations:() in
view.transform = CGAffineTransform(scaleX: 1.0, y: 0.6)
, completion: (Bool) in
UIView.animate(withDuration: 0.3, delay: 0.0, options: UIViewAnimationOptions.curveEaseInOut, animations:() in
view.transform = CGAffineTransform.identity
, completion: nil)
)
)
【讨论】:
【参考方案4】:@mrAlek 用 Swift 回答:
func mapView(mapView: MKMapView!, didAddAnnotationViews views: [AnyObject]!)
println("didAddAnnotationViews()")
var i = -1;
for view in views
i++;
let mkView = view as! MKAnnotationView
if view.annotation is MKUserLocation
continue;
// Check if current annotation is inside visible map rect, else go to next one
let point:MKMapPoint = MKMapPointForCoordinate(mkView.annotation.coordinate);
if (!MKMapRectContainsPoint(self.mapView.visibleMapRect, point))
continue;
let endFrame:CGRect = mkView.frame;
// Move annotation out of view
mkView.frame = CGRectMake(mkView.frame.origin.x, mkView.frame.origin.y - self.view.frame.size.height, mkView.frame.size.width, mkView.frame.size.height);
// Animate drop
let delay = 0.03 * Double(i)
UIView.animateWithDuration(0.5, delay: delay, options: UIViewAnimationOptions.CurveEaseIn, animations:() in
mkView.frame = endFrame
// Animate squash
, completion:(Bool) in
UIView.animateWithDuration(0.05, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations:() in
mkView.transform = CGAffineTransformMakeScale(1.0, 0.6)
, completion: (Bool) in
UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations:() in
mkView.transform = CGAffineTransformIdentity
, completion: nil)
)
)
【讨论】:
【参考方案5】:@MrAlek 在 Swift 2 中的回答:
func mapView(mapView: MKMapView, didAddAnnotationViews views: [MKAnnotationView])
print(__FUNCTION__)
var i = -1;
for view in views
i++;
if view.annotation is MKUserLocation
continue;
// Check if current annotation is inside visible map rect, else go to next one
let point:MKMapPoint = MKMapPointForCoordinate(view.annotation!.coordinate);
if (!MKMapRectContainsPoint(self.mapView.visibleMapRect, point))
continue;
let endFrame:CGRect = view.frame;
// Move annotation out of view
view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y - self.view.frame.size.height, view.frame.size.width, view.frame.size.height);
// Animate drop
let delay = 0.03 * Double(i)
UIView.animateWithDuration(0.5, delay: delay, options: UIViewAnimationOptions.CurveEaseIn, animations:() in
view.frame = endFrame
// Animate squash
, completion:(Bool) in
UIView.animateWithDuration(0.05, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations:() in
view.transform = CGAffineTransformMakeScale(1.0, 0.6)
, completion: (Bool) in
UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations:() in
view.transform = CGAffineTransformIdentity
, completion: nil)
)
)
【讨论】:
这太棒了!非常感谢;)【参考方案6】:为 Swift 4.2 更新
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView])
var i = -1;
for view in views
i += 1;
if view.annotation is MKUserLocation
continue;
let point:MKMapPoint = MKMapPoint(view.annotation!.coordinate);
if (!self.mapView.visibleMapRect.contains(point))
continue;
let endFrame:CGRect = view.frame;
view.frame = CGRect(origin: CGPoint(x: view.frame.origin.x,y :view.frame.origin.y-self.view.frame.size.height), size: CGSize(width: view.frame.size.width, height: view.frame.size.height))
let delay = 0.03 * Double(i)
UIView.animate(withDuration: 0.5, delay: delay, options: UIView.AnimationOptions.curveEaseIn, animations:() in
view.frame = endFrame
, completion:(Bool) in
UIView.animate(withDuration: 0.05, delay: 0.0, options: UIView.AnimationOptions.curveEaseInOut, animations:() in
view.transform = CGAffineTransform(scaleX: 1.0, y: 0.6)
, completion: (Bool) in
UIView.animate(withDuration: 0.3, delay: 0.0, options: UIView.AnimationOptions.curveEaseInOut, animations:() in
view.transform = CGAffineTransform.identity
, completion: nil)
)
)
【讨论】:
【参考方案7】:除了在MKMapViewDelegate
中实现mapView(_:didAdd:)
,您还可以让注释视图自己做动画。
class CustomAnnotationView: MKAnnotationView
override var annotation: MKAnnotation? didSet update(for: annotation)
override init(annotation: MKAnnotation?, reuseIdentifier: String?)
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
update(for: annotation)
override func prepareForReuse()
super.prepareForReuse()
removeFromSuperview()
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
override func didMoveToSuperview()
super.didMoveToSuperview()
transform = CGAffineTransform(translationX: 0, y: -100)
alpha = 0
UIViewPropertyAnimator(duration: 0.5, dampingRatio: 0.4)
self.transform = .identity
self.alpha = 1
.startAnimation()
private func update(for annotation: MKAnnotation?)
// do whatever update to the annotation view you want, if any
这对于避免视图控制器与注释视图动画的混乱很有用。例如。在 ios 11 及更高版本中,您可能会这样做:
mapView.register(CustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
然后你可以在你的地图视图中添加一个注解,你可以得到这个注解视图的动画,而无需在视图控制器中添加任何代码。
这个特殊的动画是一个最后有一点反弹的下降,但显然你可以做任何你想做的动画。
上面是用 Swift 编写的,但这个概念在 Objective-C 中也同样有效。
【讨论】:
以上是关于如何为 MKAnnotationView 放置动画?的主要内容,如果未能解决你的问题,请参考以下文章
如何为 JQuery addClass/removeClass 函数设置动画?
我可以重新定位已经放置在 MKMapView 上的 MKAnnotationView 吗?