带按钮的自定义MKAnnotation标注气泡

Yan*_*chi 24 frame mapkit ios callout

我正在开发应用程序,用户通过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];
    }

}
Run Code Online (Sandbox Code Playgroud)

CalloutView只是简单的类,有2个属性(标签显示地方名称和按钮)和xib.

我一直在做这个自定义标注泡泡几天.我尝试使用"异步解决方案" 解决方案,但我无法添加任何其他类型的按钮然后披露按钮.

我的下一次尝试是找到比异步解决方案更容易的东西并将其修改为我的用途.多数民众赞成我如何找到tochi的自定义标注.

Tochi的自定义标注

基于他的工作,我能够自定义他的泡泡并更改我的自定义按钮的信息按钮.然而,我的问题仍然存在.为了将我的自定义标注视图放在引脚顶部,我不得不给它负帧,所以我的按钮只能在底部5个像素中"可点击".看来,我必须深入挖掘ios默认的标注气泡,将其子类化并在那里更改标注框架.但我现在真的很无望.

如果你们能告诉我正确的方法,或者给我建议,我会很高兴的.

Rob*_*Rob 79

有几种方法可以自定义标注:

  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__);
    }
    
    Run Code Online (Sandbox Code Playgroud)

    产量:

    在此输入图像描述

  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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这会产生:

    在此输入图像描述

  3. 如果您真的想自己开发自定义标注,则" 位置和地图编程指南"概述了所涉及的步骤:

    在iOS应用程序中,最好使用mapView:annotationView:calloutAccessoryControlTapped:委托方法在用户点击标注视图的控件时进行响应(只要控件是后代UIControl).在此方法的实现中,您可以发现标注视图的注释视图的标识,以便您知道用户点击了哪个注释.在Mac应用程序中,callout视图的视图控制器可以实现一种操作方法,该方法在用户单击callout视图中的控件时响应.

    当您使用自定义视图而不是标准标注时,您需要做额外的工作以确保您的标注在用户与之交互时显示和隐藏.以下步骤概述了创建包含按钮的自定义标注的过程:

    • 设计一个NSViewUIView子类,表示自定义标注.子类可能需要实现该drawRect:方法来绘制自定义内容.

    • 创建一个视图控制器,初始化标注视图并执行与按钮相关的操作.

    • 在注释视图中,实现hitTest:响应注释视图边界之外但在callout视图边界内的命中,如清单6-7所示.

    • 在注释视图中,实现setSelected:animated:在用户单击或点击注释视图时将其添加为注释视图的子视图.

    • 如果callout视图在用户选择时已经可见,则该setSelected:方法应从注释视图中删除callout子视图(参见清单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
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 我爱你Rob .. :) (2认同)