iphone - 创建像用户位置蓝色大理石水滴这样的动画

Posted

技术标签:

【中文标题】iphone - 创建像用户位置蓝色大理石水滴这样的动画【英文标题】:iphone - create animation like user location Blue marble drop 【发布时间】:2011-08-22 16:21:17 【问题描述】:

你知道如何在 MKMapView 中创建像 Blue Marble drop User-Location 这样的动画吗?

【问题讨论】:

您是要制作类似的动画,还是要在地图视图中显示标准位置指示器? 我喜欢制作类似的动画,谢谢。 您在帖子中找到的答案 [在此处输入链接描述][1] [1]:***.com/questions/1437568/… 【参考方案1】:

虽然我不确定 Apple 是如何实现这种效果的具体细节,但我觉得这是一个使用 CoreAnimation 和自定义动画属性的绝佳机会。 This post 提供了有关该主题的一些不错的背景。我假设您所指的“Blue Marble drop”动画是以下序列:

    淡蓝色大圆圈放大到框架中 大的浅蓝色圆圈在两个相对较大的半径之间摆动,因为位置是 计算出来的 浅蓝色大圆圈放大用户位置上的深蓝色小圆圈

虽然这可能会稍微简化流程,但我认为这是一个很好的起点,可以相对轻松地添加更复杂/详细的功能(即随着较大圆圈会聚,小黑圈会脉动。)

我们需要的第一件事是一个自定义的 CALayer 子类,它为我们的外部大浅蓝色圆圈半径提供一个自定义属性:

#import <QuartzCore/QuartzCore.h>

@interface CustomLayer : CALayer
@property (nonatomic, assign) CGFloat circleRadius;
@end

和实施:

#import "CustomLayer.h"

@implementation CustomLayer
@dynamic circleRadius; // Linked post tells us to let CA implement our accessors for us.
                       // Whether this is necessary or not is unclear to me and one 
                       // commenter on the linked post claims success only when using
                       // @synthesize for the animatable property.

+ (BOOL)needsDisplayForKey:(NSString*)key 
    // Let our layer know it has to redraw when circleRadius is changed
    if ([key isEqualToString:@"circleRadius"]) 
        return YES;
     else 
        return [super needsDisplayForKey:key];
    


- (void)drawInContext:(CGContextRef)ctx 

    // This call is probably unnecessary as super's implementation does nothing
    [super drawInContext:ctx];

    CGRect rect = CGContextGetClipBoundingBox(ctx);

    // Fill the circle with a light blue
    CGContextSetRGBFillColor(ctx, 0, 0, 255, 0.1);
    // Stoke a dark blue border
    CGContextSetRGBStrokeColor(ctx, 0, 0, 255, 0.5);

    // Construct a CGMutablePath to draw the light blue circle
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddArc(path, NULL, rect.size.width / 2, 
                             rect.size.height / 2, 
                             self.circleRadius, 0, 2 * M_PI, NO);
    // Fill the circle
    CGContextAddPath(ctx, path);
    CGContextFillPath(ctx);

    // Stroke the circle's border
    CGContextAddPath(ctx, path);
    CGContextStrokePath(ctx);

    // Release the path
    CGPathRelease(path);

    // Set a dark blue color for the small inner circle
    CGContextSetRGBFillColor(ctx, 0, 0, 255, 1.0f);

    // Draw the center dot
    CGContextBeginPath (ctx);
    CGContextAddArc(ctx, rect.size.width / 2, 
                         rect.size.height / 2, 
                         5, 0, 2 * M_PI, NO);
    CGContextFillPath(ctx);
    CGContextStrokePath(ctx);



@end

有了这个基础设施,我们现在可以轻松地为外圆的半径设置动画,因为 CoreAnimation 会处理值插值以及重绘调用。我们所要做的就是为图层添加动画。作为一个简单的概念证明,我选择了一个简单的CAKeyframeAnimation 来遍历 3 阶段动画:

// In some controller class...
- (void)addLayerAndAnimate 

    CustomLayer *customLayer = [[CustomLayer alloc] init];

    // Make layer big enough for the initial radius
    // EDIT: You may want to shrink the layer when it reacehes it's final size
    [customLayer setFrame:CGRectMake(0, 0, 205, 205)];
    [self.view.layer addSublayer:customLayer];


    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"circleRadius"];

    // Zoom in, oscillate a couple times, zoom in further
    animation.values = [NSArray arrayWithObjects:[NSNumber numberWithFloat:100], 
                                                 [NSNumber numberWithFloat:45], 
                                                 [NSNumber numberWithFloat:50], 
                                                 [NSNumber numberWithFloat:45], 
                                                 [NSNumber numberWithFloat:50], 
                                                 [NSNumber numberWithFloat:45], 
                                                 [NSNumber numberWithFloat:20], 
                                                  nil];
    // We want the radii to be 20 in the end
    customLayer.circleRadius = 20;

    // Rather arbitrary values.  I thought the cubic pacing w/ a 2.5 second pacing
    // looked decent enough but you'd probably want to play with them to get a more
    // accurate imitation of the Maps app.  You could also define a keyTimes array for 
    // a more discrete control of the times per step.
    animation.duration = 2.5;
    animation.calculationMode = kCAAnimationCubicPaced;

    [customLayer addAnimation:animation forKey:nil];


以上是一个相当“hacky”的概念证明,因为我不确定您打算使用这种效果的具体方式。例如,如果您想在数据准备好之前让圆振荡,那么上面的方法就没有多大意义,因为它总是会振荡两次。

一些结束语:

再说一次,我不确定您对这种效果的意图。如果,对于 例如,您将其添加到MKMapView,以上可能需要 一些调整以与 MapKit 集成。 链接的帖子表明上述方法需要 ios 3.0+ 和 OS X 10.6+ 中的 CoreAnimation 版本 谈到链接的帖子(就像我经常做的那样),非常感谢 Ole Begemann,他写了 it 并出色地解释了 CoreAnimation 中的自定义属性。

编辑:另外,出于性能原因,您可能希望确保该层仅与需要的一样大。也就是说,在您从较大尺寸完成动画后,您可能希望缩小尺寸,以便您只使用/绘制尽可能多的空间。一个很好的方法是找到一种方法动画bounds(而不是circleRadius)并根据大小插值执行此动画,但我在实现它时遇到了一些麻烦(也许有人可以添加一些见解关于那个主题)。

希望这会有所帮助, 山姆

【讨论】:

中间的线条和蓝色圆圈看起来像素化、锯齿状......我能做些什么来解决这个问题,让它变得平滑? 想通了.. customLayer.contentsScale = [UIScreen mainScreen].scale;【参考方案2】:

将此添加到您的地图对象:

myMap.showsUserLocation = TRUE;

【讨论】:

以上是关于iphone - 创建像用户位置蓝色大理石水滴这样的动画的主要内容,如果未能解决你的问题,请参考以下文章

创建按钮边缘的“地图”

显示当前位置,以及 iphone 中的另一个位置

带光环的 iPhone MKMapView 引脚

如何创建像谷歌位置历史这样的安卓时间线视图?

如何在 iPhone 中使用 UIBezierPath 创建像 UIPopover 这样的视图?

查找当前位置时出现蓝点和圆圈