How do I animate MKAnnotationView drop?

前端 未结 7 1155
情书的邮戳
情书的邮戳 2020-11-29 01:48

I have a custom MKAnnotationView where I set my image myself in viewForAnnotation. How do I animate it\'s drop like I can with MKPinAnnotationView?

My code is

相关标签:
7条回答
  • 2020-11-29 02:27

    @MrAlek's Answer for 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)
                    })
    
            })
        }
    }
    
    0 讨论(0)
  • 2020-11-29 02:29

    @MrAlek's answer in 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)
                    })
    
            })
        }
    }
    
    0 讨论(0)
  • 2020-11-29 02:35

    Implement the didAddAnnotationViews delegate method and do the animation yourself:

    - (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; }];
        }
    }
    
    0 讨论(0)
  • 2020-11-29 02:39

    @mrAlek answer in 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)
                            })
    
                        })
        }
    }
    
    0 讨论(0)
  • 2020-11-29 02:40

    One problem with the code above by Anna Karenina is that it doesn't deal with when you add annotations below where the user is looking at the moment. Those annotations will float in mid-air before dropping because they are moved into the user's visible map rect.

    Another is that it also drops the user location blue dot. With this code below, you handle both user location and large amounts of map annotations off-screen. I've also added a nice bounce ;)

    - (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;
                            }];
                        }
                    }];
                }
            }];
        }
    }
    
    0 讨论(0)
  • 2020-11-29 02:47

    Rather than implementing mapView(_:didAdd:) in the MKMapViewDelegate, you can also have the annotation view do the animation itself.

    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
        }
    }
    

    This is useful for avoiding the cluttering of one’s view controller with annotation view animations. E.g. in iOS 11 and later, you might do:

    mapView.register(CustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
    

    And then you can add an annotation to your map view, and you get this animation of the annotation view without any more code in the view controller.

    This particular animation is a drop with a little bounce at the end, but obviously you can do whatever animation you want.

    The above was written in Swift, but the concept is equally valid in Objective-C, too.

    0 讨论(0)
提交回复
热议问题