iOS MapKit拖动的批注(MKAnnotationView)不再随地图平移

iOS MapKit拖动的批注(MKAnnotationView)不再随地图平移,ios,properties,ios7,mapkit,key-value-observing,Ios,Properties,Ios7,Mapkit,Key Value Observing,我正在学习在我羽翼未丰的iOS应用程序中使用MapKit。我使用一些模型实体作为注释(将协议添加到它们的头文件)。我还创建了自定义的mkannotationview,并将draggable属性设置为YES 我的模型对象有一个location属性,它是CLLocation*。为了符合协议,我向该对象添加了以下内容: - (CLLocationCoordinate2D) coordinate { return self.location.coordinate; } - (void) set

我正在学习在我羽翼未丰的iOS应用程序中使用MapKit。我使用一些模型实体作为注释(将
协议添加到它们的头文件)。我还创建了自定义的mkannotationview,并将
draggable
属性设置为
YES

我的模型对象有一个
location
属性,它是
CLLocation*
。为了符合
协议,我向该对象添加了以下内容:

- (CLLocationCoordinate2D) coordinate {
    return self.location.coordinate;
}

- (void) setCoordinate:(CLLocationCoordinate2D)newCoordinate {
    CLLocation* newLocation = [[CLLocation alloc]
        initWithCoordinate: newCoordinate
        altitude: self.location.altitude
        horizontalAccuracy: self.location.horizontalAccuracy
        verticalAccuracy: self.location.verticalAccuracy
        timestamp: nil];
    self.location = newLocation;
}

- (NSString*) title {
    return self.name;
}

- (NSString*) subtitle {
    return self.serialID;
}
所以,我有4个必要的方法。而且它们非常简单。当我在
MKAnnotationView
@draggable
属性上阅读苹果文档时,它说:

如果将此属性设置为“是”,则用户可以拖动批注。如果是,则关联的注释对象还必须实现setCoordinate:方法。此属性的默认值为“否”

在其他地方,
MKAnnotation
docs说:

此属性的实现必须与键值观测(KVO)兼容。有关如何实现对KVO的支持的更多信息,请参阅《关键值观察编程指南》

我已经阅读了那个(简短的)文档,但我根本不清楚我应该做些什么来完成它,因此我从我的
位置
属性派生的
坐标
,本身就是一个正确的属性

但我有理由肯定它不能正常工作。当我拖动图钉时,它会移动,但当我平移地图时,它不再重新定位

更新

所以我试着玩股票MKPinAnnotationView。为此,我只需注释掉我的代理的
mapView:viewForAnnotation:
方法。我发现这些默认情况下是不可拖动的。我将
mapView:didAddAnnotationViews:
添加到我的代理中,以将添加视图的
Dragable
属性设置为
YES

这样配置之后,正如下面John Estropia所暗示的那样,Pin视图似乎可以正常工作。我决定使用
mapView:annotationView:didChangeDragState:fromOldState:
delegate钩子来更仔细地了解发生了什么:

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState {
    NSArray* states = @[@"None", @"Starting", @"Dragging", @"Cancelling", @"Ending"];
    NSLog(@"dragStateChangeFrom: %@ to: %@", states[oldState], states[newState]);
}
对于库存管脚,您将看到如下所示的日志输出:

2014-02-05 09:07:45.924 myValve[1781:60b] dragStateChangeFrom: None to: Starting
2014-02-05 09:07:46.249 myValve[1781:60b] dragStateChangeFrom: Starting to: Dragging
2014-02-05 09:07:47.601 myValve[1781:60b] dragStateChangeFrom: Dragging to: Ending
2014-02-05 09:07:48.006 myValve[1781:60b] dragStateChangeFrom: Ending to: None
这看起来很合乎逻辑。但如果切换到已配置的
MKAnnotationView
,您将看到的输出如下所示:

2014-02-05 09:09:41.389 myValve[1791:60b] dragStateChangeFrom: None to: Starting
2014-02-05 09:09:45.451 myValve[1791:60b] dragStateChangeFrom: Starting to: Ending
它错过了两个转换,从开始到拖动,从结束到无

所以我开始怀疑我是否需要对属性做一些不同的事情。但我仍然对为什么这行不通感到沮丧

更新2


我创建了自己的
注释
对象,使其位于模型对象之间,模型对象可以具有属性
坐标
属性。行为保持不变。这似乎与
MKAnnotationView

有关。您是否使用自定义地图pin?我以前也见过这个。似乎是iOS 7中的一个bug。作为一种解决方法,我们最终使用了
MKPinAnnotationView

