26

ユーザーがGPSによってローカライズされ、特定の場所にいるかどうかを尋ねられるアプリを開発しています。これを確認するために、吹き出しがすぐに表示され、特定の場所にいるかどうか尋ねられます。

同様の質問がたくさんあるので、カスタム吹き出しバブルを作成できました。 カスタム吹き出し吹き出し

私の問題: ボタンは「クリック可能」ではありません 私の推測: このカスタム コールアウトは標準のコールアウト バブルよりも高いため、負の「フレーム」に配置する必要があったため、ボタンをクリックできません。これが私のdidSelectAnnotationView方法です

- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
    if(![view.annotation isKindOfClass:[MKUserLocation class]]) {
        CalloutView *calloutView = (CalloutView *)[[[NSBundle mainBundle] loadNibNamed:@"callOutView" owner:self options:nil] objectAtIndex:0];
        CGRect calloutViewFrame = calloutView.frame;
        calloutViewFrame.origin = CGPointMake(-calloutViewFrame.size.width/2 + 15, -calloutViewFrame.size.height);
        calloutView.frame = calloutViewFrame;
        [calloutView.calloutLabel setText:[(MyLocation*)[view annotation] title]];
        [calloutView.btnYes addTarget:self
                               action:@selector(checkin)
                     forControlEvents:UIControlEventTouchUpInside];
        calloutView.userInteractionEnabled = YES;
        view.userInteractionEnabled = YES;
        [view addSubview:calloutView];
    }

}

CalloutView は、2 つのプロパティ (場所とボタンの名前を示すラベル) と xib を持つ単純なクラスです。

私はこのカスタム コールアウト バブルを数日間実行しています。「非同期ソリューション」ソリューションを使用してみましたが、他の種類のボタンと開示ボタンを追加できませんでした。

私の次の試みは、非同期ソリューションよりも簡単なものを見つけて、それを自分の用途に合わせて変更することでした。それがtochi's custom calloutを見つけた方法です。

Tochi のカスタム コールアウト

彼の仕事に基づいて、私は彼の吹き出しをカスタマイズし、カスタム ボタンの情報ボタンを変更することができました。しかし、私の問題は同じままでした。カスタムの吹き出しビューをピンの上に配置するには、負のフレームを指定する必要があったため、ボタンは下の 5 ピクセルでのみ「クリック可能」になりました。iOSのデフォルトのコールアウトバブルをさらに掘り下げ、サブクラス化し、そこでコールアウトのフレームを変更する必要があるようです。でも今は本当に絶望的です。

正しい方法を教えていただけるか、アドバイスをいただければ幸いです。

4

3 に答える 3

82

