Linux 子类化 MKAnnotationView 并覆盖 setDragState

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3677166/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 23:22:15  来源:igfitidea点击:

Subclassing MKAnnotationView and overriding setDragState

iphonecocoa-touchios4mapkit

提问by Ben Zwak

This is about an iPhone App using MKMapKit:

这是关于使用 MKMapKit 的 iPhone 应用程序:

I created a custom MKAnnotationView for a draggable Annotation. I want to create a custom animation. I set a custom pin image and the annotation is draggable (which both is not shown here, it happens in the mapview) with the following code:

我为可拖动的注释创建了一个自定义的 MKAnnotationView。我想创建一个自定义动画。我使用以下代码设置了一个自定义图钉图像并且注释是可拖动的(这里没有显示,它发生在地图视图中):

- (void) movePinUpFinished {

     [super setDragState:MKAnnotationViewDragStateDragging];
     [self setDragState:MKAnnotationViewDragStateDragging];
}

- (void) setDragState:(MKAnnotationViewDragState) myState {
     if (myState == MKAnnotationViewDragStateStarting) {
          NSLog(@"starting");
          CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20);
          self.center = endPoint;
          [self movePinUpFinished];
     }
     if (myState == MKAnnotationViewDragStateEnding) {
          NSLog(@"ending");
          [super setDragState:MKAnnotationViewDragStateEnding];
          [self setDragState:MKAnnotationViewDragStateNone];
          [super setDragState:MKAnnotationViewDragStateNone];
     }
     if (myState == MKAnnotationViewDragStateDragging) {
          NSLog(@"dragging");
     }
     if (myState == MKAnnotationViewDragStateCanceling) {
          NSLog(@"cancel");
     }
     if (myState == MKAnnotationViewDragStateNone) {
          NSLog(@"none");
     }
}

Everything works fine, the annotation is moved up a bit, is draggable and when i release the annotation, the mapview receives the "dragstateending".

一切正常,注释向上移动一点,可拖动,当我释放注释时,地图视图收到“dragstateending”。

But now I want the animation to run over a time period and change the dragStateStarting to the following:

但现在我希望动画运行一段时间并将 dragStateStarting 更改为以下内容:

if (myState == MKAnnotationViewDragStateStarting) {
          NSLog(@"starting");
          CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20);
          [UIView animateWithDuration:1.0
           animations:^{ self.center = endPoint; }
           completion:^(BOOL finished){ [self movePinUpFinished]; }];
     }

The animations runs as wanted over the period of a second and the annotation is draggable. But when I release the annotation, the mapview is not receiving the ending through the delegat. What I also recognized was that when I am doing the animation with "UIView animateWithDuration..." is that immedently after beginning the dragging, as the animation starts, the ballon of the annotation opens. When i am setting the new center without the animation, the balloon keeps closed and is only opened after finishing the dragging by releasing the annotation.

动画在一秒钟内按需要运行,并且注释是可拖动的。但是当我发布注释时,地图视图没有通过委托接收结尾。我还认识到,当我使用“UIView animateWithDuration...”做动画时,在开始拖动后立即,随着动画开始,注释的气球打开。当我在没有动画的情况下设置新中心时,气球保持关闭状态,只有在通过释放注释完成拖动后才会打开。

What am I doing wrong? Is this the right way to override setDragState. Do I really have to call the super class? But without setting the dragstate in the superclass my mapview didnt realized the changes of the dragstate.

我究竟做错了什么?这是覆盖 setDragState 的正确方法吗?我真的必须打电话给超级班吗?但是没有在超类中设置dragstate,我的mapview没有意识到dragstate的变化。

I wonder about the original implementation of MKPinAnnotationView, but because it is an internal Class I couldn't find a description of the setDragState method.

我想知道 MKPinAnnotationView 的原始实现,但因为它是一个内部类,我找不到 setDragState 方法的描述。

Thx for help. Cheers,

谢谢你的帮助。干杯,

Ben

采纳答案by Brian Johnson

I had the pin drag working but was trying to figure out why the pin annimations that occur when you don't override setDragState - no longer work in my implementation. Your question contained my answer .. Thanks!

我让引脚拖动工作但试图弄清楚为什么当你不覆盖 setDragState 时发生的引脚动画 - 在我的实现中不再起作用。你的问题包含了我的答案..谢谢!

Part of the problem with your code is that once you override the setDragState function, per the xcode documentation, you are responsible for updating the dragState variable based on the new state coming in. I would also be a little concerned about your code calling itself (setDragState calling [self setDragState]).

您的代码的部分问题是,一旦您覆盖了 setDragState 函数,根据 xcode 文档,您就有责任根据进入的新状态更新 dragState 变量。我也有点担心您的代码调用自身( setDragState 调用 [self setDragState])。

Here is the code I ended up (with your help) that does all of the lifts, drags and drops as I expect them to occur. Hope this helps you too!

这是我最终(在您的帮助下)完成的代码,它按照我期望的方式执行所有提升、拖放操作。希望这对你也有帮助!

