可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am looking at Apple's MapCallouts example for map annotations and callouts (==bubbles that appear when you click on a pin).
Every annotation there has coordinates, title and subtitle.
I would like to display subtitle in 2 lines, i tried with:
- (NSString *)subtitle
{
return @"Founded: June 29, 1776\nSecond line text";
}
but the text "Second line text" stays in one line and makes bubble wider.I get this:
I would also like to change image of the button to one of my own's, the code that sets the button is currently like this:
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
Any ideas?
EDIT: I tried 7KV7's advice. Button change is successful, but i still cant get subtitles in 2 lines.
My code:
MKPinAnnotationView* customPinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:BridgeAnnotationIdentifier] autorelease];
customPinView.pinColor = MKPinAnnotationColorPurple;
customPinView.animatesDrop = YES;
// Button
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, 0, 23, 23);
button.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
//UIEdgeInsets titleInsets = UIEdgeInsetsMake(7.0, -20.0, 7.0, 7.0);
//button.titleEdgeInsets = titleInsets;
[button setImage:[UIImage imageNamed:@"ic_phone_default.png"] forState:UIControlStateNormal];
//[button setImage:[UIImage imageNamed:@"ic_phone_selected.png"] forState:UIControlStateSelected];
[button setImage:[UIImage imageNamed:@"ic_phone_selected.png"] forState:UIControlStateHighlighted];
[button addTarget:self action:@selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
customPinView.rightCalloutAccessoryView = button;
//two labels
UIView *leftCAV = [[UIView alloc] initWithFrame:CGRectMake(0,0,50,50)];
//[leftCAV addSubview : button];
UILabel *l1=[[UILabel alloc] init];
l1.frame=CGRectMake(0, 15, 50, 50);
l1.text=@"First line of subtitle";
l1.font=[UIFont fontWithName:@"Arial Rounded MT Bold" size:(10.0)];
UILabel *l2=[[UILabel alloc] init];
l2.frame=CGRectMake(0, 30, 50, 50);
l2.text=@"Second line of subtitle";
l2.font=[UIFont fontWithName:@"Arial Rounded MT Bold" size:(10.0)];
[leftCAV addSubview : l1];
[leftCAV addSubview : l2];
customPinView.leftCalloutAccessoryView = leftCAV;
//customPinView.
customPinView.canShowCallout = YES;
return customPinView;
I get this:
回答1:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"loc"];
// Button
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, 0, 23, 23);
button.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
[button setImage:[UIImage imageNamed:yourImageName] forState:UIControlStateNormal];
[advertButton addTarget:self action:@selector(buttonPress:) forControlEvents:UIControlEventTouchUpInside];
annView.rightCalloutAccessoryView = button;
// Image and two labels
UIView *leftCAV = [[UIView alloc] initWithFrame:CGRectMake(0,0,23,23)];
[leftCAV addSubview : yourImageView];
[leftCAV addSubview : yourFirstLabel];
[leftCAV addSubview : yourSecondLabel];
annotationView.leftCalloutAccessoryView = leftCAV;
annotationView.canShowCallout = YES;
return pin;
}
UPDATE
The default style for annotations only supports the title and subtitle. Neither title nor subtitle can include line breaks. You cannot do this without subclassing.
To use a custom view review Apple's sample code:
http://developer.apple.com/library/ios/#samplecode/WeatherMap/Introduction/Intro.html
I also think there is a problem in your code
UILabel *l1=[[UILabel alloc] init];
l1.frame=CGRectMake(0, 15, 50, 50);
l1.text=@"First line of subtitle";
l1.font=[UIFont fontWithName:@"Arial Rounded MT Bold" size:(10.0)];
UILabel *l2=[[UILabel alloc] init];
l2.frame=CGRectMake(0, 30, 50, 50);
l2.text=@"Second line of subtitle";
l2.font=[UIFont fontWithName:@"Arial Rounded MT Bold" size:(10.0)];
[leftCAV addSubview : l1];
[leftCAV addSubview : l2];
l1 has a frame (0, 15, 50, 50) and l2 has (0, 30, 50, 50). Wont these two overlap? I mean l1 will start from y=15 and its height is 50. so when l2 starts from 30 it may overlap.. Can you pls check by changing the frames
回答2:
plz use this
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
if ([annotation isKindOfClass:[CustomAnnotation class]]) {
CustomAnnotation *customAnnotation = (CustomAnnotation *) annotation;
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:@"CustomAnnotation"];
if (annotationView == nil)
annotationView = customAnnotation.annotationView;
else
annotationView.annotation = annotation;
//Adding multiline subtitle code
UILabel *subTitlelbl = [[UILabel alloc]init];
subTitlelbl.text = @"sri ganganagar this is my home twon.sri ganganagar this is my home twon.sri ganganagar this is my home twon. ";
annotationView.detailCalloutAccessoryView = subTitlelbl;
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:subTitlelbl attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:150];
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:subTitlelbl attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0];
[subTitlelbl setNumberOfLines:0];
[subTitlelbl addConstraint:width];
[subTitlelbl addConstraint:height];
return annotationView;
} else
return nil;
}
回答3:
This is how I achieved it without subclassing the annotation view, or using undocumented APIs:
MKAnnotationView* pinView = (MKAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:@"CustomPinAnnotationView"];
// Add a custom view to the the callout to allow mutli-line text
UIView *calloutView = [[UIView alloc] initWithFrame:CGRectMake(0, 10, 250, 250)];
UIView *calloutViewHeight = [[UIView alloc] initWithFrame:CGRectMake(0, 10, 1, 250)]; // The reason for this will become apparent below
UILabel *title = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 250, 21)];
[title setText:<<your single-line title text>>];
[calloutView addSubview:title];
UILabel *subtitle = [[UILabel alloc] initWithFrame:CGRectMake(10, 35, 250, 250)];
[subtitle setNumberOfLines:0];
[subtitle setText:<<your multi-line title text>>];
CGSize subtitleSize = [subtitle sizeThatFits:CGSizeMake(250, 250)];
[subtitle setFrame:CGRectMake(10, 35, 250, subtitleSize.height)];
[calloutView addSubview:subtitle];
[calloutView setFrame:CGRectMake(0, 10, 250, 35+subtitleSize.height+10)];
[calloutViewHeight setFrame:CGRectMake(0, 0, 1, 35+subtitleSize.height+10)];
pinView.leftCalloutAccessoryView = calloutView;
pinView.rightCalloutAccessoryView = calloutViewHeight; // The height of the callout is calculated by the right accessory *not* the left, so this fake view is required
You can customize the font/color of the text by adjusting title
and subtitle
accordingly.
Sure it's a bit of a hack, but it achieves the desired results without subclassing, and allows you to customize with whatever additional buttons/images you'd like to add calloutView
(and you can still place buttons in the right-accessory view if you wish, as long as you retain the height).
回答4:
Two lines in a callout can't be done without getting into unsupported APIs. If you do that, your app will be rejected.
If you have an iPad, you can see an example of a customized callout. Their callout transforms into a popout view, which sticks to the map location in the same way as a callout. So it may be that in the next version of iOS, Apple will expand the callout API. For now you're stuck with one line subtitles.
If you really want to do this, you'll have to make your own callout class and make it stick to the map as it's scrolled. This would probably be difficult.
Another idea is to fake a callout. Make a custom annotation view containing 2 views. The first is a map pin. The second is a callout. When the pin receives a touch, you can unhide your callout view. It will be missing the "pop" animation, but you can put as many lines in as you like.
I've never tried this and it sounds like there would be lots of details to get right, but it might give you a start.
回答5:
For setting the button you can use the below code
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *x=[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"someIdentifier"];
UIButton *button=[UIButton buttonWithType:UIButtonTypeCustom];
//set the target, title, image etc.
[x setRightCalloutAccessoryView:button];
return [x autorelease];
}
回答6:
You won't be able to do it with public API and will have to reimplement your own custom annotation view.
You can have a look at this code that reimplements the annotation view but mimics the standard iOS one, and you can then enrich it to your own need :
http://blog.asolutions.com/2010/09/building-custom-map-annotation-callouts-part-1/
回答7:
Result Image
This solution sets "justified" text, hope be useful:
Swift 3:
import MapKit
extension MKAnnotationView {
func setCustomLines(customLines lines: [String]) {
if lines.count <= 0 {
return
}
// get longest line
var longest = NSString()
if let max = lines.max(by: { $1.characters.count > $0.characters.count }) {
longest = max as NSString
}
else {
fatalError("Can't get longest line.")
}
// get longest text width
let font = UIFont.systemFont(ofSize: 15.0)
let width = (longest.size(attributes: [NSFontAttributeName: font])).width
// get text from lines
var text = lines.first
for i in 1..<lines.count {
text = text! + " " + lines[i]
}
// label
let label = UILabel()
label.text = text
label.font = font
label.numberOfLines = lines.count
label.textAlignment = .justified
// set width of label
let con = NSLayoutConstraint(item: label, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: width)
label.addConstraint(con)
self.detailCalloutAccessoryView = label
}
}
回答8:
It can be done in 9 steps.
I created a label and added it to the annotationView?.detailCalloutAccessoryView
property (step 5). I also set the label's text to the annotation.subtitle
text (step 2 & step 4). I set the label's .numberOfLines = 0
.This will make the subtitle extend however many # of lines needed (set to 2 if you only need 2).
For the button I created a UIButton and set it's type to .detailDisclosure
(step 7). I then set the button's image property to the custom image I want to use (step 8). And finally I set the button to the annotationView's .rightCalloutAccessoryView
property (step 9).
I also offset the annotationView's .calloutOffset
property (step 6). I got the offset from ray wenderlich
Steps are in the comments above the code
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isKindOfClass(MKUserLocation) {
return nil
}
let reuseIdentifier = "reuseIdentifier"
var annotationView = mapView.mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier) as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
// 1. set the annotationView's canShowCallout property to true
annotationView?.canShowCallout = true
// 2. get the subtitle text from the annontation's subtitle property
let subtitleText = annotation.subtitle ?? "you have no subtitle"
// 3. create a label for the subtitle text
let subtitleLabel = UILabel()
subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
// 4. set the subtitle's text to the label's text property and number of lines to 0
subtitleLabel.text = subtitleText
subtitleLabel.numberOfLines = 0
// 5. set the annotation's detailCalloutAccessoryView property to the subtitleLabel
annotationView?.detailCalloutAccessoryView = subtitleLabel
// 6. offset the annotationView's .calloutOffset property
annotationView?.calloutOffset = CGPoint(x: -5, y: 5)
// 7. create a button and MAKE SURE to set it's type to .detailDisclosure or it won't appear
let button = UIButton(type: .detailDisclosure)
// 8. set the button's image to whatever custom UIImage your using
button.setImage(UIImage(named: "yourCustomImage"), for: .normal)
// 9. set the button to the annotationView's .rightCalloutAccessoryView property
annotationView?.rightCalloutAccessoryView = button
} else {
annotationView!.annotation = annotation
}
return annotationView
}