相对于缩放级别缩放 MKMapView 注释

发布于 2024-09-02 17:24:01 字数 3000 浏览 2 评论 0原文

问题 我正在尝试围绕注释创建一个视觉半径圆,该圆实际上保持固定大小。例如。因此,如果我将半径设置为 100m,当您缩小地图视图时,半径圆会逐渐变小。

我已经能够实现缩放,但是当用户操纵视图时,半径矩形/圆似乎会“抖动”远离图钉地标。

我相信这在即将推出的 iPhone OS 4 上更容易实现,但我的应用程序需要支持 3.0。

表现 这是该行为的视频

实施 注释以通常的方式添加到 Mapview 中,并且我在 UIViewController 子类 (MapViewController) 上使用了委托方法来查看区域何时发生变化。

-(void)mapView:(MKMapView *)pMapView regionDidChangeAnimated:(BOOL)animated{

//Get the map view
MKCoordinateRegion region;
CGRect rect;

//Scale the annotations
for( id<MKAnnotation> annotation in [[self mapView] annotations] ){

    if( [annotation isKindOfClass: [Location class]] && [annotation conformsToProtocol:@protocol(MKAnnotation)] ){
        //Approximately 200 m radius
        region.span.latitudeDelta = 0.002f;
        region.span.longitudeDelta = 0.002f;

        region.center = [annotation coordinate];

        rect = [[self mapView] convertRegion:region toRectToView: self.mapView];

        if( [[[self mapView] viewForAnnotation: annotation] respondsToSelector:@selector(setRadiusFrame:)] ){

            [[[self mapView] viewForAnnotation: annotation] setRadiusFrame:rect];

        }

    }

}

Annotation 对象 (LocationAnnotationView) 是 MKAnnotationView 的子类,它的 setRadiusFrame 看起来像这样

-(void) setRadiusFrame:(CGRect) rect{

CGPoint centerPoint;

//Invert
centerPoint.x = (rect.size.width/2) * -1;
centerPoint.y = 0 + 55 + ((rect.size.height/2) * -1);

rect.origin = centerPoint;

[self.radiusView setFrame:rect];
}

最后,radiusView 对象是 UIView 的子类,它重写了 drawRect 方法来绘制半透明圆圈。 setFrame 在此 UIView 子类中也被重写,但除了 [UIView setFrame:] 之外,它仅用于调用 [UIView setNeedsDisplay] 以确保在更新框架后重新绘制视图。

radiusView 对象的(CircleView)drawRect 方法如下所示

-(void) drawRect:(CGRect)rect{

//NSLog(@"[CircleView drawRect]");

[self setBackgroundColor:[UIColor clearColor]];
//Declarations
CGContextRef context;
CGMutablePathRef path;

//Assignments
context = UIGraphicsGetCurrentContext();

path = CGPathCreateMutable();

//Alter the rect so the circle isn't cliped

//Calculate the biggest size circle
if( rect.size.height > rect.size.width ){
    rect.size.height = rect.size.width;
}
else if( rect.size.height < rect.size.width ){
    rect.size.width = rect.size.height;
}

rect.size.height -= 4;
rect.size.width  -= 4;
rect.origin.x += 2;
rect.origin.y += 2;

//Create paths
CGPathAddEllipseInRect(path, NULL, rect );

//Create colors
[[self areaColor] setFill];

CGContextAddPath( context, path);
CGContextFillPath( context );

[[self borderColor] setStroke];

CGContextSetLineWidth( context, 2.0f );
CGContextSetLineCap(context, kCGLineCapSquare);
CGContextAddPath(context, path );
CGContextStrokePath( context );
CGPathRelease( path );

//CGContextRestoreGState( context );

}

感谢您的耐心配合,我们将不胜感激。 乔纳森

The Problem
I'm trying to create a visual radius circle around a annonation, that remains at a fixed size in real terms. Eg. So If i set the radius to 100m, as you zoom out of the Map view the radius circle gets progressively smaller.

I've been able to achieve the scaling, however the radius rect/circle seems to "Jitter" away from the Pin Placemark as the user manipulates the view.

I'm lead to believe this is much easier to achieve on the forthcoming iPhone OS 4, however my application needs to support 3.0.

The Manifestation
Here is a video of the behaviour.

The Implementation
The annotations are added to the Mapview in the usual fashion, and i've used the delegate method on my UIViewController Subclass (MapViewController) to see when the region changes.

-(void)mapView:(MKMapView *)pMapView regionDidChangeAnimated:(BOOL)animated{

//Get the map view
MKCoordinateRegion region;
CGRect rect;

//Scale the annotations
for( id<MKAnnotation> annotation in [[self mapView] annotations] ){

    if( [annotation isKindOfClass: [Location class]] && [annotation conformsToProtocol:@protocol(MKAnnotation)] ){
        //Approximately 200 m radius
        region.span.latitudeDelta = 0.002f;
        region.span.longitudeDelta = 0.002f;

        region.center = [annotation coordinate];

        rect = [[self mapView] convertRegion:region toRectToView: self.mapView];

        if( [[[self mapView] viewForAnnotation: annotation] respondsToSelector:@selector(setRadiusFrame:)] ){

            [[[self mapView] viewForAnnotation: annotation] setRadiusFrame:rect];

        }

    }

}

The Annotation object (LocationAnnotationView)is a subclass of the MKAnnotationView and it's setRadiusFrame looks like this

-(void) setRadiusFrame:(CGRect) rect{

CGPoint centerPoint;

//Invert
centerPoint.x = (rect.size.width/2) * -1;
centerPoint.y = 0 + 55 + ((rect.size.height/2) * -1);

rect.origin = centerPoint;

[self.radiusView setFrame:rect];
}

And finally the radiusView object is a subclass of a UIView, that overrides the drawRect method to draw the translucent circles. setFrame is also over ridden in this UIView subclass, but it only serves to call [UIView setNeedsDisplay] in addition to [UIView setFrame:] to ensure that the view is redrawn after the frame has been updated.

The radiusView object's (CircleView) drawRect method looks like this

-(void) drawRect:(CGRect)rect{

//NSLog(@"[CircleView drawRect]");

[self setBackgroundColor:[UIColor clearColor]];
//Declarations
CGContextRef context;
CGMutablePathRef path;

//Assignments
context = UIGraphicsGetCurrentContext();

path = CGPathCreateMutable();

//Alter the rect so the circle isn't cliped

//Calculate the biggest size circle
if( rect.size.height > rect.size.width ){
    rect.size.height = rect.size.width;
}
else if( rect.size.height < rect.size.width ){
    rect.size.width = rect.size.height;
}

rect.size.height -= 4;
rect.size.width  -= 4;
rect.origin.x += 2;
rect.origin.y += 2;

//Create paths
CGPathAddEllipseInRect(path, NULL, rect );

//Create colors
[[self areaColor] setFill];

CGContextAddPath( context, path);
CGContextFillPath( context );

[[self borderColor] setStroke];

CGContextSetLineWidth( context, 2.0f );
CGContextSetLineCap(context, kCGLineCapSquare);
CGContextAddPath(context, path );
CGContextStrokePath( context );
CGPathRelease( path );

//CGContextRestoreGState( context );

}

Thanks for bearing with me, any help is appreciated.
Jonathan

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

风启觞 2024-09-09 17:24:01

首先,第一个函数中的 foo 使用了什么?我假设 RadiusView 的父级是注释视图,对吗?

“抖动”

另外,radiusView 的中心点应该与annotationView 的中心点重合。这应该可以解决您的问题:

-(void) setRadiusFrame:(CGRect)rect{
    rect.origin.x -= 0.5*(self.frame.size.width - rect.size.width);
    rect.origin.y -= 0.5*(self.frame.size.height - rect.size.height);
    [self.radiusView setFrame:rect]
}

不必要的方法

您可以直接在radiusView上设置框架并避免上述计算:

    UIView * radiusView = [[[self mapView] viewForAnnotation: annotation] radiusView];
    rect = [[self mapView] convertRegion:foo toRectToView: radiusView.superView];
    [radiusView setFrame:rect];
  1. 当绘制椭圆时,不要使用传递给的rect drawRect:,它不必与框架相同。直接使用 self.frame 更安全

不必要的视图

现在,如果您需要使用上面的层次结构,我给出了以上几点,但我不明白为什么不直接在LocationAnnotationView?毕竟它就是为了这个目的而存在的。这样做的方法如下:

  1. 缩放时,直接更改annotationView的矩形:

    rect = [[self mapView] ConvertRegion:foo toRectToView: self.mapView];
    [[[self mapView] viewForAnnotation: 注释] setFrame:rect];
    
  2. drawRect:的实现移动到LocationAnnotationView

这更容易实现,并且应该可以解决您的问题,因为注释视图的中心点随您的图钉移动,您不应该看到这个问题。

修复

代码中还有另外两个问题:

  1. 像这样设置 longitudeDelta:

    region.span.longitudeDelta = 0.002*cos(region.center.latitude*pi/180.0);
    

    转换为米的经度增量随纬度变化。或者,您可以只设置纬度增量,然后修改矩形,使其变为矩形 (width==height)。

  2. drawRect:中,不要使用传递的rect;而是使用 self.frame。不能保证它们是相同的,并且 rect 可以有任何值。

让我知道这些是否有效;-)

First, what's foo used in the first function? And I'm assuming radiusView's parent is the annotation view, right?

The "Jittering"

Also, the center point of radiusView should coincide with that of the annotationView. This should solve your problem:

-(void) setRadiusFrame:(CGRect)rect{
    rect.origin.x -= 0.5*(self.frame.size.width - rect.size.width);
    rect.origin.y -= 0.5*(self.frame.size.height - rect.size.height);
    [self.radiusView setFrame:rect]
}

Unnecessary method

You could set the frame directly on the radiusView and avoid the above calculation:

    UIView * radiusView = [[[self mapView] viewForAnnotation: annotation] radiusView];
    rect = [[self mapView] convertRegion:foo toRectToView: radiusView.superView];
    [radiusView setFrame:rect];
  1. When drawing the ellipse, don't use the rect passed to drawRect:, it doesn't have to be the same as the frame. It's safer to directly use self.frame

Unnecessary view

Now I gave the above points if you need to use the above hierarchy, but I don't see why don't you just draw your ellipses directly in the LocationAnnotationView? It's there for this purpose after all. This is how you do this:

  1. when scaling, change the annotationView's rect directly:

    rect = [[self mapView] convertRegion:foo toRectToView: self.mapView];
    [[[self mapView] viewForAnnotation: annotation] setFrame:rect];
    
  2. Move the implementation of drawRect: to LocationAnnotationView.

This is easier to implement, and should address your problem as the center point of the annotation view moves with your pin and you shouldn't see this problem.

Fixes

There are two other issues in the code:

  1. Set longitudeDelta like this:

    region.span.longitudeDelta = 0.002*cos(region.center.latitude*pi/180.0);
    

    as the longitude delta converted to meters changes with the latitude. Alternatively, you could only set latitude delta, then modify the rect so it becomes rectangular (width==height).

  2. in drawRect:, don't use the passed rect; instead use self.frame. There's no guarantee that these are the same, and rect could have any value.

Let me know if these work ;-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文