- (void)setDragState:(MKAnnotationViewDragState)newDragState animated:(BOOL)animated
{
    if (newDragState == MKAnnotationViewDragStateStarting)
    {
        // lift the pin and set the state to dragging

        CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20);
        [UIView animateWithDuration:0.2
                         animations:^{ self.center = endPoint; }
                         completion:^(BOOL finished)
                             { self.dragState = MKAnnotationViewDragStateDragging; }];
    }
    else if (newDragState == MKAnnotationViewDragStateEnding)
    {
        // save the new location, drop the pin, and set state to none

        /* my app specific code to save the new position
        objectObservations[ACTIVE].latitude = pinAnnotation.coordinate.latitude;
        objectObservations[ACTIVE].longitude = pinAnnotation.coordinate.longitude;
        posChanged = TRUE;
        */

        CGPoint endPoint = CGPointMake(self.center.x,self.center.y+20);
        [UIView animateWithDuration:0.2
                         animations:^{ self.center = endPoint; }
                         completion:^(BOOL finished)
                             { self.dragState = MKAnnotationViewDragStateNone; }];
    }
    else if (newDragState == MKAnnotationViewDragStateCanceling)
    {
        // drop the pin and set the state to none

        CGPoint endPoint = CGPointMake(self.center.x,self.center.y+20);
        [UIView animateWithDuration:0.2
                         animations:^{ self.center = endPoint; }
                         completion:^(BOOL finished)
                             { self.dragState = MKAnnotationViewDragStateNone; }];
    }
}

回答by JakubM

I didn't study Ben's code much but it didn't worked for me. So I tried Brian's and it works great. Thanks a lot! I've been trying to solve annotation's animation during drag'n'drop for a long time.

我没有过多研究 Ben 的代码,但它对我没有用。所以我尝试了布赖恩的,效果很好。非常感谢!长期以来,我一直在尝试解决拖放过程中注释的动画问题。

But I have one suggestion to Brian's solution. I think that it would be better to support MKMapKit's delegate and notify it about changing dragState and save new position within standard delegate's method: - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState. Here's my code:

但我对布赖恩的解决方案有一个建议。我认为,这将是更好地支持MKMapKit的委托,并通知其有关更改dragState和保存标准委托的方法中的新位置:- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState。这是我的代码:

DraggableAnnotationView.h:

DraggableAnnotationView.h:

#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>

@interface DraggableAnnotationView : MKAnnotationView 
{
    id <MKMapViewDelegate> delegate;
    MKAnnotationViewDragState dragState;
}

@property (nonatomic, assign) id <MKMapViewDelegate> delegate;
@property (nonatomic, assign) MKAnnotationViewDragState dragState;
@property (nonatomic, assign) MKMapView *mapView;

@end

DraggableAnnotationView.m:

DraggableAnnotationView.m:

#import "DraggableAnnotationView.h"
#import "MapAnnotation.h"

@implementation DraggableAnnotationView
@synthesize delegate, dragState, mapView;



- (void)setDragState:(MKAnnotationViewDragState)newDragState animated:(BOOL)animated
{
    [delegate mapView:mapView annotationView:self didChangeDragState:newDragState fromOldState:dragState];

    if (newDragState == MKAnnotationViewDragStateStarting) {
        // lift the pin and set the state to dragging
        CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20);
        [UIView animateWithDuration:0.2
                         animations:^{ self.center = endPoint; }
                         completion:^(BOOL finished)
         { dragState = MKAnnotationViewDragStateDragging; }];
    } else if (newDragState == MKAnnotationViewDragStateEnding) {
        // drop the pin, and set state to none

        CGPoint endPoint = CGPointMake(self.center.x,self.center.y+20);
        [UIView animateWithDuration:0.2
                         animations:^{ self.center = endPoint; }
                         completion:^(BOOL finished)
         { dragState = MKAnnotationViewDragStateNone; }];
    } else if (newDragState == MKAnnotationViewDragStateCanceling) {
        // drop the pin and set the state to none

        CGPoint endPoint = CGPointMake(self.center.x,self.center.y+20);
        [UIView animateWithDuration:0.2
                         animations:^{ self.center = endPoint; }
                         completion:^(BOOL finished)
         { dragState = MKAnnotationViewDragStateNone; }];
    }
}

- (void)dealloc 
{
    [super dealloc];
}

@end

回答by Daniel

While Brian's solutionworked, it lacked taking into account the users finger blocking the annotation view which is being manipulated.

虽然Brian 的解决方案有效,但它没有考虑到用户手指阻挡了正在操纵的注释视图。

This means that the user could not precisely place the pin once he was dragging it. The standard MKPinAnnotationViewdoes a great job at this, what happens is when the finger begins dragging, the pin is lifted above the finger, and the visual point of the pin is used for placement not the previous centre point which now resides under the finger.

这意味着用户一旦拖动它就无法精确地放置它。该标准MKPinAnnotationView在这方面做得很好,当手指开始拖动时会发生什么,引脚被抬起到手指上方,并且引脚的视觉点用于放置,而不是现在位于手指下方的先前中心点。

In addition to this my implementation also adds another animation when dropping the pin after dragging, by lifting the pin and dropping it with a higher speed. This is very close the the native user experience and will be apreciated by your users.

除此之外,我的实现还在拖动后放下引脚时添加了另一个动画,方法是抬起引脚并以更高的速度放下它。这非常接近本机用户体验,并且会受到用户的赞赏。

Please check out my gist on GitHub for the code.

请查看我在 GitHub 上的要点以获取代码

What's really cool is setting a delegate is optional, optionally a notification is sent when the annotation view is dropped back onto the map.

真正酷的是设置委托是可选的,当注释视图放回地图时,可以选择发送通知。