自定義大頭針callout

轉(zhuǎn)自 :

http://stackoverflow.com/questions/17772108/custom-mkannotation-callout-bubble-with-button

There are several approaches to customizing callouts:

The easiest approach is to use the existing right and left callout accessories, and put your button in one of those. For example:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)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__);

}

That yields:

If you really don't like the button on the right, where accessories generally go, you can turn off that accessory, and iOS 9 offers the opportunity to specify thedetailCalloutAccessoryView, which replaces the callout's subtitle with whatever view you want:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)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;

}

This yields:

If you really want to develop a custom callout yourself, theLocation and Maps Programming Guideoutlines the steps involved:

In an iOS app, it’s good practice to use themapView:annotationView:calloutAccessoryControlTapped:delegate method to respond when users tap a callout view’s control (as long as the control is a descendant ofUIControl). In your implementation of this method you can discover the identity of the callout view’s annotation view so that you know which annotation the user tapped. In a Mac app, the callout view’s view controller can implement an action method that responds when a user clicks the control in a callout view.

When you use a custom view instead of a standard callout, you need to do extra work to make sure your callout shows and hides appropriately when users interact with it. The steps below outline the process for creating a custom callout that contains a button:

Design anNSVieworUIViewsubclass that represents the custom callout. It’s likely that the subclass needs to implement thedrawRect:method to draw your custom content.

Create a view controller that initializes the callout view and performs the action related to the button.

In the annotation view, implementhitTest:to respond to hits that are outside the annotation view’s bounds but inside the callout view’s bounds, as shown in Listing 6-7.

In the annotation view, implementsetSelected:animated:to add your callout view as a subview of the annotation view when the user clicks or taps it.

If the callout view is already visible when the user selects it, thesetSelected:method should remove the callout subview from the annotation view (see Listing 6-8).

In the annotation view’sinitWithAnnotation:method, set thecanShowCalloutproperty toNOto prevent the map from displaying the standard callout when the user selects the annotation.

That previous point outlines a pretty complicated scenarios (i.e. you have to write your own code to detecting taps outside the view in order to dismiss the it). If you're supporting iOS 9, you might just use a popover view controller, e.g.:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)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

}

Another approach that avoids having to get into the weeds of the third approach, above, is to add the callouts, themselves, as annotations.

So you'd have two types of annotations, your standard annotation, and these callout annotations. This would appear to be the approach adopted by tochi, and I'd agree that this is a more robust way to tackle the issue. Thus, the idea would be that when you select one of your standard annotation views, you would add this new callout annotation. For example:

// when user selects standard annotation view, add the callout annotation and select it

- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view

{

if([view.annotation isKindOfClass:[CustomAnnotation class]]) {

CalloutAnnotation *calloutAnnotation = [[CalloutAnnotation alloc] initForAnnotation:view.annotation];

[mapView addAnnotation:calloutAnnotation];

dispatch_async(dispatch_get_main_queue(), ^{

[mapView selectAnnotation:calloutAnnotation animated:YES];

});

}

}

// when user deselects callout annotation view (i.e. taps anywhere other than the callout annotation), remove the callout annotation

- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view

{

if([view.annotation isKindOfClass:[CalloutAnnotation class]]) {

[mapView removeAnnotation:view.annotation];

}

}

TheviewForAnnotationfor the standard annotation is unremarkable, performing your existing code for the standard annotation (just make sure to turn off the system generated callout withcanShowCallout), but if the annotation is aCalloutAnnotation, you'd then create aMKAnnotationView, such as something like this over-simplified example:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation

{

static NSString *customIdentifier? = @"CustomAnnotation";

static NSString *calloutIdentifier = @"CalloutAnnotation";

if ([annotation isKindOfClass:[CustomAnnotation class]]) {

MKPinAnnotationView *view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:customIdentifier];

view.canShowCallout? ? ? = NO;? // make sure to turn off standard callout

return view;

} else if ([annotation isKindOfClass:[CalloutAnnotation class]]) {

CGSize? ? ? ? ? ? size = CGSizeMake(100.0, 80.0);

MKAnnotationView *view = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:calloutIdentifier];

view.frame? ? ? ? ? ? = CGRectMake(0.0, 0.0, size.width, size.height);

view.backgroundColor? = [UIColor whiteColor];

UIButton *button? ? ? = [UIButton buttonWithType:UIButtonTypeRoundedRect];

button.frame? ? ? ? ? = CGRectMake(5.0, 5.0, size.width - 10.0, size.height - 10.0);

[button setTitle:@"OK" forState:UIControlStateNormal];

[button addTarget:self action:@selector(didTouchUpInsideCalloutButton:) forControlEvents:UIControlEventTouchUpInside];

[view addSubview:button];

view.canShowCallout? ? = NO;

view.centerOffset? ? ? = CGPointMake(0.0, -kMyCalloutOffset);

return view;

}

return nil;

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子侵贵,更是在濱河造成了極大的恐慌娩怎,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泛粹,死亡現(xiàn)場離奇詭異遂铡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)晶姊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門扒接,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事珠增〕瑁” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵蒂教,是天一觀的道長巍举。 經(jīng)常有香客問我,道長凝垛,這世上最難降的妖魔是什么懊悯? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮梦皮,結(jié)果婚禮上炭分,老公的妹妹穿的比我還像新娘。我一直安慰自己剑肯,他們只是感情好捧毛,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著让网,像睡著了一般呀忧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上溃睹,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天而账,我揣著相機(jī)與錄音,去河邊找鬼因篇。 笑死泞辐,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的竞滓。 我是一名探鬼主播咐吼,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼商佑!你這毒婦竟也來了汽烦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤莉御,失蹤者是張志新(化名)和其女友劉穎撇吞,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體礁叔,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡牍颈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了琅关。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片煮岁。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡讥蔽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出画机,到底是詐尸還是另有隱情冶伞,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布步氏,位于F島的核電站响禽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏荚醒。R本人自食惡果不足惜沟优,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一撩穿、第九天 我趴在偏房一處隱蔽的房頂上張望倘待。 院中可真熱鬧挂据,春花似錦、人聲如沸泡躯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽较剃。三九已至坝锰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間重付,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工凫乖, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留确垫,地道東北人。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓帽芽,卻偏偏與公主長得像删掀,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子导街,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容