如何在自定义UIView下面的MKMapview中启用touchEvents(滚动和平移)?

时间:2009-09-28 06:19:39

标签: iphone uiview mkmapview

alt text http://www.gisnotes.com/wordpress/wp-content/uploads/2009/09/poly.png

简而言之,我试图弄清楚如何缩放在MKMapView(mapView)之上的自定义视图(geometryView)中实现的几何体(点,线和多边形)。

我做的是......

  1. 创建DrawMapViewController。在底部工具栏上添加UIBarButtonItems(MAP,PT,LN,PG)。

  2. 单击地图按钮后,您可以平移/缩放地图。这样可以通过设置geometryView.hidden = YES;

  3. 来启用mapView
  4. 单击三个几何按钮中的任何一个时,geometryView会显示geometryView.hidden = NO,因此,启用touchEvents并从GeometryView.drawRect的方法中绘制几何图形。

  5. 图层排序如下:mapView位于geometryView的底部。

    -geometryView

    -mapView

    我的问题是什么? 理想情况下,在“地图”模式下以及当用户进行平移和缩放时,我希望是否可以从geometryView显示绘图。但是当用户点击“map”时,geometryView.hidden = YES,因此绘图消失。如果我使geometryView可见,那么用户与geometryView交互而不是mapView,因此没有缩放和平移。

    在显示自定义视图时,是否可以在自定义视图下方处理MKMapView的touchEvents(平移/缩放)?非常感谢任何其他想法/方法。

    谢谢,

    鲁珀特

    GeometryView列表:

    @synthesize mapview, pinFactory;
    
    - (id)initWithFrame:(CGRect)frame{
        if (self = [super initWithFrame:frame]) {
        }
        return self;
    }
    
    - (void)drawRect:(CGRect)rect {
        // Drawing code
        NSLog(@"DrawRect called");
    
        CGContextRef context = UIGraphicsGetCurrentContext();
    
        // Drawing lines with a white stroke color
        CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
        // Draw them with a 2.0 stroke width so they are a bit more visible.
        CGContextSetLineWidth(context, 2.0);
    
        if(pinFactory.geometryState == 2){  //Draw Line
    
            if( [pinFactory actualPinCount] > 1){
    
                Pin *pin1 = (Pin *)[[pinFactory pinArray] objectAtIndex:0];
                CGPoint pt1 = pin1.touchLocation;
                CGContextMoveToPoint(context, pt1.x, pt1.y);
    
                for (int i = 1; i < ([pinFactory actualPinCount]); i++)
                {
                    Pin *pin2 = (Pin *)[[pinFactory pinArray] objectAtIndex:i];
                    CGPoint pt2 = pin2.touchLocation;
                    CGContextAddLineToPoint(context, pt2.x, pt2.y);
                }
    
                CGContextStrokePath(context);
            }
        }
        else if(pinFactory.geometryState == 3){ //Draw Polygon
            //if there are two points, draw a line first.
            //if there are three points, fill the polygon
            if( [pinFactory actualPinCount] == 2){
    
                Pin *pin1 = (Pin *)[[pinFactory pinArray] objectAtIndex:0];
                CGPoint pt1 = pin1.touchLocation;
                CGContextMoveToPoint(context, pt1.x, pt1.y);
    
                Pin *pin2 = (Pin *)[[pinFactory pinArray] objectAtIndex:1];
                CGPoint pt2 = pin2.touchLocation;
                CGContextAddLineToPoint(context, pt2.x, pt2.y);
    
                CGContextStrokePath(context);
            }
            else if([pinFactory actualPinCount] > 2){
    
                //fill with a blue color
                CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
    
                Pin *pin1 = (Pin *)[[pinFactory pinArray] objectAtIndex:0];
                CGPoint pt1 = pin1.touchLocation;
                CGContextMoveToPoint(context, pt1.x, pt1.y);
    
                for (int i = 1; i < ([pinFactory actualPinCount]); i++)
                {
    
                    Pin *pin2 = (Pin *)[[pinFactory pinArray] objectAtIndex:i];
                    CGPoint pt2 = pin2.touchLocation;
                    CGContextAddLineToPoint(context, pt2.x, pt2.y);
                }
    
                CGContextClosePath(context);
    
                CGContextDrawPath(context, kCGPathFillStroke);
            }
        }
    }
    
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
        [self setNeedsDisplay];
    
        UITouch* aTouch = [touches anyObject];
        location = [aTouch locationInView:self];
        NSLog(@"touchesBegan: x:%f, y:%f", location.x, location.y );
    
        CLLocationCoordinate2D coordinate = [mapview convertPoint:location toCoordinateFromView:self];
    
        switch (pinFactory.geometryState) {
            case 1:{
                if( [pinFactory actualPinCount] == 1){
                    //[UIView beginAnimations:@"stalk" context:nil];
                    //[UIView setAnimationDuration:1];
                    //[UIView setAnimationBeginsFromCurrentState:YES];
    
                    Pin *pin = (Pin *)[pinFactory getObjectAtIndex:0];
                    [mapview removeAnnotation:pin];
                    [pinFactory removeObject:pin];
    
                    Pin *newPin = [[Pin alloc] initWithCoordinate:coordinate initLocation:location withTitle:@"My Pin"];
                    [pinFactory addObject:newPin];
                    [mapview addAnnotation:newPin];
    
                    [newPin release];
    
                    //[UIView commitAnimations];
                }
                else{
                    //Lets add a new pin to the geometry
                    Pin *pin = [[Pin alloc] initWithCoordinate:coordinate initLocation:location withTitle:@"My Pin"];
                    [pinFactory addObject:pin];
                    [mapview addAnnotation:pin];
    
                    [pin release];
                }
                break;
            }
            case 2:{
                //Lets add a new pin
                Pin *pin = [[Pin alloc] initWithCoordinate:coordinate initLocation:location withTitle:@"My Pin"];
                [pinFactory addObject:pin];
                [mapview addAnnotation:pin];
    
                [pin release];
                [self setNeedsDisplay];
    
                break;
            }
            case 3:{
                //Lets add a new pin
                Pin *pin = [[Pin alloc] initWithCoordinate:coordinate initLocation:location withTitle:@"My Pin"];
                [pinFactory addObject:pin];
                [mapview addAnnotation:pin];
    
                [pin release];
                [self setNeedsDisplay];
    
                break;
            }
            default:
                break;
        }
    }
    
    
    - (void)dealloc {
        [super dealloc];
    }
    
    
    @end
    

    DrawMapViewController清单:

    #import "DrawMapViewController.h"
    #import "Pin.h"
    
    @implementation DrawMapViewController
    
    @synthesize mapview, mapBarButton, pointBarButton, lineBarButton, polygonBarButton, geometryView;
    
    /*  State represents state of the map
     *  0 = map
     *  1 = point
     *  2 = line
     *  3 = polygon
     */
    
    // The designated initializer. Override to perform setup that is required before the view is loaded.
    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
        if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
            // Custom initialization
            self.title = @"Map";
        }
        return self;
    }
    
    
    // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
    - (void)viewDidLoad {
        [super viewDidLoad];
        mapview.mapType = MKMapTypeSatellite;
    
    
        NSMutableArray *pinArray = [[NSMutableArray alloc] initWithObjects:nil];
        pinFactory = [[PinFactory alloc] initWithArray:pinArray]; 
        pinFactory.map = mapview;
        [pinArray release];
    
        geometryView = [[GeometryView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 372.0f)];
        geometryView.pinFactory = pinFactory;
        geometryView.mapview = mapview;
        geometryView.backgroundColor = [UIColor clearColor];
        [self.view addSubview:geometryView];
    
        [self changeButtonAndViewState:0];
    }
    
    
    /*
    // Override to allow orientations other than the default portrait orientation.
    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
        // Return YES for supported orientations
        return (interfaceOrientation == UIInterfaceOrientationPortrait);
    }
    */
    
    - (void)didReceiveMemoryWarning {
        // Releases the view if it doesn't have a superview.
        [super didReceiveMemoryWarning];
    
        // Release any cached data, images, etc that aren't in use.
    }
    
    - (void)viewDidUnload {
        // Release any retained subviews of the main view.
        // e.g. self.myOutlet = nil;
    }
    
    
    - (void)dealloc {
        [mapBarButton release];
        [pointBarButton release];
        [lineBarButton release];
        [polygonBarButton release];
    
        [super dealloc];
    }
    
    - (IBAction)mapBarButtonPressed{
        NSLog(@"mapBarButtonPressed");
        [self changeButtonAndViewState:0];
    }
    
    - (IBAction)pointBarButtonPressed{
        NSLog(@"pointBarButtonPressed");
        [self changeButtonAndViewState:1];
    
        if( [pinFactory actualPinCount] > 0){
            [self resetGeometry];
        }   
    }
    
    - (IBAction)lineBarButtonPressed{
        NSLog(@"lineBarButtonPressed");
    
        if( [pinFactory actualPinCount] > 0){
            [self resetGeometry];
        }
    
        [self changeButtonAndViewState:2];
    }
    
    - (IBAction)polygonBarButtonPressed{
        NSLog(@"polygonBarButtonPressed");
    
        if( [pinFactory actualPinCount] > 0){
            [self resetGeometry];
        }
    
        [self changeButtonAndViewState:3];
    }
    
    - (void)resetGeometry{
        NSLog(@"resetting geometry.. deleting all pins");
        [mapview removeAnnotations:[pinFactory pinArray]];
    
        NSMutableArray *array = [pinFactory pinArray];
        [array removeAllObjects];
    
        [geometryView setNeedsDisplay];
    }
    
    - (void)changeButtonAndViewState:(int)s{
        [pinFactory setGeometryState:s];
    
        mapBarButton.style = UIBarButtonItemStyleBordered;
        pointBarButton.style = UIBarButtonItemStyleBordered;
        lineBarButton.style = UIBarButtonItemStyleBordered;
        polygonBarButton.style = UIBarButtonItemStyleBordered;
    
        pointBarButton.enabled = YES;
        lineBarButton.enabled = YES;
        polygonBarButton.enabled = YES;
    
        switch ([pinFactory geometryState]) {
            case 0:{
                mapBarButton.style = UIBarButtonItemStyleDone;
                geometryView.hidden = YES;
                break;
            }
            case 1:{
                pointBarButton.enabled = NO;
    
                pointBarButton.style = UIBarButtonItemStyleDone;
    
                geometryView.hidden = NO;
                break;
            }
            case 2:{
                lineBarButton.enabled = NO;
    
                lineBarButton.style = UIBarButtonItemStyleDone;
    
                geometryView.hidden = NO;
                break;
            }
            case 3:{
                polygonBarButton.enabled = NO;
    
                polygonBarButton.style = UIBarButtonItemStyleDone;
    
                geometryView.hidden = NO;
    
                break;
            }
            default:
                break;
        }
    }
    
    -(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animate{
        NSLog(@"regionDidChangeAnimated");
    }
    
    @end
    

4 个答案:

答案 0 :(得分:1)

嘿,我看到没有人回答过你,我只知道如何做到这一点。不幸的是,你不能拦截事件并将它们转发到mapView,所以像

@interface MapOverlay
   MKMapView* map;
@end

@implementation
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
   // Do my stuff
   foo();
   bar();
   // Forward to the map
   [map touchesBegan....];
}
@end

