Ответ 1
У меня есть приложение, которое в основном делает это. У меня есть вид карты, с панелью инструментов в верхней части экрана. Когда вы нажимаете кнопку на этой панели инструментов, вы теперь находитесь в режиме, в котором вы можете провести пальцем по карте. Начало и конец салфетки будут представлять углы прямоугольника. Приложение отобразит полупрозрачный синий прямоугольник, чтобы отобразить выбранную область. Когда вы поднимаете палец, прямоугольный выбор завершен, и приложение начинает поиск местоположений в моей базе данных.
Я не обрабатываю круги, но думаю, что вы можете сделать что-то подобное, где у вас есть два режима выбора (прямоугольный или круговой). В круговом режиме выбора начальная и конечная точки салфетки могут представлять центр круга и край (радиус). Или два конца линии диаметра. Я оставлю эту часть вам.
Реализация
Сначала я определяю прозрачный слой наложения, который обрабатывает выбор (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:
#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):
#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:
:
#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
. Это кнопка на моей панели инструментов, которая обычно говорит Область. После того, как пользователь нажимает на него, они находятся в режиме выбора области, в этот момент на ярлыке кнопки изменяется Размах, чтобы напомнить им, чтобы пронести (возможно, не лучший пользовательский интерфейс, но то, что у меня есть).
Также обратите внимание, что, когда пользователь нажимает Область, чтобы войти в режим выбора области, я покажу им предупреждение, которое сообщает им, что им нужно провести салфетки. Поскольку это, вероятно, только напоминание, которое им нужно увидеть один раз, я использовал свой собственный PoliteAlertView, который является обычным UIAlertView
, который пользователи могут (не показывать предупреждение снова).
My lookupHistoryByArea
- это просто метод, который ищет мою базу данных для местоположений, сохраненным searchBounds
(в фоновом режиме), а затем отображает новые накладки на карте в найденных местах. Это, очевидно, будет отличаться для вашего приложения.
Ограничения
-
Так как это позволяет пользователю выбирать приблизительные области, я не считаю географическую точность критичной. Звучит не так, как должно быть в вашем приложении. Таким образом, я просто рисую прямоугольники с углом 90 градусов, не учитывая кривизну земли и т.д. Для областей всего в несколько миль это должно быть хорошо.
-
Мне нужно было сделать некоторые предположения о вашей фразе на основе касания. Я решил, что и самый простой способ реализовать приложение, и самый простой для пользователя сенсорного экрана - просто определить область с помощью одного прокрутки. Для рисования прямоугольника с касаниями потребовалось бы 4 прокрутки вместо одного, ввести сложность незакрытых прямоугольников, получить неаккуратные фигуры и, вероятно, не получить пользователя, чего они даже хотели. Итак, я старался, чтобы пользовательский интерфейс был прост. Если вы действительно хотите, чтобы пользователь рисовал на карте, см. Этот связанный ответ, который делает это.
-
Это приложение было написано до ARC и не изменено для ARC.
-
В моем приложении я фактически использую блокировку мьютекса для некоторых переменных, доступных в основном потоке (UI), и в фоновом (поисковом) потоке. Я взял этот код для этого примера. В зависимости от того, как работает поиск в базе данных, и как вы выбираете запуск поиска (GCD и т.д.), Вы должны обязательно проверить свою безопасность потоков.