吹き出しをカスタマイズするには、いくつかの方法があります。

  1. 最も簡単な方法は、既存の左右のコールアウト アクセサリを使用し、それらのいずれかにボタンを配置することです。例えば:

    - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
        static NSString *identifier = @"MyAnnotationView";
    
        if ([annotation isKindOfClass:[MKUserLocation class]]) {
            return nil;
        }
    
        MKPinAnnotationView *view = (id)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
        if (view) {
            view.annotation = annotation;
        } else {
            view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
            view.canShowCallout = true;
            view.animatesDrop = true;
            view.rightCalloutAccessoryView = [self yesButton];
        }
    
        return view;
    }
    
    - (UIButton *)yesButton {
        UIImage *image = [self yesButtonImage];
    
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        button.frame = CGRectMake(0, 0, image.size.width, image.size.height); // don't use auto layout
        [button setImage:image forState:UIControlStateNormal];
        [button addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventPrimaryActionTriggered];
    
        return button;
    }
    
    - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
        NSLog(@"%s", __PRETTY_FUNCTION__);
    }
    

    これにより、次の結果が得られます。

    ここに画像の説明を入力

  2. アクセサリが一般的に配置される右側のボタンが本当に気に入らない場合は、そのアクセサリをオフにすることができます。iOS 9 では、detailCalloutAccessoryView吹き出しのサブタイトルを必要なビューに置き換えるを指定する機会が提供されます。

    - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
        static NSString *identifier = @"MyAnnotationView";
    
        if ([annotation isKindOfClass:[MKUserLocation class]]) {
            return nil;
        }
    
        MKPinAnnotationView *view = (id)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
        if (view) {
            view.annotation = annotation;
        } else {
            view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
            view.canShowCallout = true;
            view.animatesDrop = true;
        }
        view.detailCalloutAccessoryView = [self detailViewForAnnotation:annotation];
    
        return view;
    }
    
    - (UIView *)detailViewForAnnotation:(PlacemarkAnnotation *)annotation {
        UIView *view = [[UIView alloc] init];
        view.translatesAutoresizingMaskIntoConstraints = false;
    
        UILabel *label = [[UILabel alloc] init];
        label.text = annotation.placemark.name;
        label.font = [UIFont systemFontOfSize:20];
        label.translatesAutoresizingMaskIntoConstraints = false;
        label.numberOfLines = 0;
        [view addSubview:label];
    
        UIButton *button = [self yesButton];
        [view addSubview:button];
    
        NSDictionary *views = NSDictionaryOfVariableBindings(label, button);
    
        [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[label]|" options:0 metrics:nil views:views]];
        [view addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
        [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[label]-[button]|" options:0 metrics:nil views:views]];
    
        return view;
    }
    
    - (UIButton *)yesButton {
        UIImage *image = [self yesButtonImage];
    
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        button.translatesAutoresizingMaskIntoConstraints = false; // use auto layout in this case
        [button setImage:image forState:UIControlStateNormal];
        [button addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventPrimaryActionTriggered];
    
        return button;
    }
    

    これにより、次の結果が得られます。

    ここに画像の説明を入力

  3. カスタム コールアウトを自分で開発したい場合は、Location and Maps Programming Guideに含まれる手順の概要が説明されています。

    iOS アプリでは、mapView:annotationView:calloutAccessoryControlTapped:デリゲート メソッドを使用して、ユーザーが吹き出しビューのコントロールをタップしたときに応答することをお勧めします (コントロールが の子孫である場合UIControl)。このメソッドの実装では、吹き出しビューの注釈ビューの ID を検出して、ユーザーがどの注釈をタップしたかを知ることができます。Mac アプリでは、吹き出しビューのビュー コントローラーは、ユーザーが吹き出しビューのコントロールをクリックしたときに応答するアクション メソッドを実装できます。

    標準の吹き出しの代わりにカスタム ビューを使用する場合は、ユーザーが吹き出しを操作したときに吹き出しが適切に表示および非表示になるように、追加の作業を行う必要があります。次の手順は、ボタンを含むカスタム コールアウトを作成するプロセスの概要を示しています。

    • NSViewカスタム コールアウトを表すまたはUIViewサブクラスを設計します。サブクラスはdrawRect:、カスタム コンテンツを描画するメソッドを実装する必要がある可能性があります。

    • 吹き出しビューを初期化し、ボタンに関連するアクションを実行するビュー コントローラーを作成します。

    • hitTest:注釈ビューでは、リスト 6-7 に示すように、注釈ビューの境界外でコールアウト ビューの境界内にあるヒットに応答するように実装します。

    • setSelected:animated:注釈ビューで、ユーザーがクリックまたはタップしたときに吹き出しビューを注釈ビューのサブビューとして追加するように実装します。

    • ユーザーがコールアウト ビューを選択したときにコールアウト ビューがすでに表示されている場合、setSelected:メソッドは注釈ビューからコールアウト サブビューを削除する必要があります (リスト 6-8 を参照)。

    • 注釈ビューのinitWithAnnotation:メソッドで、canShowCalloutプロパティをに設定しNOて、ユーザーが注釈を選択したときにマップに標準の吹き出しが表示されないようにします。

    Swift では、https://github.com/robertmryan/CustomMapViewAnnotationCalloutSwiftは、この吹き出しの完全なカスタマイズを行う方法の例を示しています (例: 吹き出しの吹き出しの形状の変更、背景色の変更など)。

  4. 前のポイントは、かなり複雑なシナリオの概要を示しています (つまり、ビューを閉じるには、ビュー外のタップを検出する独自のコードを作成する必要があります)。iOS 9 をサポートしている場合は、次のようなポップオーバー ビュー コントローラーを使用できます。

    - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
        static NSString *identifier = @"MyAnnotationView";
    
        if ([annotation isKindOfClass:[MKUserLocation class]]) {
            return nil;
        }
    
        MKPinAnnotationView *view = (id)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
        if (view) {
            view.annotation = annotation;
        } else {
            view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
            view.canShowCallout = false;  // note, we're not going to use the system callout
            view.animatesDrop = true;
        }
    
        return view;
    }
    
    - (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
        PopoverController *controller = [self.storyboard instantiateViewControllerWithIdentifier:@"AnnotationPopover"];
        controller.modalPresentationStyle = UIModalPresentationPopover;
    
        controller.popoverPresentationController.sourceView = view;
    
        // adjust sourceRect so it's centered over the annotation
    
        CGRect sourceRect = CGRectZero;
        sourceRect.origin.x += [mapView convertCoordinate:view.annotation.coordinate toPointToView:mapView].x - view.frame.origin.x;
        sourceRect.size.height = view.frame.size.height;
        controller.popoverPresentationController.sourceRect = sourceRect;
    
        controller.annotation = view.annotation;
    
        [self presentViewController:controller animated:TRUE completion:nil];
    
        [mapView deselectAnnotation:view.annotation animated:true];  // deselect the annotation so that when we dismiss the popover, the annotation won't still be selected
    }
    
于 2013-07-21T12:44:51.977 に答える
0
(void)mapView:(MKMapView *)mapViewIn didSelectAnnotationView:(MKAnnotationView *)view {
    if(![view.annotation isKindOfClass:[MKUserLocation class]])
    {
        CustomeCalloutViewController *calloutView = [[CustomeCalloutViewController alloc]initWithNibName:@"CustomeCalloutViewController" bundle:nil];
        [calloutView setPopinTransitionStyle:BKTPopinTransitionStyleSlide];
        [calloutView setPopinTransitionDirection:BKTPopinTransitionDirectionTop];
        [self presentPopinController:calloutView animated:YES completion:^{
            NSLog(@"Popin presented !");
        }];
        [mapView deselectAnnotation:view.annotation animated:true];
    }
}
于 2016-02-10T11:51:03.447 に答える