简体   繁体   English

防止MKMapView改变选择(干净地)

[英]Preventing MKMapView changing selection (cleanly)

I have a custom subclass of MKPinAnnotationView that displays a custom call out. 我有一个MKPinAnnotationView的自定义子类,显示自定义调用。 I want to handle touch events inside that annotation. 我想处理该注释中的触摸事件。

I have a working solution (below) but it just doesn't feel right. 我有一个有效的解决方案(下图),但感觉不对。 I have a rule of thumb that whenever I use performSelector: .. withDelay: I'm fighting the system rather than working with it. 我有一个经验法则,每当我使用performSelector: .. withDelay:我正在与系统作斗争而不是使用它。

Does anyone have a good, clean workaround for the aggressive event handling of MKMapView and annotation selection handling? 有没有人为MKMapView和注释选择处理的积极事件处理有一个良好,干净的解决方法?

My current solution: 我目前的解决方案

(All code from my annotation selection class) (我的注释选择类中的所有代码)

I do my own hit testing (without this my gesture recognisers don't fire as the Map View consumes the events: 我进行自己的热门测试(没有这个我的手势识别器不会触发,因为Map View会消耗这些事件:

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event; {
    // To enable our gesture recogniser to fire. we have to hit test and return the correct view for events inside the callout.

    UIView* hitView = nil;

    if (self.selected) {
        // check if we tpped inside the custom view
        if (CGRectContainsPoint(self.customView.frame, point))
            hitView = self.customView;
    }

    if(hitView) {
        // If we are performing a gesture recogniser (and hence want to consume the action)
        // we need to turn off selections for the annotation temporarily
        // the are re-enabled in the gesture recogniser.
        self.selectionEnabled = NO;

        // *1* The re-enable selection a moment later
        [self performSelector:@selector(enableAnnotationSelection) withObject:nil afterDelay:kAnnotationSelectionDelay];

    } else {
        // We didn't hit test so pass up the chain.
        hitView = [super hitTest:point withEvent:event];
    }

    return hitView;
}

Note that I also turn off selections so that in my overridden setSelected I can ignore the deselection. 请注意,我也关闭选择,以便在我重写的setSelected我可以忽略取消选择。

- (void)setSelected:(BOOL)selected animated:(BOOL)animated; {
    // If we have hit tested positive for one of our views with a gesture recogniser,  temporarily 
    // disable selections through _selectionEnabled
    if(!_selectionEnabled){
        // Note that from here one, we are out of sync with the enclosing map view
        // we're just displaying out callout even though it thinks we've been deselected
        return;
    }

    if(selected) {
        // deleted code to set up view here
        [self addSubview:_customView];

        _mainTapGestureRecogniser = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(calloutTapped:)];
        [_customView addGestureRecognizer: _mainTapGestureRecogniser];

    } else {
        self.selected = NO;
        [_customView removeFromSuperview];
    }


}

It's the line commented 1 that I don't like but it's also pretty hairy at the end of theta delayed fire. 这条线评论1 ,我不喜欢,但它在theta延迟火灾结束时也非常毛茸茸。 I have to walk back up the superview chain to get to the mapView so that I can convince it the selection is still in place. 我必须走回superview链才能到达mapView,以便我可以说服选择仍然存在。

// Locate the mapview so that we can ensure it has the correct annotation selection state after we have ignored selections.
- (void)enableAnnotationSelection {
    // This reenables the seelction state and resets the parent map view's idea of the
    // correct selection i.e. us.
    MKMapView* map = [self findMapView];
    [map selectAnnotation:self.annotation animated:NO];
    _selectionEnabled = YES;

}

with

-(MKMapView*)findMapView; {
    UIView* view = [self superview];
    while(!_mapView) {
        if([view isKindOfClass:[MKMapView class]]) {
            _mapView = (MKMapView*)view;
        } else if ([view isKindOfClass:[UIWindow class]]){
            return nil;
        } else{
            view = [view superview];
            if(!view)
                return nil;
        }
    }

    return _mapView;
}

This all seems to work without and downside (like flicker I've seen from other solutions. It's relatively straightforward but it doesn't feel right. 这一切似乎没有任何缺点和下行(就像我从其他解决方案中看到的闪烁一样。它相对简单,但感觉不对。

Anyone have a better solution? 谁有更好的解决方案?

I don't think you need to monkey with the map view's selection tracking. 我不认为你需要使用地图视图的选择跟踪。 If I'm correctly interpreting what you want, you should be able to accomplish it with just the hitTest:withEvent: override and canShowCallout set to NO . 如果我正确地解释了你想要的东西,你应该只用hitTest:withEvent: override和canShowCallout设置为NO来完成它。 In setSelected: perform your callout appearance/disappearance animation accordingly. setSelected:执行你的标注出现/消失动画。 You should also override setHighlighted: and adjust the display of your custom callout if visible. 您还应该覆盖setHighlighted:并调整自定义标注的显示(如果可见)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM