iOS + MKMapView基于用户触摸的绘图 [英] iOS + MKMapView user touch based drawing

查看:94
本文介绍了iOS + MKMapView基于用户触摸的绘图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经搜索了很多这个问题,但是他们似乎都没有完全符合我的要求。
许多教程向我展示了如何在代码中添加线条和多边形,但没有使用徒手绘图。

I have searched a lot for this question, but none of them seem to do exactly what I want. A lot of tutorials show me how to add lines and polygons in code, but not with freehand drawing.

问题如下:

我正在建立房地产申请。如果用户在 MKMapView 上,它可以在他/她想要购买/租赁房屋的某个区域周围绘制一个矩形/圆形/ ...然后我需要显示用户所选区域内对应的结果。

I am building a real estate application. If the user is on the MKMapView it has the ability to draw a rectangle/circle/... around a certain area where he/she wants to buy/rent a house. Then I need to display the results that correspond within the area the user has selected.

目前我有一个 UIView 在我的 MKMapView 的顶部,我做了一些自定义绘图,有没有办法将点转换为坐标或...或者这完全不是这样做的方式?我也听说过 MKMapOverlayView 等等。但我不确定如何使用它。

Currently I have a UIView on top of my MKMapView where I do some custom drawing, is there a way to translate points to coordinates from that or ..? Or is this completely not the way this is done ? I have also heard about MKMapOverlayView, etc .. but am not exactly sure how to use this.

任何人都可以指向正确的方向,还是有一些示例代码或教程可以帮助我完成我需要的工作?

Can anybody point me in the right direction or does he have some sample code or a tutorial that can help me accomplish what I am in need for?

谢谢

推荐答案

我有一个基本上这样做的应用程序。我有一个地图视图,屏幕顶部有一个工具栏。当您按下该工具栏上的按钮时,您现在处于可以在地图上滑动手指的模式。滑动的开始和结束将代表矩形的角。该应用程序将绘制一个半透明的蓝色矩形覆盖图,以显示您选择的区域。当您抬起手指时,矩形选择已完成,应用程序开始搜索我的数据库中的位置。

I have an app that basically does this. I have a map view, with a toolbar at the top of the screen. When you press a button on that toolbar, you are now in a mode where you can swipe your finger across the map. The start and end of the swipe will represent the corners of a rectangle. The app will draw a translucent blue rectangle overlay to show the area you've selected. When you lift your finger, the rectangular selection is complete, and the app begins a search for locations in my database.

我不处理圈子,但我认为你可以做类似的事情,你有两种选择模式(矩形或圆形)。在圆形选择模式中,滑动起点和终点可以表示圆心和边(半径)。或者,直径线的两端。我会把那部分留给你。

I do not handle circles, but I think you could do something similar, where you have two selection modes (rectangular, or circular). In the circular selection mode, the swipe start and end points could represent circle center, and edge (radius). Or, the two ends of a diameter line. I'll leave that part to you.

首先,我定义一个透明的覆盖层,处理选择(OverlaySelectionView.h):

First, I define a transparent overlay layer, that handles selection (OverlaySelectionView.h):

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

@protocol OverlaySelectionViewDelegate
// callback when user finishes selecting map region
- (void) areaSelected: (CGRect)screenArea;
@end


@interface OverlaySelectionView : UIView {
@private    
    UIView* dragArea;
    CGRect dragAreaBounds;
    id<OverlaySelectionViewDelegate> delegate;
}

@property (nonatomic, assign) id<OverlaySelectionViewDelegate> delegate;

@end

和OverlaySelectionView.m:

and OverlaySelectionView.m:

#import "OverlaySelectionView.h"

@interface OverlaySelectionView()
@property (nonatomic, retain) UIView* dragArea;
@end

@implementation OverlaySelectionView

@synthesize dragArea;
@synthesize delegate;

- (void) initialize {
    dragAreaBounds = CGRectMake(0, 0, 0, 0);
    self.userInteractionEnabled = YES;
    self.multipleTouchEnabled = NO;
    self.backgroundColor = [UIColor clearColor];
    self.opaque = NO;
    self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
}

