MKAnnotationViewの吹き出しをカスタマイズする方法は?


88

私は現在マップキットで作業していて、行き詰まっています。

使用しているカスタムアノテーションビューがあり、imageプロパティを使用して、マップ上のポイントを独自のアイコンで表示します。私はこれで問題なく動いています。しかし、私がやりたいのは、デフォルトの吹き出しビュー(注釈アイコンがタッチされたときにタイトル/サブタイトルとともに表示されるバブル)をオーバーライドすることです。吹き出し自体を制御できるようにしたいのです。マップキットは、左と右の補助吹き出しビューへのアクセスのみを提供しますが、吹き出しバブルのカスタムビューを提供したり、サイズをゼロにしたりすることはできません。

私のアイデアは、私MKMapViewDelegateのでselectAnnotation / deselectAnnotationをオーバーライドしてから、カスタムアノテーションビューを呼び出して独自のカスタムビューを描画することでした。これは機能canShowCalloutしますYESが、カスタムアノテーションビュークラスでがに設定されている場合のみです。これが設定されている場合、これらのメソッドは呼び出されませんNO(これが私が望んでいるため、デフォルトの吹き出しのバブルは描画されません)。したがって、デフォルトの吹き出しのバブルビューが表示されない限り、ユーザーがマップ上の自分のポイントに触れた(選択した)か、注釈ビューの一部ではない(選択した)ポイントに触れたかを知る方法がありません。

別のパスをたどって、マップ内ですべてのタッチイベントを自分で処理しただけで、これが機能していないようです。マップビューでタッチイベントをキャッチすることに関連する他の投稿を読みましたが、それらは私が望むものではありません。描画する前に吹き出しの吹き出しを削除するためにマップビューを掘り下げる方法はありますか?私は途方に暮れています。

助言がありますか?私は明白な何かを見逃していますか?


このリンクは機能しませんが、同じ投稿がここにあります-> カスタムマップアノテーションコールアウトの作成–パート1
Fede Mika

2
簡単なデモについては、このプロジェクトを参照できます。github.com/akshay1188/CustomAnnotation上記の回答を1つの実行中のデモにコンパイルします。
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

このソリューションのシンプルさが大好きです。
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を取ります。


どちらが最適ですか?
サティヤム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(つまり「すべて」)に設定されていることを忘れないでください。これにより、サブビューはアニメーションで正しくスケーリングされます。

:-ジョー


ところで、コンテンツの適切なスケーリングを取得するために、フレームではなくスケールプロパティでアニメーション化することができます。
occulus 2012年

CGRectMakeは使用しないでください。CGTransformを使用します。
Cameron Lowell Palmer

@occulus-最初に試したと思いますが、iOS 4ではレンダリングの問題がありましたが、それは私が使用していないためである可能性がありますCABasicAnimation... 中心のオフセットのため、yプロパティもアニメーション化する必要があることはわかっています。
jowie

@CameronLowellPalmer-なぜ使用しないのCGRectMakeですか?
ジョーイ

@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++;
            }
        }
    }
}

場合とsubviewUICalloutView、その後、あなたはそれを周りのネジ、及びその中に何ができます。


1
UICalloutviewはパブリッククラスではないため、サブビューがUICalloutViewかどうかをどのように確認しますか。
Manish Ahuja 2012年

if ([[[subview class] description] isEqualToString:@"UICalloutView"]) もっと良い方法があると思いますが、これでうまくいきます。
яοвοτағτєяаււ

マニッシュが指摘したように、これはプライベートクラスなので、何か問題がありましたか。
ダニエル

おそらく彼らは、calloutviewsのUIView構造を変更しました。P:あなたがあなた自身にしているので、私は、これを使用するアプリケーションをアップグレードしていない
яοвοτағτєяаււ

6

同じ問題がありました。このブログ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オブジェクトを受け取り、独自のタイトルを設定します。その後、吹き出しの内容の幅とサイズを調整し、適切な位置でその周りに吹き出しを描画する非常に醜い2つのメソッドを呼び出します。

最後に、ビューがサブビューとしてmapViewに追加されます。このソリューションの問題は、マップビューがスクロールされたときにコールアウトを正しい位置に維持することが難しいことです。この問題を解決するために、リージョンの変更時にマップビューのデリゲートメソッドでコールアウトを非表示にしています。

これらすべての問題を解決するには少し時間がかかりましたが、コールアウトはほぼ公式のコールアウトのように動作しますが、私は自分のスタイルでそれを持っています。


2つ目は、UICalloutViewはプライベートクラスであり、SDKで公式に使用できないことです。あなたの最善の策は、サシャのアドバイスに従うことです。
JoePasq

こんにちはSascha、ご返信ありがとうございます。ただし、現在発生している問題は、ピンが表示される地図上の位置(緯度、経度ではなく、x、yである)を取得して、吹き出しビューを表示できるようにする方法です。
ザック

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は320x100 UIViewのサイズです。showCalloutはBOOL です。[self animateIn];アニメーションを表示する関数ですUIAlertView


selectedCoordinateは何を指定していますか?それはどのタイプのオブジェクトですか?
Pradeep Reddy Kypa

CLLocationCoordinate2dオブジェクトです。
bharathi kumar 2016

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.