MKAnnotationView의 콜 아웃 풍선을 사용자 정의하는 방법은 무엇입니까?


88

나는 현재 맵킷으로 작업하고 있는데 막혔습니다.

사용중인 사용자 지정 주석보기가 있고 image 속성을 사용하여 내 아이콘으로지도에 지점을 표시하려고합니다. 나는 이것이 잘 작동한다. 하지만 제가 또한하고 싶은 것은 기본 콜 아웃보기 (주석 아이콘을 터치하면 제목 / 부제목과 함께 표시되는 풍선)를 재정의하는 것입니다. 콜 아웃 자체를 제어 할 수 있기를 원합니다. 맵킷은 왼쪽 및 오른쪽 보조 콜 아웃보기에 대한 액세스 만 제공하지만 콜 아웃 풍선에 대한 사용자 정의보기를 제공하거나 크기를 0으로 지정하거나 다른 어떤 것도 제공 할 수 없습니다.

내 생각은 내에서 selectAnnotation / deselectAnnotation을 재정의 MKMapViewDelegate한 다음 내 사용자 지정 주석보기를 호출하여 내 사용자 지정보기를 그리는 것입니다. 이것은 작동하지만 내 사용자 정의 주석보기 클래스에서 canShowCallout로 설정된 경우에만 가능 YES합니다. 이 메서드를 설정하면이 메서드가 호출되지 않습니다 NO(원하는대로 기본 설명 선 풍선이 그려지지 않음). 따라서 기본 콜 아웃 풍선보기가 표시되지 않고 사용자가지도에서 내 지점을 터치했는지 (선택했는지) 또는 내 주석보기의 일부가 아닌 지점을 터치했는지 (삭제했는지) 알 수있는 방법이 없습니다.

다른 경로로 내려 가서지도에서 모든 터치 이벤트를 직접 처리하려고했지만이 작업을 수행 할 수없는 것 같습니다. 지도보기에서 터치 이벤트 잡기와 관련된 다른 게시물을 읽었지만 정확히 내가 원하는 것이 아닙니다. 그리기 전에 콜 아웃 버블을 제거하기 위해 맵 뷰를 파헤치는 방법이 있습니까? 나는 헤매고있다.

어떤 제안? 나는 명백한 것을 놓치고 있습니까?


이 링크는 작동하지 않지만 여기에서 동일한 게시물을 찾았습니다.-> 사용자 지정지도 주석 설명 선 만들기 – 파트 1
Fede Mika 2011 년

2
빠른 데모 를 위해이 프로젝트 를 참조 할 수 있습니다 . github.com/akshay1188/CustomAnnotation 위의 답변을 하나의 실행중인 데모로 컴파일합니다.
akshay1188

답변:


53

더 쉬운 해결책이 있습니다.

사용자 지정 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...
    }
}

붐, 완료되었습니다.


8
안녕하세요 TappCandy! 첫째, 귀하의 솔루션에 감사드립니다. 작동하지만 뷰를로드 할 때 (나는 nib 파일에서 수행하고 있음) 버튼이 작동하지 않습니다. 제대로 작동하려면 어떻게해야합니까 ?? 감사합니다
Frade 2012 년

이 솔루션의 단순함을 좋아하십시오!
Eric Brotto 2012

6
흠-작동하지 않습니다! 콜 아웃 "버블"이 아닌지도 주석 (예 : 핀)을 대체합니다.
PapillonUK

2
작동하지 않습니다. MKAnnotationView에는 맵뷰에 대한 참조가 없으므로 사용자 지정 콜 아웃을 추가하는 데 몇 가지 문제가 있습니다. 예를 들어 이것을 하위보기로 추가하는 것이 화면에서 벗어날 지 알 수 없습니다.
Cameron Lowell Palmer

@PapillonUK 콜 아웃의 프레임을 제어 할 수 있습니다. 핀을 교체하는 것이 아니라 그 위에 나타납니다. 하위보기로 추가 할 때 위치를 조정하십시오.
Ben Packard

40

detailCalloutAccessoryView

옛날에는 이것이 고통 스러웠지만 Apple에서 해결했습니다. MKAnnotationView 의 문서를 확인하십시오.

view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.detailCalloutAccessoryView = UIImageView(image: UIImage(named: "zebra"))

그게 다야. 모든 UIView를받습니다.


어느 것을 사용하는 것이 가장 좋습니까?
Satyam 2014

13

@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 (즉, "all")으로 설정되어 있는지 확인하여 애니메이션에서 올바르게 확장되도록하십시오.

:-조


Btw, 콘텐츠의 적절한 크기 조정을 위해 프레임이 아닌 scale 속성에서 애니메이션 할 수 있습니다.
occulus

CGRectMake를 사용하지 마십시오. CGTransform을 사용하십시오.
Cameron Lowell Palmer

@occulus-먼저 시도했지만 iOS 4에서 렌더링 문제가있는 것 같습니다.하지만 사용하지 않았기 때문일 수 있습니다 CABasicAnimation. 기억하기에 너무 오래 전입니다! 중앙 오프셋으로 인해 y 속성도 애니메이션해야한다는 것을 알고 있습니다.
jowie 2012-07-24

