【问题标题】:Updating and organizing overlays on an MKMapView在 MKMapView 上更新和组织覆盖
【发布时间】:2013-05-11 20:49:09
【问题描述】:

我想在 MKMapView 上进行叠加。所以我创建了我的自定义对象:

@interface Spot : NSObject

@property int spot_id;
@property CLLocationCoordinate2D coordinate;
@property float radius;
@property NSObject<MKOverlay> * overlay;

- (id) initWithSpotId:(int)spot_id position:(CLLocationCoordinate2D)coordinate andRadius:(float)radius;

@end

有一个后端,可以不断更新 Spot 并将它们传送到手机。所以我必须:

  • 检查当前渲染的点是否仍然有效并在那里
  • 如果是,它们是否移动或改变了半径?如果是这样,更新它们(移动?/删除+重绘?!)
  • 如果它们不再存在则删除。

是否有更好的方法来跟踪叠加层?将NSObject&lt;MKoverlay&gt; * 引用附加到每个位置并不断重新分配对我来说感觉有点奇怪。

【问题讨论】:

    标签: ios objective-c map overlay


    【解决方案1】:

    这里好像有三个问题:

    1. 如何检测服务器中的点变化?

      这样做的丑陋方法是遍历您的位置,看看coordinate 和/或radius 是否发生了变化。更好的做法是让服务器更新创建、修改和删除时间戳或其他标识符,以便客户端能够检索自上次更新以来的所有创建、修改和/或删除。最好的办法是将其与某种形式的推送通知结合起来,这样客户端也会主动收到这些更改的通知。

      这个问题很难抽象地回答。这在很大程度上取决于您的服务器的功能和数据库的性质(例如,有多少“点”,它们更改的频率等)。这不仅会影响客户端-服务器架构,还会影响客户端实现。

    2. 地点变化时如何更新地图?

      这是一个容易得多的问题。插入和删除很容易。您只需执行 addOverlay: (或者,在 iOS 7 中,addOverlay:level:removeOverlay:。对于更新,虽然它不优雅,但我认为最简单的方法是删除旧的覆盖并将其重新添加,viewForOverlay 将为您处理好用户界面。

    3. Spot 类的正确结构是什么?

      就像一个想法,但是拥有coordinateradius 属性,然后也拥有id&lt;MKOverlay&gt; overlay 对象似乎是重复的(因为这可能是具有相同两个属性的MKCircle)。如果您的叠加层将是 MKCircle 对象,那么只拥有一个 Spot 类本身可能更容易符合 MKOverlay

      @interface Spot : NSObject <MKOverlay>
      
      @property (nonatomic) int spot_id;
      @property (nonatomic) CLLocationCoordinate2D coordinate;
      @property (nonatomic) CLLocationDistance radius;
      @property (nonatomic, readonly) MKMapRect boundingMapRect;
      
      - (id) initWithSpotId:(int)spot_id position:(CLLocationCoordinate2D)coordinate andRadius:(CLLocationDistance)radius;
      
      @end
      

      那么,你需要做的就是实现boundingMapRectintersectsMapRect

      - (MKMapRect) boundingMapRect
      {
          MKMapPoint point = MKMapPointForCoordinate(self.coordinate);
          CLLocationDistance distance = self.radius * MKMetersPerMapPointAtLatitude(self.coordinate.latitude);
          MKMapRect rect = MKMapRectMake(point.x, point.y, distance * 2.0, distance * 2.0);
          rect = MKMapRectOffset(rect, -distance, -distance);
      
          return rect;
      }
      
      - (BOOL)intersectsMapRect:(MKMapRect)mapRect
      {
          return MKMapRectIntersectsRect(mapRect, [self boundingMapRect]);
      }
      

      您可能想仔细检查 boundingMapRect 的逻辑,但我认为它是正确的。

      然后您可以添加和删除 Spot 对象作为覆盖本身。您需要做的就是在您的MKMapViewDelegate 中实现viewForOverlay,例如,在iOS 7 之前的版本中,这将是:

      - (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
      {
          if ([overlay isKindOfClass:[Spot class]])
          {
              Spot *spot       = (id)overlay;
              MKCircle *circle = [MKCircle circleWithCenterCoordinate:spot.coordinate
                                                               radius:spot.radius];
      
              MKCircleView *overlayView = [[MKCircleView alloc] initWithCircle:circle];
              overlayView.fillColor     = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
              overlayView.strokeColor   = [[UIColor blueColor] colorWithAlphaComponent:0.7];
              overlayView.lineWidth     = 3.0;
              return overlayView;
          }
      
          return nil;
      }
      

      在 iOS 7 中,这将是:

      - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
      {
          if ([overlay isKindOfClass:[Spot class]])
          {
              Spot *spot       = (id)overlay;
              MKCircle *circle = [MKCircle circleWithCenterCoordinate:spot.coordinate
                                                               radius:spot.radius];
      
              MKCircleRenderer *renderer = [[MKCircleRenderer alloc] initWithCircle:circle];
              renderer.fillColor         = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
              renderer.strokeColor       = [[UIColor blueColor] colorWithAlphaComponent:0.7];
              renderer.lineWidth         = 3;
      
              return renderer;
          }
      
          return nil;
      }
      

      另一种方法是将您的Spot 定义为:

      @interface Spot : NSObject <MKOverlay>
      
      @property (nonatomic) int spot_id;
      @property (nonatomic, strong) MKCircle *overlay;
      
      - (id) initWithSpotId:(int)spot_id position:(CLLocationCoordinate2D)coordinate andRadius:(CLLocationDistance)radius;
      

      然后您可以定义 boundingMapRectcoordinateMKCircle 返回适当的值(让您不必自己编写):

      - (MKMapRect)boundingMapRect
      {
          return [self.circle boundingMapRect];
      }
      
      - (CLLocationCoordinate2D)coordinate
      {
          return [self.circle coordinate];
      }
      

      显然init 方法会改变:

      - (id) initWithSpotId:(int)spot_id position:(CLLocationCoordinate2D)coordinate andRadius:(CLLocationDistance)radius;
      {
          self = [super init];
          if (self) {
              _spot_id = spot_id;
              _circle = [MKCircle circleWithCenterCoordinate:coordinate radius:radius];
          }
          return self;
      }
      

      MKMapViewDelegate 中的 viewForOverlay(iOS 版本 7.0 之前的版本)也是如此:

      - (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
      {
          if ([overlay isKindOfClass:[SpotCircle class]])
          {
              SpotCircle *spot = (id)overlay;
      
              MKCircleView *overlayView = [[MKCircleView alloc] initWithCircle:spot.circle];
              overlayView.fillColor     = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
              overlayView.strokeColor   = [[UIColor blueColor] colorWithAlphaComponent:0.7];
              overlayView.lineWidth     = 3.0;
      
              return overlayView;
          }
      
          return nil;
      }
      

      在 iOS 7 中,这将是:

      - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
      {
          if ([overlay isKindOfClass:[SpotCircle class]])
          {
              SpotCircle *spot = (id)overlay;
      
              MKCircleRenderer *renderer = [[MKCircleRenderer alloc] initWithCircle:spot.circle];
              renderer.fillColor         = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
              renderer.strokeColor       = [[UIColor blueColor] colorWithAlphaComponent:0.7];
              renderer.lineWidth         = 3;
      
              return renderer;
          }
      
          return nil;
      }
      

    希望对你有帮助。

    【讨论】:

    • 感谢不同的想法和示例代码!你没看错,我需要一个 MKCircle。我现在正在做的是:@interface Spot : MKCircle。这真的很容易。删除和添加也可以。服务器通知仍在进行中,因此也感谢那里的提示!
    • @kadrian 我不建议这样做,因为MKCircle 是不可变的,所以如果您的位置发生变化,您必须丢弃旧的Spot,然后创建一个新的。它有效,但从数据建模的角度来看感觉不对。但如果这对你有用,那就太好了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多