Как настроить пузырь выноски для MKAnnotationView?
В настоящее время я работаю с mapkit и застрял.
У меня есть пользовательский вид аннотации, который я использую, и я хочу использовать свойство изображения, чтобы отображать точку на карте со своим собственным значком. У меня это работает нормально. Но то, что я также хотел бы сделать, это переопределить представление выноски по умолчанию (пузырь, который появляется с заголовком / подзаголовком при касании значка аннотации). Я хочу иметь возможность контролировать саму выноску: mapkit предоставляет доступ только к левому и правому вспомогательным представлениям выноски, но не дает возможности предоставить настраиваемое представление для выноски выноски, или присвоить ему нулевой размер, или что-либо еще.
Моя идея состояла в том, чтобы переопределить selectAnnotation / deselectAnnotation в моем MKMapViewDelegate
и затем нарисуйте свой собственный вид, вызвав мой пользовательский вид аннотации. Это работает, но только когда canShowCallout
установлен в YES
в моем классе просмотра пользовательских аннотаций. Эти методы НЕ вызываются, если у меня установлено NO
(это то, что я хочу, чтобы пузырь выноски по умолчанию не рисовался). Поэтому у меня нет возможности узнать, коснулся ли пользователь моей точки на карте (выбрал ее) или коснулся точки, которая не является частью моих видов аннотаций (удалил ее), без отображения всплывающего представления с выноской по умолчанию.
Я попытался пойти другим путем и просто обработать все сенсорные события самостоятельно на карте, и я не могу заставить это работать. Я читал другие посты, связанные с отловом сенсорных событий в виде карты, но они не совсем то, что я хочу. Есть ли способ покопаться в виде карты, чтобы удалить пузырь выноски перед рисованием? Я в недоумении.
Какие-либо предложения? Я что-то упускаю из виду?
9 ответов
Есть еще более простое решение.
Создать кастом UIView
(для вашего выноски).
Затем создайте подкласс MKAnnotationView
и переопределить setSelected
следующее:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if(selected)
{
//Add your custom view to self...
}
else
{
//Remove your custom view...
}
}
Бум, работа сделана.
detailCalloutAccessoryView
В старые времена это было больно, но Apple решила это, просто проверьте документы на MKAnnotationView
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.detailCalloutAccessoryView = UIImageView(image: UIImage(named: "zebra"))
На самом деле, это все. Принимает любой UIView.
Продолжая блестяще простой ответ @TappCandy, если вы хотите анимировать свой пузырь так же, как и по умолчанию, я создал этот метод анимации:
- (void)animateIn
{
float myBubbleWidth = 247;
float myBubbleHeight = 59;
calloutView.frame = CGRectMake(-myBubbleWidth*0.005+8, -myBubbleHeight*0.01-2, myBubbleWidth*0.01, myBubbleHeight*0.01);
[self addSubview:calloutView];
[UIView animateWithDuration:0.12 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^(void) {
calloutView.frame = CGRectMake(-myBubbleWidth*0.55+8, -myBubbleHeight*1.1-2, myBubbleWidth*1.1, myBubbleHeight*1.1);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.1 animations:^(void) {
calloutView.frame = CGRectMake(-myBubbleWidth*0.475+8, -myBubbleHeight*0.95-2, myBubbleWidth*0.95, myBubbleHeight*0.95);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.075 animations:^(void) {
calloutView.frame = CGRectMake(-round(myBubbleWidth/2-8), -myBubbleHeight-2, myBubbleWidth, myBubbleHeight);
}];
}];
}];
}
Это выглядит довольно сложно, но до тех пор, пока точка вашего выносного пузыря спроектирована так, чтобы она находилась в центре снизу, вы должны просто иметь возможность заменить myBubbleWidth
а также myBubbleHeight
с вашим собственным размером для работы. И не забудьте убедиться, что ваши подпредставления имеют свои autoResizeMask
свойство установлено на 63 (то есть "все"), чтобы они правильно масштабировались в анимации.
:-Джо
Нашел это лучшее решение для меня. Вы должны будете использовать некоторую креативность, чтобы сделать ваши собственные настройки
В вашем MKAnnotationView
подкласс, вы можете использовать
- (void)didAddSubview:(UIView *)subview{
int image = 0;
int labelcount = 0;
if ([[[subview class] description] isEqualToString:@"UICalloutView"]) {
for (UIView *subsubView in subview.subviews) {
if ([subsubView class] == [UIImageView class]) {
UIImageView *imageView = ((UIImageView *)subsubView);
switch (image) {
case 0:
[imageView setImage:[UIImage imageNamed:@"map_left"]];
break;
case 1:
[imageView setImage:[UIImage imageNamed:@"map_right"]];
break;
case 3:
[imageView setImage:[UIImage imageNamed:@"map_arrow"]];
break;
default:
[imageView setImage:[UIImage imageNamed:@"map_mid"]];
break;
}
image++;
}else if ([subsubView class] == [UILabel class]) {
UILabel *labelView = ((UILabel *)subsubView);
switch (labelcount) {
case 0:
labelView.textColor = [UIColor blackColor];
break;
case 1:
labelView.textColor = [UIColor lightGrayColor];
break;
default:
break;
}
labelView.shadowOffset = CGSizeMake(0, 0);
[labelView sizeToFit];
labelcount++;
}
}
}
}
И если subview
это UICalloutView
, тогда вы можете облажаться с ним, и что внутри него.
У меня такая же проблема. В этом блоге есть серьезные посты на эту тему http://spitzkoff.com/craig/?p=81.
Просто используя MKMapViewDelegate
не поможет вам здесь и подкласс MKMapView
и попытка расширить существующую функциональность также не сработала для меня.
То, что я в итоге сделал, это создать свой собственный CustomCalloutView
что у меня на вершине MKMapView
, Вы можете стилизовать это представление любым удобным для вас способом.
мой CustomCalloutView
имеет метод, похожий на этот:
- (void) openForAnnotation: (id)anAnnotation
{
self.annotation = anAnnotation;
// remove from view
[self removeFromSuperview];
titleLabel.text = self.annotation.title;
[self updateSubviews];
[self updateSpeechBubble];
[self.mapView addSubview: self];
}
Требуется MKAnnotation
объект и устанавливает свой собственный заголовок, после чего он вызывает два других довольно уродливых метода, которые регулируют ширину и размер содержимого выноски, а затем рисуют речевой пузырь вокруг него в правильном положении.
Наконец, представление добавляется как подпредставление для mapView. Проблема с этим решением состоит в том, что трудно удерживать выноску в правильном положении при прокрутке вида карты. Я просто скрываю выноску в методе делегатов представлений карты при изменении региона, чтобы решить эту проблему.
Потребовалось некоторое время, чтобы решить все эти проблемы, но теперь выноска почти ведет себя как официальная, но у меня это в своем собственном стиле.
В основном, чтобы решить эту проблему, необходимо: a) Предотвратить всплывание выноски по умолчанию. б) выяснить, какая аннотация была нажата.
Я смог достичь этого с помощью: a) установки canShowCallout на NO b) создания подклассов, MKPinAnnotationView и переопределения методов touchesBegan и touchesEnd.
Примечание: вам нужно обрабатывать сенсорные события для MKAnnotationView, а не MKMapView
Я просто придумаю подход, идея здесь
// Detect the touch point of the AnnotationView ( i mean the red or green pin )
// Based on that draw a UIView and add it to subview.
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
CGPoint newPoint = [self.mapView convertCoordinate:selectedCoordinate toPointToView:self.view];
// NSLog(@"regionWillChangeAnimated newPoint %f,%f",newPoint.x,newPoint.y);
[testview setCenter:CGPointMake(newPoint.x+5,newPoint.y-((testview.frame.size.height/2)+35))];
[testview setHidden:YES];
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
CGPoint newPoint = [self.mapView convertCoordinate:selectedCoordinate toPointToView:self.view];
// NSLog(@"regionDidChangeAnimated newPoint %f,%f",newPoint.x,newPoint.y);
[testview setCenter:CGPointMake(newPoint.x,newPoint.y-((testview.frame.size.height/2)+35))];
[testview setHidden:NO];
}
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
NSLog(@"Select");
showCallout = YES;
CGPoint point = [self.mapView convertPoint:view.frame.origin fromView:view.superview];
[testview setHidden:NO];
[testview setCenter:CGPointMake(point.x+5,point.y-(testview.frame.size.height/2))];
selectedCoordinate = view.annotation.coordinate;
[self animateIn];
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
NSLog(@"deSelect");
if(!showCallout)
{
[testview setHidden:YES];
}
}
Здесь - testview является UIView
размером 320x100 - showCallout BOOL - [self animateIn];
это функция, которая просматривает анимацию как UIAlertView
,
Я вытащил свой форк из отличного SMCalloutView, который решает проблему с предоставлением настраиваемого представления для выносок и позволяет довольно безболезненно использовать гибкую ширину / высоту. Все еще есть некоторые причуды, но пока они довольно функциональны:
Вы можете использовать leftCalloutView, установив annotation.text равным @" "
Пожалуйста, найдите ниже пример кода:
pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if(pinView == nil){
pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID] autorelease];
}
CGSize sizeText = [annotation.title sizeWithFont:[UIFont fontWithName:@"HelveticaNeue" size:12] constrainedToSize:CGSizeMake(150, CGRectGetHeight(pinView.frame)) lineBreakMode:UILineBreakModeTailTruncation];
pinView.canShowCallout = YES;
UILabel *lblTitolo = [[UILabel alloc] initWithFrame:CGRectMake(2,2,150,sizeText.height)];
lblTitolo.text = [NSString stringWithString:ann.title];
lblTitolo.font = [UIFont fontWithName:@"HelveticaNeue" size:12];
lblTitolo.lineBreakMode = UILineBreakModeTailTruncation;
lblTitolo.numberOfLines = 0;
pinView.leftCalloutAccessoryView = lblTitolo;
[lblTitolo release];
annotation.title = @" ";