@CameronLowellPalmer-왜 사용하지 CGRectMake않습니까?
jowie 2012-07-24

@jowie 구문이 더 좋고 코드의 마법 값을 피하기 때문입니다. CGAffineTransformMakeScale (1.1f, 1.1f); 상자를 110 % 늘리는 것은 분명합니다. 당신이 한 방식은 효과가 있을지 모르지만 예쁘지 않습니다.
Cameron Lowell Palmer

8

이것이 저에게 최고의 솔루션이라는 것을 알았습니다. 자신 만의 커스터마이징을하려면 창의력을 발휘해야합니다.

당신에 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, 당신은 그것과 그 안에 무엇이 들어 있는지 나사로 돌릴 수 있습니다.


1
UICalloutview가 공용 클래스가 아니기 때문에 하위 뷰가 UICalloutView인지 어떻게 확인합니까?
Manish Ahuja

if ([[[subview class] description] isEqualToString:@"UICalloutView"]) 더 나은 방법이 있다고 생각하지만 이것은 작동합니다.
яοвοτағτєяа ււ jan

Manish가 지적했듯이 개인 수업이기 때문에 이것에 문제가 있습니까?
다니엘

아마도 그들은 콜 아웃 뷰에 대한 UIView 구조를 변경했을 것입니다. 나는 이것을 사용하는 앱을 업그레이드하지 않았으므로 당신은 당신 자신입니다 : P
яοвοτағτєяа ււ

6

나는 같은 문제가 있었다. 이 블로그 http://spitzkoff.com/craig/?p=81 에이 주제에 대한 심각한 블로그 게시물이 있습니다.

를 사용하는 것만으로 MKMapViewDelegate는 도움이되지 않으며 MKMapView기존 기능을 하위 클래스 화 하고 확장하려는 시도도 저에게 효과적이지 않았습니다.

내가 결국 한 것은 CustomCalloutViewMKMapView. 원하는 방식으로이보기의 스타일을 지정할 수 있습니다.

내 다음 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에 하위보기로 추가됩니다. 이 솔루션의 문제점은지도보기를 스크롤 할 때 콜 아웃을 올바른 위치에 유지하기 어렵다는 것입니다. 이 문제를 해결하기 위해 지역 변경에 대한 맵 뷰 위임 메서드에서 콜 아웃을 숨기고 있습니다.

이러한 모든 문제를 해결하는 데 시간이 좀 걸렸지 만 이제는 콜 아웃이 공식적인 콜 아웃과 거의 비슷하게 작동하지만 제 스타일로 구현했습니다.


두 번째로 UICalloutView는 비공개 클래스이며 공식적으로 SDK에서 사용할 수 없습니다. 가장 좋은 방법은 Sascha의 조언을 따르는 것입니다.
JoePasq 2010 년

안녕하세요 Sascha, 답장 해 주셔서 감사합니다. 그러나 현재 우리가 겪고있는 문제는 핀이 나타나는지도에서 위치 (위도 또는 경도가 아닌 x, y)를 가져 와서 콜 아웃보기를 표시하는 방법입니다.
Zach

1
toPointToView : # - convertPoint - convertCoordinate # : 좌표 변환 쉽게 MKMapView 두 기능을 수행 할 수 toCoordinateFromView :
샤샤 Konietzke

5

기본적으로이 문제를 해결하려면 다음을 수행해야합니다. a) 기본 콜 아웃 풍선이 표시되지 않도록합니다. b) 클릭 한 주석을 파악합니다.

a) canShowCallout을 NO로 설정 b) 서브 클래 싱, MKPinAnnotationView 및 touchesBegan 및 touchesEnd 메서드를 재정 의하여이를 달성 할 수있었습니다.

참고 : MKMapView가 아닌 ​​MKAnnotationView에 대한 터치 이벤트를 처리해야합니다.


3

저는 접근 방식을 생각해 냈습니다. 여기 아이디어는

  // 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는 UIView320x100 크기입니다. showCallout은 BOOL 입니다. [self animateIn];같은 뷰 애니메이션을 수행하는 기능입니다 UIAlertView.


selectedCoordinate는 무엇을 지정합니까? 어떤 유형의 개체입니까?
Pradeep Reddy Kypa

CLLocationCoordinate2d 객체입니다.
bharathi kumar

1

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 = @" ";            

1
이것은 어느 정도 '작동'할 것이지만 더 일반적인 질문에 실제로 대답하지 않으며 매우 엉망입니다.
Cameron Lowell Palmer

1

콜 아웃에 대한 사용자 정의보기를 제공하고 유연한 너비 / 높이를 매우 쉽게 허용하는 문제를 해결하는 우수한 SMCalloutView를 내 포크로 밀어 냈습니다. 해결해야 할 몇 가지 단점이 있지만 지금까지는 꽤 기능적입니다.

https://github.com/u10int/calloutview

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.