จะปรับแต่งฟองคำบรรยายภาพสำหรับ MKAnnotationView ได้อย่างไร


88

ฉันกำลังทำงานกับ mapkit และฉันติดอยู่

ฉันมีมุมมองคำอธิบายประกอบแบบกำหนดเองที่ฉันใช้อยู่และฉันต้องการใช้คุณสมบัติรูปภาพเพื่อแสดงจุดบนแผนที่ด้วยไอคอนของฉันเอง ฉันทำงานได้ดี แต่สิ่งที่ฉันต้องการทำคือการลบล้างมุมมองคำบรรยายเริ่มต้น (ฟองอากาศที่แสดงพร้อมชื่อเรื่อง / คำบรรยายเมื่อแตะไอคอนคำอธิบายประกอบ) ฉันต้องการควบคุมไฮไลต์เองได้: mapkit ให้การเข้าถึงเฉพาะมุมมองไฮไลต์เสริมด้านซ้ายและด้านขวาเท่านั้น แต่ไม่มีวิธีใดที่จะให้มุมมองที่กำหนดเองสำหรับฟองคำบรรยายภาพหรือกำหนดขนาดเป็นศูนย์หรืออย่างอื่น

ความคิดของฉันคือการแทนที่ selectAnnotation / deselectAnnotation ในของฉันMKMapViewDelegateจากนั้นวาดมุมมองที่กำหนดเองของฉันเองโดยการโทรไปยังมุมมองคำอธิบายประกอบที่กำหนดเองของฉัน สิ่งนี้ใช้ได้ แต่เมื่อcanShowCalloutถูกตั้งค่าเป็นYESคลาสมุมมองคำอธิบายประกอบที่กำหนดเองของฉันเท่านั้น วิธีการเหล่านี้จะไม่ถูกเรียกหากฉันตั้งค่านี้เป็นNO(ซึ่งเป็นสิ่งที่ฉันต้องการเพื่อไม่ให้มีการดึงฟองคำบรรยายเริ่มต้น) ดังนั้นฉันจึงไม่มีทางรู้ได้ว่าผู้ใช้แตะจุดของฉันบนแผนที่ (เลือกไว้) หรือแตะจุดที่ไม่ได้เป็นส่วนหนึ่งของมุมมองคำอธิบายประกอบของฉัน (เลือกที่จะเลือก) โดยไม่ต้องมีมุมมองคำบรรยายภาพเริ่มต้นปรากฏขึ้น

ฉันพยายามไปตามเส้นทางอื่นและจัดการเหตุการณ์การสัมผัสทั้งหมดด้วยตัวเองในแผนที่และดูเหมือนจะไม่สามารถใช้งานได้ ฉันอ่านโพสต์อื่น ๆ ที่เกี่ยวข้องกับการจับเหตุการณ์การสัมผัสในมุมมองแผนที่ แต่ไม่ตรงกับที่ฉันต้องการ มีวิธีเจาะลึกลงไปในมุมมองแผนที่เพื่อลบฟองคำบรรยายก่อนวาดหรือไม่ ฉันกำลังสูญเสีย

ข้อเสนอแนะใด ๆ ? ฉันขาดอะไรบางอย่างที่ชัดเจนหรือไม่?


ลิงก์นี้ใช้ไม่ได้ แต่พบโพสต์เดียวกันที่นี่ -> การสร้างคำอธิบายประกอบคำอธิบายประกอบแผนที่ที่กำหนดเอง - ส่วนที่ 1
Fede Mika

2
คุณสามารถอ้างถึงโครงการนี้เพื่อดูการสาธิตอย่างรวดเร็ว github.com/akshay1188/CustomAnnotationรวบรวมคำตอบด้านบนไว้ในการสาธิตที่ใช้งานอยู่
akshay1188

คำตอบ:


54

มีวิธีที่ง่ายกว่านี้

สร้างแบบกำหนดเอง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

6
อืม - ไม่ได้ผล! มันแทนที่คำอธิบายประกอบแผนที่ (เช่นหมุด) ไม่ใช่คำบรรยายภาพ "ฟอง"
PapillonUK

2
วิธีนี้ใช้ไม่ได้ MKAnnotationView ไม่มีการอ้างอิงถึง mapview ดังนั้นคุณจึงมีปัญหาหลายประการในการเพิ่มคำบรรยายภาพที่กำหนดเอง ตัวอย่างเช่นคุณไม่รู้ว่าการเพิ่มสิ่งนี้เป็นมุมมองย่อยจะเป็นการปิดหน้าจอหรือไม่
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

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 (เช่น "ทั้งหมด") เพื่อให้ปรับขนาดได้อย่างถูกต้องในภาพเคลื่อนไหว

: - โจ


