如何将UIBezierPath置于MK注释对象的后面?

5
在我的应用中,用户在地图上绘制形状,然后使用UIBeizerPath来绘制路径。然后根据路径的坐标显示仅在该区域内的结果。所有东西都很好,除了现在当注释放在地图视图上时,图钉看起来像是在路径后面,这意味着路径看起来在最前面。
我正在使用以下代码显示注释和路径:
 -(void)clearAnnotationAndPath:(id)sender {
    [_mapView removeAnnotations:_mapView.annotations];
    path = [UIBezierPath bezierPath];
    [shapeLayer removeFromSuperlayer];
}

- (void)handleGesture:(UIPanGestureRecognizer *)gesture
{

    CGPoint location = [gesture locationInView:_pathOverlay];

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        shapeLayer = [[CAShapeLayer alloc] init];
        shapeLayer.fillColor = [[UIColor clearColor] CGColor];
        shapeLayer.strokeColor = [[UIColor greenColor] CGColor];
        shapeLayer.lineWidth = 5.0;
        //[_mapView.layer addSublayer:shapeLayer];
        [pathOverlay.layer addSublayer:shapeLayer];
        path = [UIBezierPath bezierPath];
        [path moveToPoint:location];
    }

    else if (gesture.state == UIGestureRecognizerStateChanged)
    {
        [path addLineToPoint:location];
        shapeLayer.path = [path CGPath];
    }

    else if (gesture.state == UIGestureRecognizerStateEnded)
    {
        // MKMapView *mapView = (MKMapView *)gesture.view;

        [path addLineToPoint:location];
        [path closePath];
        allStations = [RoadmapData sharedInstance].data;
        for (int i=0; i<[allStations count]; i++) {
            NSDictionary * itemNo = [allStations objectAtIndex:i];

            NSString * fullAddress = [NSString stringWithFormat:@"%@,%@,%@,%@",[itemNo objectForKey:@"address"],[itemNo objectForKey:@"city"],[itemNo objectForKey:@"state"],[itemNo objectForKey:@"zip"]];
            CLGeocoder * geoCoder = [[CLGeocoder alloc]init];
            [geoCoder geocodeAddressString:fullAddress completionHandler:^(NSArray *placemarks, NSError *error) {

                if (error) {
                    NSLog(@"Geocode failed with error: %@", error);
                    return;
                }

                if(placemarks && placemarks.count > 0)
                {
                    CLPlacemark *placemark = placemarks[0];
                    CLLocation *location = placemark.location;
                    CLLocationCoordinate2D coords = location.coordinate;
                    CGPoint loc = [_mapView convertCoordinate:coords toPointToView:_pathOverlay];
                    if ([path containsPoint:loc])
                    {
                        NSString * name = [itemNo objectForKey:@"name"];
                        stationAnn = [[LocationAnnotation alloc]initWithCoordinate:coords Title:name subTitle:@"Wells Fargo Offer" annIndex:i];
                        stationAnn.tag = i;
                        [_mapView addAnnotation:stationAnn];
                    }
                    else{
                        NSLog(@"Out of boundary");
                    }
                }
            }];
            [self turnOffGesture:gesture];
        }
    }
}

- (void)mapView:(MKMapView *)aMapView didAddAnnotationViews:(NSArray *)views{
    if (views.count > 0) {
        UIView* firstAnnotation = [views objectAtIndex:0];
        UIView* parentView = [firstAnnotation superview];
        if (_pathOverlay == nil){
            // create a transparent view to add bezier paths to
            pathOverlay = [[UIView alloc] initWithFrame: parentView.frame];
            pathOverlay.opaque = NO;
            pathOverlay.backgroundColor = [UIColor clearColor];
            [parentView addSubview:pathOverlay];
        }

        // make sure annotations stay above pathOverlay
        for (UIView* view in views) {
            [parentView bringSubviewToFront:view];
        }
    }
}

另外,当我返回并查看后,它甚至没有绘制路径。

请帮忙。

谢谢。

1个回答

3
显然,当您通过以下方式将贝塞尔路径添加到地图中时:
        [_mapView.layer addSublayer:shapeLayer];

正在添加的是内部层之上,MKMapView用于绘制注释时的一些内容。如果您查看此相对相关的问题,您将看到可以实现MKMapViewDelegate协议,并在添加新站点注释时获得回调。当发生这种情况时,您基本上要检查新添加注释的视图层次结构,并在它们下方插入一个新的透明的UIView层。请注意,将所有注释放在此透明的UIView的前面。

  // always remember to assign the delegate to get callbacks!
  _mapView.delegate = self;

