TCCMapTileAnimation
一个用于从瓦片图创建动画地图覆盖层的 iOS 库。
特性
- 使用来自本地文件系统或地图瓦片服务器的地图瓦片,在
MKMapView
上创建一个动画覆盖层,其中动画的每一帧由一组瓦片组成。 - 允许用户播放、暂停以及在动画时间轴上跳转/快进。
- 动画覆盖层流畅且一致。根据设备的图形性能,绘制动画帧时可能会有很少或没有瓦片或闪烁。
- 当用户在地图上平移时,覆盖层将回退到按需检索和渲染瓦片。
TCCMapTileAnimation 不提供任何 UI 播放控制,但请查看示例项目,了解如何将 UI 控件连接到覆盖层。
入门
安装
如果您使用 CocoaPods,请将 pod 'TCCMapTileAnimation'
添加到您的 Podfile,然后运行 pod install
。
您也可以将 TCCMapTileAnimation
目录中的 .m
和 .h
文件手动添加到您的项目中,或者从这些文件创建一个静态库并添加到您的项目中。
必备条件
TCCMapTileAnimation 使用 NSURLCache 来快速查找和渲染已经从网络中获取的覆盖图块。这必须由您的应用程序明确设置,最好在 application:didFinishLaunchingWithOptions
中进行。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
diskCapacity:20 * 1024 * 1024
diskPath:nil];
[NSURLCache setSharedURLCache:cache];
...
}
创建图层
NSArray *templateURLs = @[@"http://url.to/first_frame/{z}/{x}/{y}", @"http://url.to/second_frame/{z}/{x}/{y}"];
self.overlay = [[TCCAnimationTileOverlay alloc] initWithMapView:self.mapView templateURLs:templateURLs frameDuration:0.50 minimumZ:3 maximumZ:9 tileSize:CGSizeMake(256, 256)];
self.overlay.delegate = self;
[self.mapView addOverlay:self.overlay];
创建图层时设置 URL 会话配置
NSURLSessionConfiguration * configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSArray *templateURLs = @[@"http://url.to/first_frame/{z}/{x}/{y}", @"http://url.to/second_frame/{z}/{x}/{y}"];
self.overlay = [[TCCAnimationTileOverlay alloc] initWithTemplateURLs: templateURLs, configuration: configuration, frameDuration:0.50 minimumZ:3 maximumZ:9 tileSize:CGSizeMake(256, 256)];
self.overlay.delegate = self;
[self.mapView addOverlay:self.overlay];
创建图层渲染器
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
if ([overlay isKindOfClass:[TCCAnimationTileOverlay class]]) {
self.animatedTileRenderer = [[TCCAnimationTileOverlayRenderer alloc] initWithOverlay:overlay];
// Optional - draws tile grid debug info
// self.animatedTileRenderer.drawDebugInfo = YES;
self.animatedTileRenderer.alpha = .75;
return self.animatedTileRenderer;
}
return nil;
}
获取图块并开始动画
[self.overlay fetchTilesForMapRect:self.mapView.visibleMapRect zoomLevel:self.animatedTileRenderer.renderedTileZoomLevel progressHandler:^(NSUInteger currentTimeIndex) {
// Show loading progress
[self.downloadProgressView setProgress:(CGFloat)currentTimeIndex / self.animatedTileOverlay.numberOfAnimationFrames animated:YES];
} completionHandler:^(BOOL success, NSError *error) {
if (success) {
[self.animatedTileOverlay startAnimating];
return;
}
}];
渲染动画
// When the overlay has started animating, it calls this delegate method when each frame of animation ticks.
// It is its responsibility to tell the overlay renderer to redraw for each frame.
- (void)animationTileOverlay:(TCCAnimationTileOverlay *)animationTileOverlay didAnimateWithAnimationFrameIndex:(NSInteger)animationFrameIndex
{
[self.animatedTileRenderer setNeedsDisplayInMapRect:self.mapView.visibleMapRect];
...
}
停止动画
[self.overlay pauseAnimating];
动画帧间擦除
// This action is connected to the "Value Changed" event for the playback slider
- (IBAction)onSliderValueChange:(id)sender
{
// Only advance the animated overlay to the next frame if the slider no longer matches the current frame index
NSInteger sliderVal = floor(self.timeSlider.value);
if (sliderVal == self.animatedTileOverlay.currentFrameIndex) return;
[self.animatedTileOverlay moveToFrameIndex:frameIndex isContinuouslyMoving:YES];
}
// This action is connected to "Touch Up Inside" and "Touch Up Outside" for the playback slider
- (IBAction)finishedSliding:(id)sender
{
NSInteger sliderVal = floor(self.timeSlider.value);
[self.animatedTileOverlay moveToFrameIndex:frameIndex isContinuouslyMoving:NO];
}
地图上对用户输入做出反应
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
// When the user moves/zooms/rotates the map, it should pause loading or animating, since
// otherwise we might not have fetched the tile data necessary to display the overlay
// for the new region.
if (self.animatedTileOverlay.currentAnimationState == TCCAnimationStateAnimating ||
self.animatedTileOverlay.currentAnimationState == TCCAnimationStateLoading) {
[self.animatedTileOverlay pauseAnimating];
}
}
查看演示项目 MapTileAnimationDemo
,以深入了解如何在项目中整合 TCCMapTileAnimation。
技术细节
- 它依赖于缓存的HTTP网络响应(来自NSURLCache)来在动画期间提供良好的性能。