使用MapKit叠加图层(raywenderlich翻译)(下)
2013-03-12 15:18
218 查看
如果你喜欢的话那就在地图上放置一个Pin — 注解
如果你用Maps程序搜索过位置信息,那么你肯定看到过在地图上出现的许多Pin。这可以理解为注解(annotation),它是用 MKAnnotationView创建的。你也可以在你的程序中使用注解 — 并使用你想要的任何图片,不仅仅是pin!
在程序中使用注解来标出具体的某个景点,这对游客来说非常有用。注解对象的使用方法跟MKOverlay 和 MKOverlayView非常类似, 只不过需要使用的类是MKAnnotation 和 MKAnnotationView。
在Annotations群组中创建一个名为 PVAttractionAnnotation 新的类,并继承自 NSObject。然后用下面的代码替换 PVAttractionAnnotation.h 文件中的内容:
#import
#import
typedef NS_ENUM(NSInteger, PVAttractionType) {
PVAttractionDefault = 0,
PVAttractionRide,
PVAttractionFood,
PVAttractionFirstAid
};
@interface PVAttractionAnnotation : NSObject
@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *subtitle;
@property (nonatomic) PVAttractionType type;
@end
上面的代码中,首先是import MapKit,然后为PVAttractionType.定义了一个枚举。这个枚举列出了注解的类型: 游乐设施,食物,急救和默认。
接着让这个类遵循 MKAnnotation Protocol。 跟MKOverlay类似, MKAnnotation 有一个 required coordinate 属性. 最后是定义了一些属性。OK, 下面我们来看看PVAttractionAnnotation的实现。
将 PVAttractionAnnotation.m 按照如下修改:
#import
#import
@interface PVAttractionAnnotationView : MKAnnotationView
@end
将下面的代码添加到 PVAttractionAnnotationView.m:
#import "PVAttractionAnnotationView.h"
#import "PVAttractionAnnotation.h"
@implementation PVAttractionAnnotationView
- (id)initWithAnnotation:(id)annotation reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self) {
PVAttractionAnnotation *attractionAnnotation = self.annotation;
switch (attractionAnnotation.type) {
case PVAttractionFirstAid:
self.image = [UIImage imageNamed:@"firstaid"];
break;
case PVAttractionFood:
self.image = [UIImage imageNamed:@"food"];
break;
case PVAttractionRide:
self.image = [UIImage imageNamed:@"ride"];
break;
default:
self.image = [UIImage imageNamed:@"star"];
break;
}
}
return self;
}
@end
上面重载了方法 initWithAnnotation:reuseIdentifier:; 根据注解不同的type属性,为注解设置不同的image属性。现在你创建好了注解和与其相关的view,下面可以将它们添加到map view中了!
首先,你需要准备在 initWithAnnotation:reuseIdentifier:方法中用到的一些资源,以及经典相关的位置信息(MagicMountainAttractions.plist). 这些资源包含在 resources for this tutorial – 将它们拷贝到工程的Images群组中。
在plist文件中包含了坐标信息以及其它与公园景点相关的一些详细信息,如下所示:
name
Goliath
location
{34.42635,-118.59712}
type
1
subtitle
Intensity: 8/10
name
Batman
location
{34.42581,-118.60089}
type
1
subtitle
Intensity: 6/10
name
Ridler's Revenge
location
{34.42430,-118.60074}
type
1
subtitle
Intensity: 6/10
name
X2
location
{34.42156,-118.59556}
type
1
subtitle
Intensity: 10/10
name
Tatsu
location
{34.42150,-118.59741}
type
1
subtitle
Intensity: 7/10
name
Panda Express
location
{34.42126,-118.595637}
type
2
subtitle
Cost: $$
name
Cold Stone
location
{34.42401,-118.59495}
type
2
subtitle
Cost: $
name
First Aid
location
{34.42640,-118.59918}
type
3
subtitle
Call 911 For Emergency
现在你以及拥有上面这些资源了,当然你也可以使用新的注解!回到 PVParkMapViewController.m 并import MKAnnotation 和 MKAnnotationView两个类, 如下所示:
#import "PVAttractionAnnotation.h"
#import "PVAttractionAnnotationView.h"
下面定义一个方法,将景点的注解添加到map view中:
添加如下方法(还是在PVParkMapViewController.m):
- (void)addAttractionPins {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"MagicMountainAttractions" ofType:@"plist"];
NSArray *attractions = [NSArray arrayWithContentsOfFile:filePath];
for (NSDictionary *attraction in attractions) {
PVAttractionAnnotation *annotation = [[PVAttractionAnnotation alloc] init];
CGPoint point = CGPointFromString(attraction[@"location"]);
annotation.coordinate = CLLocationCoordinate2DMake(point.x, point.y);
annotation.title = attraction[@"name"];
annotation.type = [attraction[@"type"] integerValue];
annotation.subtitle = attraction[@"subtitle"];
[self.mapView addAnnotation:annotation];
}
}
上面的个方法读取 MagicMountainAttractions.plist 文件,并对字典数组进行枚举. 针对每个条目,都使用相关的景点信息来创建一个 PVAttractionAnnotation 实例,并将它们添加到map view中。现在需要更新一下 loadSelectedOptions 方法,当被选中时,以匹配新的选项,并执行新的方法。
将下面的代码添加到 loadSelectedOptions 中(仍然在 PVParkMapViewController.m):
- (void)loadSelectedOptions {
[self.mapView removeAnnotations:self.mapView.annotations];
[self.mapView removeOverlays:self.mapView.overlays];
for (NSNumber *option in self.selectedOptions) {
switch ([option integerValue]) {
case PVMapOverlay:
[self addOverlay];
break;
case PVMapPins:
[self addAttractionPins];
break;
default:
break;
}
}
}
现在快完成了!不过还差一点,你需要实现另外一个delegate方法,这个delegate方法给map view提供一个MKAnnotationView 实例,这样才能够进行渲染。
将下面的代码添加到 PVParkMapViewController.m:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation {
PVAttractionAnnotationView *annotationView = [[PVAttractionAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"Attraction"];
annotationView.canShowCallout = YES;
return annotationView;
}
上面的方法会接受一个 MKAnnotation ,并用此来创建一个 PVAttractionAnnotationView. 它的canShowCallout 属性设置为YES,这样当用户触摸到这个注解时,会显示出更多的信息,最后将这个注解返回!
启动景点Pin,看看结果是什么样,如下图:
景点pin在这里看起来有点不圆滑!至此,已经介绍了MapKit许多复杂的知识了,包括overlays和annotation。不过如果你希望绘制一些基本的图形呢:线条、形状和圆等?使用MapKit framework同样可以在map view上进行绘制!MapKit提供的MKPolyline, MKPolygon 和 MKCircle 就可以做到。
我走的线路 – MKPolyline
如果你去过魔山,你应该知道 Goliath hypercoaster 是一个非常令人难以置信的旅程, 当骑手们跨进了大门,就想一路狂奔!为了帮助这些骑手们,你绘制了一条从公园入口到Goliath的路径。
通过MKPolyline 可以非常方便的画一条连接多个点的线,比如绘制一条从A到B的非直线路径。在程序中用 MKPolyline 绘制路径,Goliath的粉丝们会更加快捷的骑行。!需要一系列的经纬度坐标信息,这些坐标将用来按顺序的进行绘制。在这里顺序非常重要 — 否则,绘制出来的线路会是无用的,对骑手们来说毫无意义!
resources for this tutorial中包含了一个叫 EntranceToGoliathRoute.plist 的文件,里面有路径信息,将这个文件添加到工程中。这个plist文件中的内容如下:
01.
02.
03.
04.
05. {34.42367,-118.594836}
06. {34.423597,-118.595205}
07. {34.423004,-118.59537}
08. {34.423044,-118.595806}
09. {34.423419,-118.596126}
10. {34.423569,-118.596229}
11. {34.42382,-118.596192}
12. {34.42407,-118.596283}
13. {34.424323,-118.596534}
14. {34.42464,-118.596858}
15. {34.42501,-118.596838}
16. {34.42537,-118.596688}
17. {34.425690,-118.596683}
18. {34.42593,-118.596806}
19. {34.42608,-118.597101}
20. {34.42634,-118.597094}
21.
22.
如上,属性列表是一个简单的字符串数组,字符串中包含了路径中每个点的经度和纬度信息。现在,你需要写一个方法来读取plist文件中的内容,并为骑手们创建出一条路。
将下面的代码添加到 PVParkMapViewController.m:
- (void)addRoute {
NSString *thePath = [[NSBundle mainBundle] pathForResource:@"EntranceToGoliathRoute" ofType:@"plist"];
NSArray *pointsArray = [NSArray arrayWithContentsOfFile:thePath];
NSInteger pointsCount = pointsArray.count;
CLLocationCoordinate2D pointsToUse[pointsCount];
for(int i = 0; i < pointsCount; i++) {
CGPoint p = CGPointFromString(pointsArray[i]);
pointsToUse[i] = CLLocationCoordinate2DMake(p.x,p.y);
}
MKPolyline *myPolyline = [MKPolyline polylineWithCoordinates:pointsToUse count:pointsCount];
[self.mapView addOverlay:myPolyline];
上面这个方法会读取 EntranceToGoliathRoute.plist, 并遍历其中包含的内容,然后将信息单独坐标字符串转换为 CLLocationCoordinate2D 结构。
很明显,在程序中绘制折线是很简单的;你创建一个数组,数组里面包含所有的点,并将这个数组传递给 MKPolyline! 没有比这更简单的了。
下面,你需要添加一个选项,允许用户打开或者关闭折线路径。用下面的代码更新一下 loadSelectedOptions 方法:
- (void)loadSelectedOptions {
[self.mapView removeAnnotations:self.mapView.annotations];
[self.mapView removeOverlays:self.mapView.overlays];
for (NSNumber *option in self.selectedOptions) {
switch ([option integerValue]) {
case PVMapOverlay:
[self addOverlay];
break;
case PVMapPins:
[self addAttractionPins];
break;
case PVMapRoute:
[self addRoute];
break;
default:
break;
}
}
}
最后,为了将所有的这些内容结合在一起,需要更新一下delegate方法—返回在map view中实际需要渲染的view。更新一下 mapView:viewForOverlay: 方法,以能够处理折线图层的情况,如下:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay {
if ([overlay isKindOfClass:PVParkMapOverlay.class]) {
UIImage *magicMountainImage = [UIImage imageNamed:@"overlay_park"];
PVParkMapOverlayView *overlayView = [[PVParkMapOverlayView alloc] initWithOverlay:overlay overlayImage:magicMountainImage];
return overlayView;
} else if ([overlay isKindOfClass:MKPolyline.class]) {
MKPolylineView *lineView = [[MKPolylineView alloc] initWithOverlay:overlay];
lineView.strokeColor = [UIColor greenColor];
return lineView;
}
return nil;
}
折线图层显示的处理跟之前的overlay view十分类似。只不过,绘制折线的时候,不需要提供自己的view对象。只需要使用 MKPolyLineView 即可—用overlay初始化一个新的实例即可。
MKPolyLineView 同样提供可以修改折线的一些属性。比如,可以把折线的颜色修改为绿色。
编译并运行程序,将route选项打开,出现在屏幕中的内容如下图所示:
Goliath的粉丝们现在可以利用这个绘制的路径去创造新的记录啦!要是能够实际的显示出公园的边界范围就太棒了——因为公园实际上并不是完整的占据整个屏幕。
虽然可以使用 MKPolyline 来绘制公园的边界,不过MapKit提供了另外一个类专门来绘制封闭的多边形:MKPolygon。
不要围着我 – MKPolygon
MKPolygon 跟MKPolyline非常相似(坐标集合中除了第一个点和最后一个点是连接以外)。可以创建一个 MKPolygon ,将其当做显示公园边界的一个overylay。公园的边界坐标已经定义在the MagicMountain.plist中了; 可以返回之前的内容看看 initWithFilename: 方法是如何从plist文件中读取出边界点的。
将下面的代码添加到 PVParkMapViewController.m:
- (void)addBoundary {
MKPolygon *polygon = [MKPolygon polygonWithCoordinates:self.park.boundary
count:self.park.boundaryPointsCount];
[self.mapView addOverlay:polygon];
}
addBoundary 方法的实现非常简单。从park实例中制定边界数组和边界点数,这样就可以很快捷的创建一个新的 MKPolygon 实例了!
你能猜猜下一步要做什么吗?跟上面的MKPolyline 操作非常类似。更新一下 loadSelectedOptions 方法,以能够显示或者隐藏公园的边界,如下所示:
- (void)loadSelectedOptions {
[self.mapView removeAnnotations:self.mapView.annotations];
[self.mapView removeOverlays:self.mapView.overlays];
for (NSNumber *option in self.selectedOptions) {
switch ([option integerValue]) {
case PVMapOverlay:
[self addOverlay];
break;
case PVMapPins:
[self addAttractionPins];
break;
case PVMapRoute:
[self addRoute];
break;
case PVMapBoundary:
[self addBoundary];
break;
default:
break;
}
}
}
MKPolygon 跟MKPolyline一样,遵循 MKOverlay ,因此也需要更新一下相关的delegate方法。
在PVParkMapViewController.m中更新一下delegate方法:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay {
if ([overlay isKindOfClass:PVParkMapOverlay.class]) {
UIImage *magicMountainImage = [UIImage imageNamed:@"overlay_park"];
PVParkMapOverlayView *overlayView = [[PVParkMapOverlayView alloc] initWithOverlay:overlay overlayImage:magicMountainImage];
return overlayView;
} else if ([overlay isKindOfClass:MKPolyline.class]) {
MKPolylineView *lineView = [[MKPolylineView alloc] initWithOverlay:overlay];
lineView.strokeColor = [UIColor greenColor];
return lineView;
} else if ([overlay isKindOfClass:MKPolygon.class]) {
MKPolygonView *polygonView = [[MKPolygonView alloc] initWithOverlay:overlay];
polygonView.strokeColor = [UIColor magentaColor];
return polygonView;
}
return nil;
}
如上所示,delegate的更新方式跟之前一样简单。创建一个MKPolygonView实例,并将其颜色设置为magenta.编译并运行程序,将看到公园的边界!
上面就是折线和多边形的绘制。下面我将介绍最后一种绘制——使用MKCircle绘制圆
沙滩上的圈圈 – MKCircle
MKCircle 跟 MKPolyline 和 MKPolygon,同样非常相似。只不过它是根据给定的中心坐标和半径来绘制一个圆。
可以想象一下,在公园里,用户可能希望在地图上做一下标注,跟将这个标注与他人分享。此时,就可以使用一个圆来代表用户的标注。
在本文中,你不会走的很远,不过,最起码的,你可以从文件中加载一些坐标数据,并在地图上绘制出一些圆,来当做用户在地图上的一些标注。
MKCircle overlay 可以很容易的就实现这个功能.
resources for this tutorial包含了相关标注的位置信息,只需要将其添加到工程中。
每个文件包含了一些坐标信息。
{34.42481,-118.596914}
{34.423383,-118.596101}
{34.423628,-118.595197}
{34.421832,-118.595404}
我将用 PVCharacter代表用户的标注。下面就在Models群组中创建一个新类 PVCharacter ,并继承自 MKCircle.
然后用下面的代码替换 PVCharacter.h 中的内容:
#import
#import
@interface PVCharacter : MKCircle
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) UIColor *color;
@end
新的这个类遵循 MKOverlay 协议, 并定义了两个属性: name和 color.
这个类的实现非常简单 — 不需要添加任何内容.
#import "PVCharacter.h"
@implementation PVCharacter
@end
PVParkMapViewController.m 中import PVCharacter.h。如下代码:
#import "PVCharacter.h"
现在,需要一个方法,将plist文件中的数据加载到程序中。将如下代码添加到PVParkMapViewController.m:
- (void)addCharacterLocation {
NSString *batmanFilePath = [[NSBundle mainBundle] pathForResource:@"BatmanLocations" ofType:@"plist"];
NSArray *batmanLocations = [NSArray arrayWithContentsOfFile:batmanFilePath];
CGPoint batmanPoint = CGPointFromString(batmanLocations[rand()%4]);
PVCharacter *batman = (PVCharacter *)[PVCharacter circleWithCenterCoordinate:CLLocationCoordinate2DMake(batmanPoint.x, batmanPoint.y)
radius:MAX(5, rand()%40)];
batman.color = [UIColor blueColor];
NSString *tazFilePath = [[NSBundle mainBundle] pathForResource:@"TazLocations" ofType:@"plist"];
NSArray *tazLocations = [NSArray arrayWithContentsOfFile:tazFilePath];
CGPoint tazPoint = CGPointFromString(tazLocations[rand()%4]);
PVCharacter *taz = (PVCharacter *)[PVCharacter circleWithCenterCoordinate:CLLocationCoordinate2DMake(tazPoint.x, tazPoint.y)
radius:MAX(5, rand()%40)];
taz.color = [UIColor orangeColor];
NSString *tweetyFilePath = [[NSBundle mainBundle] pathForResource:@"TweetyBirdLocations" ofType:@"plist"];
NSArray *tweetyLocations = [NSArray arrayWithContentsOfFile:tweetyFilePath];
CGPoint tweetyPoint = CGPointFromString(tweetyLocations[rand()%4]);
PVCharacter *tweety = (PVCharacter *)[PVCharacter circleWithCenterCoordinate:CLLocationCoordinate2DMake(tweetyPoint.x, tweetyPoint.y)
radius:MAX(5, rand()%40)];
tweety.color = [UIColor yellowColor];
[self.mapView addOverlay:batman];
[self.mapView addOverlay:taz];
[self.mapView addOverlay:tweety];
}
上面的这个方法对每个标注都做了相同的操作。首先,从plist中读出数据,然后随即选取出一个位置。下一步,根据选取出的位置,创建一个 PVCharacter 实例。随即选取一个半径值。
最后,给每个标注都设置一个颜色,并添加到map view中。
现在基本算是完成了 — 你还记得最后几步是如何操作的吗?
没错 — 你同样还需要通过delegate方法,给map view提供 MKOverlayView.
更新一下 PVParkMapViewController.m 中的delegate方法:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay {
if ([overlay isKindOfClass:PVParkMapOverlay.class]) {
UIImage *magicMountainImage = [UIImage imageNamed:@"overlay_park"];
PVParkMapOverlayView *overlayView = [[PVParkMapOverlayView alloc] initWithOverlay:overlay overlayImage:magicMountainImage];
return overlayView;
} else if ([overlay isKindOfClass:MKPolyline.class]) {
MKPolylineView *lineView = [[MKPolylineView alloc] initWithOverlay:overlay];
lineView.strokeColor = [UIColor greenColor];
return lineView;
} else if ([overlay isKindOfClass:MKPolygon.class]) {
MKPolygonView *polygonView = [[MKPolygonView alloc] initWithOverlay:overlay];
polygonView.strokeColor = [UIColor magentaColor];
return polygonView;
} else if ([overlay isKindOfClass:PVCharacter.class]) {
MKCircleView *circleView = [[MKCircleView alloc] initWithOverlay:overlay];
circleView.strokeColor = [(PVCharacter *)overlay color]];
return circleView;
}
return nil;
}
最后,更新一下 loadSelectedOptions ,让用户可以打开或者隐藏标注信息。
如下是更新后的代码:
- (void)loadSelectedOptions {
[self.mapView removeAnnotations:self.mapView.annotations];
[self.mapView removeOverlays:self.mapView.overlays];
for (NSNumber *option in self.selectedOptions) {
switch ([option integerValue]) {
case PVMapOverlay:
[self addOverlay];
break;
case PVMapPins:
[self addAttractionPins];
break;
case PVMapRoute:
[self addRoute];
break;
case PVMapBoundary:
[self addBoundary];
break;
case PVMapCharacterLocation:
[self addCharacterLocation];
break;
default:
break;
}
}
}
编译并运行程序,然后打开character,将看到如下内容:
何去何从?
上面的内容就是本文要介绍的了 — 至此,你已经知道如何使用MapKit绘制自己的overylay image和overlay view。
这里是本文最后完成工程代码: final example project 。
恭喜你 — 你已经使用了MapKit提供的许多重要的功能了。并完成了一个地图程序最基本的功能,实现了标注,卫星视图和定制图层!
这个程序的另一个方向就是研究一下创建地图图层的其它方法。
实际上,从简单到复杂的,有许多不同的方法来创建图层。本文使用的方法是图片: overlay_park .
为了生成图层,将卫星视图的截图当做公园的底层。然后绘制一些过山车,树,停车点,以及其它的一些新图层。
当获得了卫星截图后,需要给这个截图的4个角落确定一下经纬度。这些经纬度将用来创建公园的属性列表 — 用在map view上的位置图层信息。
这里还有很多高级的方法来创建图层 — 或许更加高效. 一些可选的方法是使用 KML文件、 MapBox 或者其它第三方提供的资源。
本文的主要目的是介绍关于MapKit framework APIs,因此没有深入介绍overylay类型。如果你是一名地图高级开发者,你可以更加深入的研究一下其它方面的主题。
希望本文对你有用,我也希望能够看到你在程序中使用MapKit overylay。如果你有任何问题或者建议,请反馈给我!
来源:破船的博客
原文:http://www.raywenderlich.com/30001/overlay-images-and-overlay-views-with-mapkit-tutorial
如果你用Maps程序搜索过位置信息,那么你肯定看到过在地图上出现的许多Pin。这可以理解为注解(annotation),它是用 MKAnnotationView创建的。你也可以在你的程序中使用注解 — 并使用你想要的任何图片,不仅仅是pin!
在程序中使用注解来标出具体的某个景点,这对游客来说非常有用。注解对象的使用方法跟MKOverlay 和 MKOverlayView非常类似, 只不过需要使用的类是MKAnnotation 和 MKAnnotationView。
在Annotations群组中创建一个名为 PVAttractionAnnotation 新的类,并继承自 NSObject。然后用下面的代码替换 PVAttractionAnnotation.h 文件中的内容:
#import
#import
typedef NS_ENUM(NSInteger, PVAttractionType) {
PVAttractionDefault = 0,
PVAttractionRide,
PVAttractionFood,
PVAttractionFirstAid
};
@interface PVAttractionAnnotation : NSObject
@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *subtitle;
@property (nonatomic) PVAttractionType type;
@end
上面的代码中,首先是import MapKit,然后为PVAttractionType.定义了一个枚举。这个枚举列出了注解的类型: 游乐设施,食物,急救和默认。
接着让这个类遵循 MKAnnotation Protocol。 跟MKOverlay类似, MKAnnotation 有一个 required coordinate 属性. 最后是定义了一些属性。OK, 下面我们来看看PVAttractionAnnotation的实现。
将 PVAttractionAnnotation.m 按照如下修改:
#import
#import
@interface PVAttractionAnnotationView : MKAnnotationView
@end
将下面的代码添加到 PVAttractionAnnotationView.m:
#import "PVAttractionAnnotationView.h"
#import "PVAttractionAnnotation.h"
@implementation PVAttractionAnnotationView
- (id)initWithAnnotation:(id)annotation reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self) {
PVAttractionAnnotation *attractionAnnotation = self.annotation;
switch (attractionAnnotation.type) {
case PVAttractionFirstAid:
self.image = [UIImage imageNamed:@"firstaid"];
break;
case PVAttractionFood:
self.image = [UIImage imageNamed:@"food"];
break;
case PVAttractionRide:
self.image = [UIImage imageNamed:@"ride"];
break;
default:
self.image = [UIImage imageNamed:@"star"];
break;
}
}
return self;
}
@end
上面重载了方法 initWithAnnotation:reuseIdentifier:; 根据注解不同的type属性,为注解设置不同的image属性。现在你创建好了注解和与其相关的view,下面可以将它们添加到map view中了!
首先,你需要准备在 initWithAnnotation:reuseIdentifier:方法中用到的一些资源,以及经典相关的位置信息(MagicMountainAttractions.plist). 这些资源包含在 resources for this tutorial – 将它们拷贝到工程的Images群组中。
在plist文件中包含了坐标信息以及其它与公园景点相关的一些详细信息,如下所示:
name
Goliath
location
{34.42635,-118.59712}
type
1
subtitle
Intensity: 8/10
name
Batman
location
{34.42581,-118.60089}
type
1
subtitle
Intensity: 6/10
name
Ridler's Revenge
location
{34.42430,-118.60074}
type
1
subtitle
Intensity: 6/10
name
X2
location
{34.42156,-118.59556}
type
1
subtitle
Intensity: 10/10
name
Tatsu
location
{34.42150,-118.59741}
type
1
subtitle
Intensity: 7/10
name
Panda Express
location
{34.42126,-118.595637}
type
2
subtitle
Cost: $$
name
Cold Stone
location
{34.42401,-118.59495}
type
2
subtitle
Cost: $
name
First Aid
location
{34.42640,-118.59918}
type
3
subtitle
Call 911 For Emergency
现在你以及拥有上面这些资源了,当然你也可以使用新的注解!回到 PVParkMapViewController.m 并import MKAnnotation 和 MKAnnotationView两个类, 如下所示:
#import "PVAttractionAnnotation.h"
#import "PVAttractionAnnotationView.h"
下面定义一个方法,将景点的注解添加到map view中:
添加如下方法(还是在PVParkMapViewController.m):
- (void)addAttractionPins {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"MagicMountainAttractions" ofType:@"plist"];
NSArray *attractions = [NSArray arrayWithContentsOfFile:filePath];
for (NSDictionary *attraction in attractions) {
PVAttractionAnnotation *annotation = [[PVAttractionAnnotation alloc] init];
CGPoint point = CGPointFromString(attraction[@"location"]);
annotation.coordinate = CLLocationCoordinate2DMake(point.x, point.y);
annotation.title = attraction[@"name"];
annotation.type = [attraction[@"type"] integerValue];
annotation.subtitle = attraction[@"subtitle"];
[self.mapView addAnnotation:annotation];
}
}
上面的个方法读取 MagicMountainAttractions.plist 文件,并对字典数组进行枚举. 针对每个条目,都使用相关的景点信息来创建一个 PVAttractionAnnotation 实例,并将它们添加到map view中。现在需要更新一下 loadSelectedOptions 方法,当被选中时,以匹配新的选项,并执行新的方法。
将下面的代码添加到 loadSelectedOptions 中(仍然在 PVParkMapViewController.m):
- (void)loadSelectedOptions {
[self.mapView removeAnnotations:self.mapView.annotations];
[self.mapView removeOverlays:self.mapView.overlays];
for (NSNumber *option in self.selectedOptions) {
switch ([option integerValue]) {
case PVMapOverlay:
[self addOverlay];
break;
case PVMapPins:
[self addAttractionPins];
break;
default:
break;
}
}
}
现在快完成了!不过还差一点,你需要实现另外一个delegate方法,这个delegate方法给map view提供一个MKAnnotationView 实例,这样才能够进行渲染。
将下面的代码添加到 PVParkMapViewController.m:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation {
PVAttractionAnnotationView *annotationView = [[PVAttractionAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"Attraction"];
annotationView.canShowCallout = YES;
return annotationView;
}
上面的方法会接受一个 MKAnnotation ,并用此来创建一个 PVAttractionAnnotationView. 它的canShowCallout 属性设置为YES,这样当用户触摸到这个注解时,会显示出更多的信息,最后将这个注解返回!
启动景点Pin,看看结果是什么样,如下图:
景点pin在这里看起来有点不圆滑!至此,已经介绍了MapKit许多复杂的知识了,包括overlays和annotation。不过如果你希望绘制一些基本的图形呢:线条、形状和圆等?使用MapKit framework同样可以在map view上进行绘制!MapKit提供的MKPolyline, MKPolygon 和 MKCircle 就可以做到。
我走的线路 – MKPolyline
如果你去过魔山,你应该知道 Goliath hypercoaster 是一个非常令人难以置信的旅程, 当骑手们跨进了大门,就想一路狂奔!为了帮助这些骑手们,你绘制了一条从公园入口到Goliath的路径。
通过MKPolyline 可以非常方便的画一条连接多个点的线,比如绘制一条从A到B的非直线路径。在程序中用 MKPolyline 绘制路径,Goliath的粉丝们会更加快捷的骑行。!需要一系列的经纬度坐标信息,这些坐标将用来按顺序的进行绘制。在这里顺序非常重要 — 否则,绘制出来的线路会是无用的,对骑手们来说毫无意义!
resources for this tutorial中包含了一个叫 EntranceToGoliathRoute.plist 的文件,里面有路径信息,将这个文件添加到工程中。这个plist文件中的内容如下:
01.
02.
03.
04.
05. {34.42367,-118.594836}
06. {34.423597,-118.595205}
07. {34.423004,-118.59537}
08. {34.423044,-118.595806}
09. {34.423419,-118.596126}
10. {34.423569,-118.596229}
11. {34.42382,-118.596192}
12. {34.42407,-118.596283}
13. {34.424323,-118.596534}
14. {34.42464,-118.596858}
15. {34.42501,-118.596838}
16. {34.42537,-118.596688}
17. {34.425690,-118.596683}
18. {34.42593,-118.596806}
19. {34.42608,-118.597101}
20. {34.42634,-118.597094}
21.
22.
如上,属性列表是一个简单的字符串数组,字符串中包含了路径中每个点的经度和纬度信息。现在,你需要写一个方法来读取plist文件中的内容,并为骑手们创建出一条路。
将下面的代码添加到 PVParkMapViewController.m:
- (void)addRoute {
NSString *thePath = [[NSBundle mainBundle] pathForResource:@"EntranceToGoliathRoute" ofType:@"plist"];
NSArray *pointsArray = [NSArray arrayWithContentsOfFile:thePath];
NSInteger pointsCount = pointsArray.count;
CLLocationCoordinate2D pointsToUse[pointsCount];
for(int i = 0; i < pointsCount; i++) {
CGPoint p = CGPointFromString(pointsArray[i]);
pointsToUse[i] = CLLocationCoordinate2DMake(p.x,p.y);
}
MKPolyline *myPolyline = [MKPolyline polylineWithCoordinates:pointsToUse count:pointsCount];
[self.mapView addOverlay:myPolyline];
上面这个方法会读取 EntranceToGoliathRoute.plist, 并遍历其中包含的内容,然后将信息单独坐标字符串转换为 CLLocationCoordinate2D 结构。
很明显,在程序中绘制折线是很简单的;你创建一个数组,数组里面包含所有的点,并将这个数组传递给 MKPolyline! 没有比这更简单的了。
下面,你需要添加一个选项,允许用户打开或者关闭折线路径。用下面的代码更新一下 loadSelectedOptions 方法:
- (void)loadSelectedOptions {
[self.mapView removeAnnotations:self.mapView.annotations];
[self.mapView removeOverlays:self.mapView.overlays];
for (NSNumber *option in self.selectedOptions) {
switch ([option integerValue]) {
case PVMapOverlay:
[self addOverlay];
break;
case PVMapPins:
[self addAttractionPins];
break;
case PVMapRoute:
[self addRoute];
break;
default:
break;
}
}
}
最后,为了将所有的这些内容结合在一起,需要更新一下delegate方法—返回在map view中实际需要渲染的view。更新一下 mapView:viewForOverlay: 方法,以能够处理折线图层的情况,如下:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay {
if ([overlay isKindOfClass:PVParkMapOverlay.class]) {
UIImage *magicMountainImage = [UIImage imageNamed:@"overlay_park"];
PVParkMapOverlayView *overlayView = [[PVParkMapOverlayView alloc] initWithOverlay:overlay overlayImage:magicMountainImage];
return overlayView;
} else if ([overlay isKindOfClass:MKPolyline.class]) {
MKPolylineView *lineView = [[MKPolylineView alloc] initWithOverlay:overlay];
lineView.strokeColor = [UIColor greenColor];
return lineView;
}
return nil;
}
折线图层显示的处理跟之前的overlay view十分类似。只不过,绘制折线的时候,不需要提供自己的view对象。只需要使用 MKPolyLineView 即可—用overlay初始化一个新的实例即可。
MKPolyLineView 同样提供可以修改折线的一些属性。比如,可以把折线的颜色修改为绿色。
编译并运行程序,将route选项打开,出现在屏幕中的内容如下图所示:
Goliath的粉丝们现在可以利用这个绘制的路径去创造新的记录啦!要是能够实际的显示出公园的边界范围就太棒了——因为公园实际上并不是完整的占据整个屏幕。
虽然可以使用 MKPolyline 来绘制公园的边界,不过MapKit提供了另外一个类专门来绘制封闭的多边形:MKPolygon。
不要围着我 – MKPolygon
MKPolygon 跟MKPolyline非常相似(坐标集合中除了第一个点和最后一个点是连接以外)。可以创建一个 MKPolygon ,将其当做显示公园边界的一个overylay。公园的边界坐标已经定义在the MagicMountain.plist中了; 可以返回之前的内容看看 initWithFilename: 方法是如何从plist文件中读取出边界点的。
将下面的代码添加到 PVParkMapViewController.m:
- (void)addBoundary {
MKPolygon *polygon = [MKPolygon polygonWithCoordinates:self.park.boundary
count:self.park.boundaryPointsCount];
[self.mapView addOverlay:polygon];
}
addBoundary 方法的实现非常简单。从park实例中制定边界数组和边界点数,这样就可以很快捷的创建一个新的 MKPolygon 实例了!
你能猜猜下一步要做什么吗?跟上面的MKPolyline 操作非常类似。更新一下 loadSelectedOptions 方法,以能够显示或者隐藏公园的边界,如下所示:
- (void)loadSelectedOptions {
[self.mapView removeAnnotations:self.mapView.annotations];
[self.mapView removeOverlays:self.mapView.overlays];
for (NSNumber *option in self.selectedOptions) {
switch ([option integerValue]) {
case PVMapOverlay:
[self addOverlay];
break;
case PVMapPins:
[self addAttractionPins];
break;
case PVMapRoute:
[self addRoute];
break;
case PVMapBoundary:
[self addBoundary];
break;
default:
break;
}
}
}
MKPolygon 跟MKPolyline一样,遵循 MKOverlay ,因此也需要更新一下相关的delegate方法。
在PVParkMapViewController.m中更新一下delegate方法:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay {
if ([overlay isKindOfClass:PVParkMapOverlay.class]) {
UIImage *magicMountainImage = [UIImage imageNamed:@"overlay_park"];
PVParkMapOverlayView *overlayView = [[PVParkMapOverlayView alloc] initWithOverlay:overlay overlayImage:magicMountainImage];
return overlayView;
} else if ([overlay isKindOfClass:MKPolyline.class]) {
MKPolylineView *lineView = [[MKPolylineView alloc] initWithOverlay:overlay];
lineView.strokeColor = [UIColor greenColor];
return lineView;
} else if ([overlay isKindOfClass:MKPolygon.class]) {
MKPolygonView *polygonView = [[MKPolygonView alloc] initWithOverlay:overlay];
polygonView.strokeColor = [UIColor magentaColor];
return polygonView;
}
return nil;
}
如上所示,delegate的更新方式跟之前一样简单。创建一个MKPolygonView实例,并将其颜色设置为magenta.编译并运行程序,将看到公园的边界!
上面就是折线和多边形的绘制。下面我将介绍最后一种绘制——使用MKCircle绘制圆
沙滩上的圈圈 – MKCircle
MKCircle 跟 MKPolyline 和 MKPolygon,同样非常相似。只不过它是根据给定的中心坐标和半径来绘制一个圆。
可以想象一下,在公园里,用户可能希望在地图上做一下标注,跟将这个标注与他人分享。此时,就可以使用一个圆来代表用户的标注。
在本文中,你不会走的很远,不过,最起码的,你可以从文件中加载一些坐标数据,并在地图上绘制出一些圆,来当做用户在地图上的一些标注。
MKCircle overlay 可以很容易的就实现这个功能.
resources for this tutorial包含了相关标注的位置信息,只需要将其添加到工程中。
每个文件包含了一些坐标信息。
{34.42481,-118.596914}
{34.423383,-118.596101}
{34.423628,-118.595197}
{34.421832,-118.595404}
我将用 PVCharacter代表用户的标注。下面就在Models群组中创建一个新类 PVCharacter ,并继承自 MKCircle.
然后用下面的代码替换 PVCharacter.h 中的内容:
#import
#import
@interface PVCharacter : MKCircle
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) UIColor *color;
@end
新的这个类遵循 MKOverlay 协议, 并定义了两个属性: name和 color.
这个类的实现非常简单 — 不需要添加任何内容.
#import "PVCharacter.h"
@implementation PVCharacter
@end
PVParkMapViewController.m 中import PVCharacter.h。如下代码:
#import "PVCharacter.h"
现在,需要一个方法,将plist文件中的数据加载到程序中。将如下代码添加到PVParkMapViewController.m:
- (void)addCharacterLocation {
NSString *batmanFilePath = [[NSBundle mainBundle] pathForResource:@"BatmanLocations" ofType:@"plist"];
NSArray *batmanLocations = [NSArray arrayWithContentsOfFile:batmanFilePath];
CGPoint batmanPoint = CGPointFromString(batmanLocations[rand()%4]);
PVCharacter *batman = (PVCharacter *)[PVCharacter circleWithCenterCoordinate:CLLocationCoordinate2DMake(batmanPoint.x, batmanPoint.y)
radius:MAX(5, rand()%40)];
batman.color = [UIColor blueColor];
NSString *tazFilePath = [[NSBundle mainBundle] pathForResource:@"TazLocations" ofType:@"plist"];
NSArray *tazLocations = [NSArray arrayWithContentsOfFile:tazFilePath];
CGPoint tazPoint = CGPointFromString(tazLocations[rand()%4]);
PVCharacter *taz = (PVCharacter *)[PVCharacter circleWithCenterCoordinate:CLLocationCoordinate2DMake(tazPoint.x, tazPoint.y)
radius:MAX(5, rand()%40)];
taz.color = [UIColor orangeColor];
NSString *tweetyFilePath = [[NSBundle mainBundle] pathForResource:@"TweetyBirdLocations" ofType:@"plist"];
NSArray *tweetyLocations = [NSArray arrayWithContentsOfFile:tweetyFilePath];
CGPoint tweetyPoint = CGPointFromString(tweetyLocations[rand()%4]);
PVCharacter *tweety = (PVCharacter *)[PVCharacter circleWithCenterCoordinate:CLLocationCoordinate2DMake(tweetyPoint.x, tweetyPoint.y)
radius:MAX(5, rand()%40)];
tweety.color = [UIColor yellowColor];
[self.mapView addOverlay:batman];
[self.mapView addOverlay:taz];
[self.mapView addOverlay:tweety];
}
上面的这个方法对每个标注都做了相同的操作。首先,从plist中读出数据,然后随即选取出一个位置。下一步,根据选取出的位置,创建一个 PVCharacter 实例。随即选取一个半径值。
最后,给每个标注都设置一个颜色,并添加到map view中。
现在基本算是完成了 — 你还记得最后几步是如何操作的吗?
没错 — 你同样还需要通过delegate方法,给map view提供 MKOverlayView.
更新一下 PVParkMapViewController.m 中的delegate方法:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay {
if ([overlay isKindOfClass:PVParkMapOverlay.class]) {
UIImage *magicMountainImage = [UIImage imageNamed:@"overlay_park"];
PVParkMapOverlayView *overlayView = [[PVParkMapOverlayView alloc] initWithOverlay:overlay overlayImage:magicMountainImage];
return overlayView;
} else if ([overlay isKindOfClass:MKPolyline.class]) {
MKPolylineView *lineView = [[MKPolylineView alloc] initWithOverlay:overlay];
lineView.strokeColor = [UIColor greenColor];
return lineView;
} else if ([overlay isKindOfClass:MKPolygon.class]) {
MKPolygonView *polygonView = [[MKPolygonView alloc] initWithOverlay:overlay];
polygonView.strokeColor = [UIColor magentaColor];
return polygonView;
} else if ([overlay isKindOfClass:PVCharacter.class]) {
MKCircleView *circleView = [[MKCircleView alloc] initWithOverlay:overlay];
circleView.strokeColor = [(PVCharacter *)overlay color]];
return circleView;
}
return nil;
}
最后,更新一下 loadSelectedOptions ,让用户可以打开或者隐藏标注信息。
如下是更新后的代码:
- (void)loadSelectedOptions {
[self.mapView removeAnnotations:self.mapView.annotations];
[self.mapView removeOverlays:self.mapView.overlays];
for (NSNumber *option in self.selectedOptions) {
switch ([option integerValue]) {
case PVMapOverlay:
[self addOverlay];
break;
case PVMapPins:
[self addAttractionPins];
break;
case PVMapRoute:
[self addRoute];
break;
case PVMapBoundary:
[self addBoundary];
break;
case PVMapCharacterLocation:
[self addCharacterLocation];
break;
default:
break;
}
}
}
编译并运行程序,然后打开character,将看到如下内容:
何去何从?
上面的内容就是本文要介绍的了 — 至此,你已经知道如何使用MapKit绘制自己的overylay image和overlay view。
这里是本文最后完成工程代码: final example project 。
恭喜你 — 你已经使用了MapKit提供的许多重要的功能了。并完成了一个地图程序最基本的功能,实现了标注,卫星视图和定制图层!
这个程序的另一个方向就是研究一下创建地图图层的其它方法。
实际上,从简单到复杂的,有许多不同的方法来创建图层。本文使用的方法是图片: overlay_park .
为了生成图层,将卫星视图的截图当做公园的底层。然后绘制一些过山车,树,停车点,以及其它的一些新图层。
当获得了卫星截图后,需要给这个截图的4个角落确定一下经纬度。这些经纬度将用来创建公园的属性列表 — 用在map view上的位置图层信息。
这里还有很多高级的方法来创建图层 — 或许更加高效. 一些可选的方法是使用 KML文件、 MapBox 或者其它第三方提供的资源。
本文的主要目的是介绍关于MapKit framework APIs,因此没有深入介绍overylay类型。如果你是一名地图高级开发者,你可以更加深入的研究一下其它方面的主题。
希望本文对你有用,我也希望能够看到你在程序中使用MapKit overylay。如果你有任何问题或者建议,请反馈给我!
来源:破船的博客
原文:http://www.raywenderlich.com/30001/overlay-images-and-overlay-views-with-mapkit-tutorial
相关文章推荐
- 使用MapKit叠加图层(raywenderlich翻译)(上)
- 03-05 创建和编辑AutoCAD实体(五) 使用图层、颜色和线型(1)使用图层(1-1)
- Reveal使用教程-使用Reveal查看任意iOS App的图层结构
- [转]Ultra Fractal教程系列24——如何使用图层07——渐变编辑器中的透明度
- MapObject控件的使用之加入图层
- Geoserver style使用(三)——按属性和缩放级别渲染矢量图层
- ps中渐变映射调整图层如何定义及其各项参数的使用技巧
- 03-05 创建和编辑AutoCAD实体(五) 使用图层、颜色和线型(1)使用图层(1-9)
- [转]Ultra Fractal教程系列21——如何使用图层04——学习关于图层的不透明度
- 转,MapX使用动态图层
- Photoshop教程:图层遮罩使用方法
- 7-11使用色彩调整图层
- 使用AE加载带有制图表达(Representation)的图层(Layer)
- 使用Phyghtmap为OpenStreetMap添加 DEM 高程图层
- PS基础 之 图层样式的使用
- Arcgis 图层编辑器“粘贴”功能菜单灰色不能使用的问题与解决办法
- 使用查询图层快速浏览
- Direct2D (19) : 图层之 TD2D1LayerParameters.geometricMask (使用几何图形剪裁图层)
- Unity3D-使用图层,锁定背景图片
- 操作系统内核开发:使用图层技术开发一个MessageBox