子类化 MKAnnotationView 并覆盖 setDragState

发布于 2024-09-18 09:33:38 字数 2002 浏览 1 评论 0原文

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

我为可拖动注释创建了一个自定义 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");
     }
}

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

但现在我希望动画运行一段时间,并将 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]; }];
     }

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

我做错了什么?这是重写 setDragState 的正确方法吗?我真的必须调用超级类吗?但是如果没有在超类中设置拖动状态,我的地图视图没有意识到拖动状态的变化。

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

谢谢帮助。干杯,

This is about an iPhone App using MKMapKit:

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:

- (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".

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

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.

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.

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.

Thx for help. Cheers,

Ben

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

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

发布评论

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

评论(3

无风消散 2024-09-25 09:33:39

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

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

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

- (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; }];
    }
}

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!

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]).

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; }];
    }
}
倾城泪 2024-09-25 09:33:39

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

这意味着用户一旦拖动图钉就无法精确放置图钉。标准的 MKPinAnnotationView 在这方面做得很好,当手指开始拖动时,图钉被提升到手指上方,并且图钉的视觉点用于放置,而不是之前的中心点现在位于手指之下。

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

请查看我的 GitHub 上的代码要点

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

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

This means that the user could not precisely place the pin once he was dragging it. The standard MKPinAnnotationView does 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.

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.

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.

冰魂雪魄 2024-09-25 09:33:39

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

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

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:

#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

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.

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:

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:

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