Btw คุณสามารถสร้างภาพเคลื่อนไหวในคุณสมบัติมาตราส่วนมากกว่าเฟรมเพื่อให้ได้ขนาดเนื้อหาที่เหมาะสม
เกิด

อย่าใช้ CGRectMake ใช้ CGTransform
Cameron Lowell Palmer

@occulus - ฉันคิดว่าฉันได้ลองก่อน แต่มีปัญหาในการแสดงผลใน iOS 4 แต่นั่นอาจเป็นเพราะฉันไม่ได้ใช้CABasicAnimation... นานเกินไปที่จะจำได้! ฉันรู้ว่าฉันต้องทำให้คุณสมบัติ y เคลื่อนไหวด้วยเช่นกันเนื่องจากการชดเชยศูนย์
jowie

@CameronLowellPalmer - ทำไมไม่ใช้CGRectMakeล่ะ?
jowie

@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เป็น a UICalloutViewคุณก็สามารถคาดคั้นได้และมีอะไรอยู่ข้างใน


1
คุณจะตรวจสอบได้อย่างไรว่ามุมมองย่อยเป็น UICalloutView เนื่องจาก UICalloutview ไม่ใช่คลาสสาธารณะ
Manish Ahuja

if ([[[subview class] description] isEqualToString:@"UICalloutView"]) ฉันคิดว่ามีวิธีที่ดีกว่านี้ แต่ได้ผล
яοвοτағτєяаււ

คุณเคยมีปัญหากับเรื่องนี้หรือไม่เนื่องจาก Manish ชี้ให้เห็นว่ามันเป็นคลาสส่วนตัว
Daniel

พวกเขาอาจเปลี่ยนโครงสร้าง 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วัตถุและตั้งชื่อเรื่องของตัวเองหลังจากนั้นจะเรียกอีกสองวิธีที่ค่อนข้างน่าเกลียดซึ่งปรับความกว้างและขนาดของเนื้อหาคำบรรยายภาพและหลังจากนั้นให้วาดฟองคำพูดรอบ ๆ ในตำแหน่งที่ถูกต้อง

ในที่สุดมุมมองจะถูกเพิ่มเป็นมุมมองย่อยไปยัง mapView ปัญหาในการแก้ปัญหานี้คือยากที่จะทำให้คำบรรยายภาพอยู่ในตำแหน่งที่ถูกต้องเมื่อเลื่อนมุมมองแผนที่ ฉันแค่ซ่อนคำบรรยายภาพในวิธีการมอบสิทธิ์มุมมองแผนที่ในการเปลี่ยนแปลงภูมิภาคเพื่อแก้ปัญหานี้

ใช้เวลาพอสมควรในการแก้ปัญหาทั้งหมด แต่ตอนนี้คำบรรยายภาพเกือบจะทำงานเหมือนอย่างเป็นทางการ แต่ฉันมีในสไตล์ของฉันเอง


ประการที่สอง: UICalloutView เป็นคลาสส่วนตัวและไม่มีใน SDK อย่างเป็นทางการ ทางออกที่ดีที่สุดคือทำตามคำแนะนำของ Sascha
JoePasq

สวัสดี Sascha เราขอขอบคุณสำหรับการตอบกลับของคุณ แต่ปัญหาปัจจุบันที่เราพบคือทำอย่างไรให้ได้ตำแหน่ง (x, y และไม่ใช่ lat หรือ long) บนแผนที่ที่หมุดปรากฏเพื่อให้เราสามารถแสดงมุมมองคำบรรยาย
Zach

1
การแปลงพิกัดสามารถทำได้อย่างง่ายดายด้วยสองฟังก์ชั่นของ MKMapView: # - convertCoordinate: toPointToView: # - convertPoint: toCoordinateFromView:
Sascha Konietzke

5

โดยทั่วไปในการแก้ปัญหานี้เราต้อง: a) ป้องกันไม่ให้ฟองคำบรรยายเริ่มต้นปรากฏขึ้น b) ดูว่ามีการคลิกคำอธิบายประกอบใด

ฉันสามารถบรรลุสิ่งเหล่านี้ได้โดย: a) การตั้งค่า canShowCallout เป็น NO b) subclassing, MKPinAnnotationView และการแทนที่วิธีการ touchBegan และ touchEnd

หมายเหตุ: คุณต้องจัดการเหตุการณ์การสัมผัสสำหรับ MKAnnotationView ไม่ใช่ MKMapView


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 เป็นUIViewขนาด 320x100 - showCallout เป็นบูล - เป็นฟังก์ชั่นที่ไม่เหมือนมุมมองภาพเคลื่อนไหว[self animateIn];UIAlertView


selectedCoordinate ระบุอะไร ประเภทของ Object คืออะไร?
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.