- (id) initWithCoder: (NSCoder*) coder {
    self = [super initWithCoder: coder];
    if (self != nil) {
        [self initialize];
    }
    return self;
}

- (id) initWithFrame: (CGRect) frame {
    self = [super initWithFrame: frame];
    if (self != nil) {
        [self initialize];
    }
    return self;
}

- (void)drawRect:(CGRect)rect {
    // do nothing
}

#pragma mark - Touch handling

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch* touch = [[event allTouches] anyObject];
    dragAreaBounds.origin = [touch locationInView:self];
}

- (void)handleTouch:(UIEvent *)event {
    UITouch* touch = [[event allTouches] anyObject];
    CGPoint location = [touch locationInView:self];

    dragAreaBounds.size.height = location.y - dragAreaBounds.origin.y;
    dragAreaBounds.size.width = location.x - dragAreaBounds.origin.x;

    if (self.dragArea == nil) {
        UIView* area = [[UIView alloc] initWithFrame: dragAreaBounds];
        area.backgroundColor = [UIColor blueColor];
        area.opaque = NO;
        area.alpha = 0.3f;
        area.userInteractionEnabled = NO;
        self.dragArea = area;
        [self addSubview: self.dragArea];
        [dragArea release];
    } else {
        self.dragArea.frame = dragAreaBounds;
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [self handleTouch: event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [self handleTouch: event];

    if (self.delegate != nil) {
        [delegate areaSelected: dragAreaBounds];
    }
    [self initialize];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [self initialize];
    [self.dragArea removeFromSuperview];
    self.dragArea = nil;
}

#pragma mark -

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

@end

然后我有一个课程实现上面定义的协议(MapViewController.h):

Then I have a class that implements the protocol defined above (MapViewController.h):

#import "OverlaySelectionView.h"

typedef struct {
    CLLocationDegrees minLatitude;
    CLLocationDegrees maxLatitude;
    CLLocationDegrees minLongitude;
    CLLocationDegrees maxLongitude;
} LocationBounds;

@interface MapViewController : UIViewController<MKMapViewDelegate, OverlaySelectionViewDelegate> {
    LocationBounds searchBounds;
    UIBarButtonItem* areaButton;

在我的MapViewController.m中, areaSelected 方法是我用 convertPoint:toCoordinateFromView执行触摸坐标到地理坐标的转换:

And in my MapViewController.m, the areaSelected method is where I perform the conversion of touch coordinates to geographic coordinates with convertPoint:toCoordinateFromView: :

#pragma mark - OverlaySelectionViewDelegate

- (void) areaSelected: (CGRect)screenArea
{       
    self.areaButton.style = UIBarButtonItemStyleBordered;
    self.areaButton.title = @"Area";

    CGPoint point = screenArea.origin;
    // we must account for upper nav bar height!
    point.y -= 44;
    CLLocationCoordinate2D upperLeft = [mapView convertPoint: point toCoordinateFromView: mapView];
    point.x += screenArea.size.width;
    CLLocationCoordinate2D upperRight = [mapView convertPoint: point toCoordinateFromView: mapView];
    point.x -= screenArea.size.width;
    point.y += screenArea.size.height;
    CLLocationCoordinate2D lowerLeft = [mapView convertPoint: point toCoordinateFromView: mapView];
    point.x += screenArea.size.width;
    CLLocationCoordinate2D lowerRight = [mapView convertPoint: point toCoordinateFromView: mapView];

    searchBounds.minLatitude = MIN(lowerLeft.latitude, lowerRight.latitude);
    searchBounds.minLongitude = MIN(upperLeft.longitude, lowerLeft.longitude);
    searchBounds.maxLatitude = MAX(upperLeft.latitude, upperRight.latitude);
    searchBounds.maxLongitude = MAX(upperRight.longitude, lowerRight.longitude);

    // TODO: comment out to keep search rectangle on screen
    [[self.view.subviews lastObject] removeFromSuperview];

    [self performSelectorInBackground: @selector(lookupHistoryByArea) withObject: nil];
}

// this action is triggered when user selects the Area button to start selecting area
// TODO: connect this to areaButton yourself (I did it in Interface Builder)
- (IBAction) selectArea: (id) sender
{
    PoliteAlertView* message = [[PoliteAlertView alloc] initWithTitle: @"Information"
                                                              message: @"Select an area to search by dragging your finger across the map"
                                                             delegate: self
                                                              keyName: @"swipe_msg_read"
                                                    cancelButtonTitle: @"Ok"
                                                    otherButtonTitles: nil];
    [message show];
    [message release];

    OverlaySelectionView* overlay = [[OverlaySelectionView alloc] initWithFrame: self.view.frame];
    overlay.delegate = self;
    [self.view addSubview: overlay];
    [overlay release];

    self.areaButton.style = UIBarButtonItemStyleDone;
    self.areaButton.title = @"Swipe";
}

你会注意到我的 MapViewController 有一个属性, areaButton 。这是我工具栏上的一个按钮,通常表示区域。在用户按下它之后,它们处于区域选择模式,此时,按钮标签会更改为滑动以提醒它们滑动(可能不是最好的UI,但是这就是我所拥有的。

You'll notice that my MapViewController has a property, areaButton. That's a button on my toolbar, which normally says Area. After the user presses it, they are in area selection mode at which point, the button label changes to say Swipe to remind them to swipe (maybe not the best UI, but that's what I have).

另外请注意,当用户按区域进入区域选择模式时,我会向他们显示一条警告,告诉他们他们需要刷卡。由于这可能只是提醒他们需要看一次,我使用了自己的 PoliteAlertView ,这是一个用户可以抑制的自定义 UIAlertView (不再显示警报)。

Also notice that when the user presses Area to enter area selection mode, I show them an alert that tells them that they need to swipe. Since this is probably only a reminder they need to see once, I have used my own PoliteAlertView, which is a custom UIAlertView that users can suppress (don't show the alert again).

我的 lookupHistoryByArea 只是一种通过保存的 searchBounds (在后台)搜索我的数据库中的位置的方法,然后在找到的位置绘制地图上的新叠加层。对于您的应用,这显然会有所不同。

My lookupHistoryByArea is just a method that searches my database for locations, by the saved searchBounds (in the background), and then plots new overlays on the map at the found locations. This will obviously be different for your app.


  • 由于这是为了让用户选择近似区域,因此我认为地理精度并不重要。它听起来不应该在你的应用程序中。因此,我只绘制90度角的矩形,不考虑地球曲率等。对于只有几英里的区域,这应该没问题。

  • Since this is for letting the user select approximate areas, I did not consider geographic precision to be critical. It doesn't sound like it should be in your app, either. Thus, I just draw rectangles with 90 degree angles, not accounting for earth curvature, etc. For areas of just a few miles, this should be fine.

我不得不对你的短语基于触摸的绘图做出一些假设。我认为实现应用程序的最简单方法和触摸屏用户最容易使用的方法是简单地用一次滑动来定义区域。 绘图一个带触摸的矩形需要4次滑动而不是一次,引入非闭合矩形的复杂性,产生邋shape的形状,并且可能无法让用户得到他们想要的东西。所以,我试图保持UI简单。如果您真的希望用户绘图在地图上,请查看相关的答案

I had to make some assumptions about your phrase touch based drawing. I decided that both the easiest way to implement the app, and the easiest for a touchscreen user to use, was to simply define the area with one single swipe. Drawing a rectangle with touches would require 4 swipes instead of one, introduce the complexity of non-closed rectangles, yield sloppy shapes, and probably not get the user what they even wanted. So, I tried to keep the UI simple. If you really want the user drawing on the map, see this related answer which does that.

此应用程序是在ARC之前编写的,而不是为ARC更改的。

This app was written before ARC, and not changed for ARC.

In我的应用程序,我实际上使用互斥锁定在主(UI)线程上访问的一些变量,在后台(搜索)线程中。我为这个例子拿了那个代码。根据数据库搜索的工作方式以及您选择如何运行搜索(GCD等),您应该确保审核自己的线程安全。

In my app, I actually do use mutex locking for some variables accessed on the main (UI) thread, and in the background (search) thread. I took that code out for this example. Depending on how your database search works, and how you choose to run the search (GCD, etc.), you should make sure to audit your own thread-safety.

这篇关于iOS + MKMapView基于用户触摸的绘图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