...

#pragma mark - MKMapViewDelegate

- (void)mapView:(MKMapView *)aMapView didAddAnnotationViews:(NSArray *)views{
    if (views.count > 0) {
        UIView* firstAnnotation = [views objectAtIndex:0];
        UIView* parentView = [firstAnnotation superview];
        // NOTE: could perform this initialization in viewDidLoad, too
        if (self.pathOverlay == nil){
            // create a transparent view to add bezier paths to
            pathOverlay = [[UIView alloc] initWithFrame: parentView.frame];
            pathOverlay.opaque = NO;
            pathOverlay.backgroundColor = [UIColor clearColor];
            [parentView addSubview:pathOverlay]; 
        }

        // make sure annotations stay above pathOverlay
        for (UIView* view in views) {
            [parentView bringSubviewToFront:view];
        }
    }
}

然后,不是将您的形状图层添加到_mapView.layer中,而是将其添加到透明视图层中,并在坐标转换中使用此新图层:

- (void)handleGesture:(UIPanGestureRecognizer*)gesture
{
    CGPoint location = [gesture locationInView: self.pathOverlay];

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        if (!shapeLayer)
        {
            shapeLayer = [[CAShapeLayer alloc] init];
            shapeLayer.fillColor = [[UIColor clearColor] CGColor];
            shapeLayer.strokeColor = [[UIColor greenColor] CGColor];
            shapeLayer.lineWidth = 5.0;
            [pathOverlay.layer addSublayer:shapeLayer];   // <- change here !!!
        }
        self.path = [[UIBezierPath alloc] init];
        [path moveToPoint:location];
    }
    else if (gesture.state == UIGestureRecognizerStateChanged)
    {
        [path addLineToPoint:location];
        shapeLayer.path = [path CGPath];
    }
    else if (gesture.state == UIGestureRecognizerStateEnded)
    {
        /*
         * This code is the same as what you already have ...
         */

             // But replace this next line with the following line ...
             //CGPoint loc = [_mapView convertCoordinate:coords toPointToView:self];
             CGPoint loc = [_mapView convertCoordinate:coords toPointToView: self.pathOverlay];

        /*
         * And again use the rest of your original code
         */            
    }
}

我还添加了一个ivar(和属性)来实现新的透明层:

UIView* pathOverlay;

我使用一个虚假的站点网络进行测试,得到了以下结果:

enter image description here

顺便提一下,我建议您摆脱static变量。将它们设置为类的ivars/属性即可。


我做了这个,现在甚至看不到地图上绘制的图层。虽然它确实检查了正确的注释并显示它们。 - Ashutosh
如果你想要更安全,只需将 pathOverlay 的四行初始化代码添加到你的视图控制器的 viewDidLoad: 方法中即可。我之前的实现方式是为了实现懒加载并减少需要展示的代码量。但是,你也可以在整个视图层次结构加载时初始化 pathOverlay。另外,如我之前所说,请确保 pathOverlay 是一个属性,而不仅仅是一个ivar。如果你想要更加安全,请确保它被保留(@property (nonatomic, strong) UIView* pathOverlay;,或者对于非ARC代码,使用@property (nonatomic, retain) UIView* pathOverlay;)。 - Nate
@Ashutosh,听起来你对属性的概念还不是很清楚。属性与类相关联。读/写属性基本上是一个设置和一个获取方法,它们包装了一个实例变量(ivar)。我没有包含设置属性所需的所有代码,因为假定你知道如何做。通常需要在头文件中声明它作为一个ivar,使用@property声明,然后在.m文件中使用@synthesize合成它。正如我所说,它也应该是保留strong - Nate
这并不是一个学习Objective-C属性的好论坛。我建议你只需在谷歌上搜索Objective-C入门指南,网上有大量相关资料。 - Nate
@Ashutosh,如果你仍然在这个问题上遇到困难,我建议你发布一个新的问题。我认为现在的问题与地图或贝塞尔路径无关。我认为这只是一个简单的实现/分配/保留属性的问题。只需添加一个新问题,展示如何声明你的ivar和property,如何分配它,然后说它在稍后的某个时刻仍然是“nil”。任何数量的iOS开发人员都应该能够快速回答。祝你好运。 - Nate
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接