的默认pin。关于如何使用委托方法
mapView:viewForAnnotation:
有很多示例,显示了如何设置
MKAnnotationView
。但不明显的是,仅仅因为您将
MKAnnotationView
实例的
draggable
属性设置为
YES
,您仍然需要编写一些代码来帮助它转换某些状态。MapKit将负责将实例的
dragState
移动到
MKAnnotationViewDragStateStarting
MKAnnotationViewDragStateEnding
,但不会执行其他转换。关于子类化
MKAnnotationView
以及重写“setDragState:animated:”的需要,您可以在文档注释中看到这方面的提示

当拖动状态更改为MKAnnotationViewDragStateStarting时,请将状态设置为mkAnnotationViewDragStateStaring。如果执行动画以指示拖动的开始,并且动画参数为“是”,请在更改状态之前执行该动画

当状态更改为MKAnnotationViewDragStateCanceling或MKAnnotationViewDragStateEnding时,请将状态设置为MKAnnotationViewDragStateNone。如果在拖动结束时执行动画,且动画参数为“是”,则应在更改状态之前执行该动画

在本例中,我没有子类化,但似乎
MKAnnotationView
仍在努力自行进行转换。因此,您必须实现委托的
mapView:annotationView:didChangeDragState:fromOldState:
方法。例如

- (void)mapView:(MKMapView *)mapView
        annotationView:(MKAnnotationView *)annotationView
        didChangeDragState:(MKAnnotationViewDragState)newState
        fromOldState:(MKAnnotationViewDragState)oldState {
    if (newState == MKAnnotationViewDragStateStarting) {
        annotationView.dragState = MKAnnotationViewDragStateDragging;
    }
    else if (newState == MKAnnotationViewDragStateEnding || newState == MKAnnotationViewDragStateCanceling) {
        annotationView.dragState = MKAnnotationViewDragStateNone;}
    }
}

这样可以适当地完成操作,以便在拖动注释后平移地图时,注释随平移一起移动。

您的设置坐标缺少所需的键值观察方法(willChangeValueForKey/didChangeValueForKey)地图需要检测何时移动注释,以便移动注释视图以匹配,例如

- (void) setCoordinate:(CLLocationCoordinate2D)newCoordinate {

    [self willChangeValueForKey:@"coordinate"];

    CLLocation* newLocation = [[CLLocation alloc]
        initWithCoordinate: newCoordinate
        altitude: self.location.altitude
        horizontalAccuracy: self.location.horizontalAccuracy
        verticalAccuracy: self.location.verticalAccuracy
        timestamp: nil];

    self.location = newLocation;

    [self didChangeValueForKey:@"coordinate"];
}
- (void) setCoordinate:(CLLocationCoordinate2D)newCoordinate {

    [self willChangeValueForKey:@"coordinate"];

    CLLocation* newLocation = [[CLLocation alloc]
        initWithCoordinate: newCoordinate
        altitude: self.location.altitude
        horizontalAccuracy: self.location.horizontalAccuracy
        verticalAccuracy: self.location.verticalAccuracy
        timestamp: nil];

    self.location = newLocation;

    [self didChangeValueForKey:@"coordinate"];
}
资料来源:


“此属性的实现必须符合键值观测(KVO)要求。有关如何实现对KVO的支持的更多信息,请参阅《键值观测编程指南》。

很抱歉,Apple在文档中没有明确说明,他们想说的是,setCoordinate需要关键值观察方法willChangeValueForKey&didChangeValueForKey,该方法允许地图检测注释何时被移动,以便它可以移动注释视图以匹配,例如

- (void) setCoordinate:(CLLocationCoordinate2D)newCoordinate {

    [self willChangeValueForKey:@"coordinate"];

    CLLocation* newLocation = [[CLLocation alloc]
        initWithCoordinate: newCoordinate
        altitude: self.location.altitude
        horizontalAccuracy: self.location.horizontalAccuracy
        verticalAccuracy: self.location.verticalAccuracy
        timestamp: nil];

    self.location = newLocation;

    [self didChangeValueForKey:@"coordinate"];
}
- (void) setCoordinate:(CLLocationCoordinate2D)newCoordinate {

    [self willChangeValueForKey:@"coordinate"];

    CLLocation* newLocation = [[CLLocation alloc]
        initWithCoordinate: newCoordinate
        altitude: self.location.altitude
        horizontalAccuracy: self.location.horizontalAccuracy
        verticalAccuracy: self.location.verticalAccuracy
        timestamp: nil];

    self.location = newLocation;

    [self didChangeValueForKey:@"coordinate"];
}

看看这可能的解决方案的要点:@Anna这帮了大忙,谢谢。我不是子类化,但基本上是一样的。我差点就成功了。我将发布我的解决方案,并提出一个新问题。有人能解释一下为什么这个答案没有被提升100次吗?这太重要了!谢谢你(100次)。你救了我的命!此解决方案适用于MKPinAnnotation