这是你想要做的但是它不起作用。由于某种原因,你不能拦截地图的事件,也不能反向和子类化MKMapView并重载它的触摸方法。我发现这样做的唯一方法如下。

创建MapOverlay视图,将其设置为具有透明背景并禁用触摸。在MKMapView上叠加它。然后执行以下操作

子类UIWindow带有一个自定义类,可以将所有触摸转发到叠加层,或者将逻辑处理为“如果叠加层未隐藏,然后转发”,或者在叠加层本身保持状态。无论如何它看起来像这样

@implementation CustomWindow

- (void)sendEvent:(UIEvent*)event
{
   [super sendEvent:event];
   // Forward the event to the overlay
}

当您将事件转发到叠加层时,首先检查触摸是否在mapView区域内,然后找出它们的触摸类型,然后在叠加层上调用正确的触摸方法。

祝你好运!

答案 1 :(得分:0)

我知道这个答案可能来得太晚了,但无论如何我会为了那些也遇到过这个问题的人(比如我自己)的利益而采取刺激。

MKMapView类的触摸事件都由内部的UIScrollView处理。您可以通过将MKMapView作为自定义UIView的子视图,并在自定义视图中提供自定义触摸方法来捕获此滚动视图的事件。

诀窍是跟踪MKMapView使用的UIScrollView。为此,我重写了“hitTest”方法,该方法返回“self”,我相信这意味着这个自定义视图应该处理触摸事件。

此外,hitTest方法获取MKMapView的UIScrollView。在我的自定义UIView中,我将其称为“echo_to”,因为事件随后被回显到UIScrollView以使地图正常工作。


- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

    //  Get the UIView (in this case, a UIScrollView) that
    //  would ordinarily handle the events
    echo_to = [super hitTest:point withEvent:event];

    //  But let it be known we'll handle them instead.
    return self;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    NSLog(@"Touches Began");
    //  add your custom annotation behavior here
    [echo_to touchesBegan:touches withEvent:event];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

    NSLog(@"Touches moved");
    //  add your custom annotation behavior here
    [echo_to touchesMoved:touches withEvent:event];
}

祝你好运。

答案 2 :(得分:0)

根据geometryView的复杂程度,您可以使用注释视图来绘制它。

这种方法的缺点是视图不会随地图缩放(尽管您可以使用mapView:regionDidChangeanimated:委托方法在缩放后重绘),但它会平移地图。

答案 3 :(得分:0)

只要您不需要用户点击注释或捏合缩放地图,hotTest技巧就会很有效。这些都不能转发。