Làm cách nào để tùy chỉnh bong bóng chú thích cho MKAnnotationView?


88

Tôi hiện đang làm việc với bộ bản đồ và bị mắc kẹt.

Tôi đang sử dụng chế độ xem chú thích tùy chỉnh và tôi muốn sử dụng thuộc tính hình ảnh để hiển thị điểm trên bản đồ với biểu tượng của riêng tôi. Tôi có cái này hoạt động tốt. Nhưng những gì tôi cũng muốn làm là ghi đè chế độ xem chú thích mặc định (bong bóng hiển thị với tiêu đề / phụ đề khi chạm vào biểu tượng chú thích). Tôi muốn có thể tự kiểm soát chú thích: bộ bản đồ chỉ cung cấp quyền truy cập vào các chế độ xem chú thích phụ bên trái và bên phải, nhưng không có cách nào để cung cấp chế độ xem tùy chỉnh cho bong bóng chú thích hoặc để cung cấp cho nó kích thước bằng 0 hoặc bất kỳ thứ gì khác.

Ý tưởng của tôi là ghi đè selectAnnotation / deselectAnnotation trong của tôi MKMapViewDelegate, sau đó vẽ chế độ xem tùy chỉnh của riêng tôi bằng cách gọi đến chế độ xem chú thích tùy chỉnh của tôi. Điều này hoạt động, nhưng chỉ khi canShowCalloutđược đặt thànhYES trong lớp chế độ xem chú thích tùy chỉnh của tôi. Các phương thức này KHÔNG được gọi nếu tôi đã đặt thiết lập này NO(đó là những gì tôi muốn, để bong bóng chú thích mặc định không được vẽ). Vì vậy, tôi không có cách nào để biết liệu người dùng đã chạm vào điểm của tôi trên bản đồ (đã chọn nó) hoặc chạm vào một điểm không phải là một phần của chế độ xem chú thích của tôi (đã cắt nó) mà không có chế độ xem bong bóng chú thích mặc định hiển thị.

Tôi đã thử đi xuống một con đường khác và chỉ tự mình xử lý tất cả các sự kiện chạm trong bản đồ và dường như tôi không thể làm việc này. Tôi đã đọc các bài đăng khác liên quan đến bắt các sự kiện chạm trong chế độ xem bản đồ, nhưng chúng không chính xác như những gì tôi muốn. Có cách nào để tìm hiểu chế độ xem bản đồ để loại bỏ bong bóng chú thích trước khi vẽ không? Tôi thua thiệt.

Bất kỳ đề xuất? Tôi có thiếu một cái gì đó rõ ràng không?


Liên kết này không làm việc, nhưng không tìm thấy bài viết tương tự ở đây -> Xây dựng Bản đồ tùy chỉnh Chú Callouts - Phần 1
Fede Mika

2
Bạn có thể tham khảo dự án này để có bản demo nhanh. github.com/akshay1188/CustomAnnotation Tổng hợp các câu trả lời ở trên thành một bản demo đang chạy.
akshay1188

Câu trả lời:


53

Có một giải pháp thậm chí còn dễ dàng hơn.

Tạo một tùy chỉnh UIView (cho chú thích của bạn).

Sau đó, tạo một lớp con MKAnnotationViewvà ghi đè setSelectednhư sau:

- (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...
    }
}

Boom, hoàn thành công việc.


8
chào TappCandy! Đầu tiên, cảm ơn cho giải pháp của bạn. Nó hoạt động, nhưng khi tải một chế độ xem (tôi đang thực hiện nó từ tệp nib) thì các nút không hoạt động. Tôi có thể làm gì để chúng hoạt động bình thường ?? Cảm ơn
Frade

Yêu thích sự đơn giản của giải pháp này!
Eric Brotto

6
Hmm - điều này không hoạt động! Nó thay thế chú thích bản đồ (tức là ghim) KHÔNG PHẢI là "bong bóng" chú thích.
PapillonUK

2
Điều này sẽ không hoạt động. MKAnnotationView không có tham chiếu đến mapview, vì vậy bạn gặp một số vấn đề khi thêm chú thích tùy chỉnh. Ví dụ: bạn không biết nếu thêm điều này làm lượt xem phụ có bị ngoại hình hay không.
Cameron Lowell Palmer

@PapillonUK Bạn có quyền kiểm soát khung của chú thích. Nó không phải là thay thế pin, nó đang xuất hiện trên đầu trang của nó. Điều chỉnh vị trí của nó khi bạn thêm nó làm lượt xem phụ.
Ben Packard

40

detailCalloutAccessoryView

Ngày xưa đây là một nỗi đau, nhưng Apple đã giải quyết được nó, chỉ cần kiểm tra tài liệu trên MKAnnotationView

view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.detailCalloutAccessoryView = UIImageView(image: UIImage(named: "zebra"))

Thực sự, đó là nó. Mất bất kỳ UIView nào.


Cái nào là tốt nhất để sử dụng?
Satyam

13

Tiếp tục từ câu trả lời đơn giản tuyệt vời của @ TappCandy, nếu bạn muốn tạo hoạt ảnh bong bóng của mình theo cách giống như cách mặc định, tôi đã tạo ra phương pháp hoạt ảnh này:

- (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);
            }];
        }];
    }];
}

Nó trông khá phức tạp, nhưng miễn là điểm của bong bóng chú thích của bạn được thiết kế ở giữa, bạn chỉ có thể thay thế myBubbleWidthmyBubbleHeightvới kích thước của riêng bạn để nó hoạt động. Và hãy nhớ đảm bảo rằng các lượt xem phụ của bạn cóautoResizeMask tính được đặt thành 63 (tức là "tất cả") để chúng chia tỷ lệ chính xác trong hoạt ảnh.

: -Joe


Btw, bạn có thể hoạt ảnh trên thuộc tính tỷ lệ thay vì khung, để có được tỷ lệ phù hợp của nội dung.
Occulus

Không sử dụng CGRectMake. Sử dụng CGTransform.
Cameron Lowell Palmer,

@occulus - Tôi nghĩ rằng tôi đã thử điều đó đầu tiên, nhưng gặp sự cố hiển thị trong iOS 4. Nhưng đó có thể là do tôi đã không sử dụng CABasicAnimation... Quá lâu để nhớ! Tôi biết tôi cũng sẽ phải tạo hiệu ứng cho thuộc tính y, do phần bù trung tâm.
jowie,

@CameronLowellPalmer - tại sao không sử dụng CGRectMake?
jowie,

@jowie Vì cú pháp tốt hơn và tránh các giá trị ma thuật trong mã của bạn. CGAffineTransformMakeScale (1.1f, 1.1f); Để tăng hộp 110% là rõ ràng. Cách bạn đã đi về nó có thể hiệu quả, nhưng nó không đẹp.
Cameron Lowell Palmer,

8

Tìm thấy đây là giải pháp tốt nhất cho tôi. Bạn sẽ phải sử dụng một số khả năng sáng tạo để thực hiện các tùy chỉnh của riêng mình

Trong MKAnnotationViewlớp con của bạn , bạn có thể sử dụng

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

Và nếu subviewlà a UICalloutView, thì bạn có thể vặn nó với nó và những gì bên trong nó.


1
làm cách nào để bạn kiểm tra xem lượt xem phụ có phải là UICalloutView hay không vì UICalloutview không phải là một lớp công khai.
Manish Ahuja

if ([[[subview class] description] isEqualToString:@"UICalloutView"]) Tôi nghĩ có một cách tốt hơn, nhưng cách này hiệu quả.
яοвοτағτєяа ււ

Bạn có gặp vấn đề gì với điều này không vì như Manish đã chỉ ra, đó là một lớp học riêng.
Daniel

Có thể là họ đã thay đổi cấu trúc UIView cho calloutviews. Tôi chưa nâng cấp ứng dụng sử dụng cái này, vì vậy bạn tự làm: P
яοвοτағτєяа ււ

6

Tôi đã từng gặp vấn đề tương tự. Có rất nhiều bài đăng trên blog về chủ đề này trên blog này http://spitzkoff.com/craig/?p=81 .

Chỉ sử dụng MKMapViewDelegatekhông giúp bạn ở đây và phân lớp MKMapViewvà cố gắng mở rộng chức năng hiện có cũng không hiệu quả với tôi.

Cuối cùng, những gì tôi đã làm là tạo ra cái của riêng tôi CustomCalloutViewmà tôi đang có trên đầu MKMapView. Bạn có thể tạo kiểu cho chế độ xem này theo bất kỳ cách nào bạn muốn.

My CustomCalloutViewcó một phương pháp tương tự như phương pháp này:


- (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];
}

Nó lấy một MKAnnotationđối tượng và đặt tiêu đề của chính nó, sau đó nó gọi hai phương thức khác khá xấu để điều chỉnh chiều rộng và kích thước của nội dung chú thích và sau đó vẽ bong bóng thoại xung quanh nó ở vị trí chính xác.

Cuối cùng chế độ xem được thêm làm chế độ xem phụ vào mapView. Vấn đề với giải pháp này là khó có thể giữ chú thích ở vị trí chính xác khi chế độ xem bản đồ được cuộn. Tôi chỉ ẩn chú thích trong phương pháp đại biểu chế độ xem bản đồ về sự thay đổi khu vực để giải quyết vấn đề này.

Phải mất một thời gian để giải quyết tất cả những vấn đề đó, nhưng bây giờ chú thích gần như hoạt động như chính thức, nhưng tôi có nó theo phong cách riêng của mình.


Tôi thứ hai rằng: UICalloutView là một lớp riêng tư và không có sẵn trong SDK chính thức. Đặt cược tốt nhất của bạn là làm theo lời khuyên của Sascha.
JoePasq

Xin chào Sascha, Chúng tôi cảm ơn bạn đã trả lời. Tuy nhiên, vấn đề hiện tại mà chúng tôi đang gặp phải là làm thế nào để có được vị trí (x, y chứ không phải vĩ độ hay độ dài) trên bản đồ nơi các ghim xuất hiện, để chúng tôi có thể hiển thị chế độ xem chú thích.
Zach

1
chuyển đổi tọa độ có thể dễ dàng được thực hiện bằng hai chức năng của MKMapView: # - convertCoordinate: toPointToView: # - convertPoint: toCoordinateFromView:
Sascha Konietzke

5

Về cơ bản để giải quyết điều này, người ta cần: a) Ngăn bong bóng chú thích mặc định xuất hiện. b) Tìm ra chú thích nào đã được nhấp.

Tôi đã có thể đạt được những điều này bằng cách: a) đặt canShowCallout thành KHÔNG b) phân lớp, MKPinAnnotationView và ghi đè các phương thức touchBegan và touchEnd.

Lưu ý: Bạn cần xử lý các sự kiện cảm ứng cho MKAnnotationView chứ không phải MKMapView


3

Tôi chỉ nghĩ ra một cách tiếp cận, ý tưởng ở đây là

  // 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];
    }
}

Đây - testview có UIViewkích thước 320x100 - showCallout là BOOL - [self animateIn];là chức năng xem hoạt ảnh như thế nào UIAlertView.


Lựa chọn cấp dưới chỉ định những gì? Loại Đối tượng đó là gì?
Pradeep Reddy Kypa

nó là đối tượng CLLocationCoosystem2d.
bharathi kumar

1

Bạn có thể sử dụng leftCalloutView, đặt annotation.text thành @ ""

Vui lòng tìm mã ví dụ bên dưới:

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
Điều này sẽ 'hoạt động' ở một mức độ nào đó, nhưng không thực sự trả lời câu hỏi chung hơn được hỏi và nó rất khó hiểu.
Cameron Lowell Palmer,

1

Tôi đã đẩy ra ngã ba của SMCalloutView tuyệt vời giải quyết vấn đề bằng cách cung cấp chế độ xem tùy chỉnh cho chú thích và cho phép chiều rộng / chiều cao linh hoạt khá dễ dàng. Vẫn còn một số điều kỳ quặc cần giải quyết, nhưng nó khá hoạt động cho đến nay:

https://github.com/u10int/calloutview

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.