自定义iOS8标注泡泡(Swift) [英] Customise iOS8 Callout bubble (Swift)

查看:177
本文介绍了自定义iOS8标注泡泡(Swift)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想自定义iOS8 MapView Callout气泡,点击MKAnnotationView时可以看到它。默认气泡有点限制(只有标题,副标题和2个附件视图)所以我很难找到替代解决方案。这里有两种可能的方式和我面临的相关问题:

I want to customise the iOS8 MapView Callout bubble which get visualised when clicking on a MKAnnotationView. The Default bubble is a bit limiting (having only Title,Subtitle and 2 accessory view) so I'm struggling to find an alternative solution. Here two possible ways and the relative problems I'm facing:

问题1)创建一个自定义的调用气泡

挖掘 Apple文档我发现了这个:


当你使用自定义视图而不是标准标注时,你需要
做额外的工作当用户与其互动时,请确保您的标注显示并隐藏
。下面的步骤概述了
创建包含按钮的自定义标注的过程:

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:

设计一个代表自定义
标注的NSView或UIView子类。子类可能需要实现
drawRect:方法来绘制自定义内容。创建一个视图控制器
,初始化标注视图并执行与
按钮相关的操作。在注释视图中,实现hitTest:以响应注释视图边界之外但在
callout视图边界内的
命中,如清单6-7所示。在注释
视图中,实现setSelected:animated:在用户单击或点击注释视图时,将您的标注视图添加为注释视图的
子视图。如果
callout视图在用户选择时已经可见,则
setSelected:方法应该从
注释视图中删除callout子视图(参见清单6-8)。在注释视图的
initWithAnnotation:方法中,将canShowCallout属性设置为NO,以
阻止地图在用户
选择注释时显示标准标注。清单6-7显示了实现
hitTest的示例:处理callout视图中可能位于注释视图的
范围之外的命中。

Design an NSView or UIView subclass that represents the custom callout. It’s likely that the subclass needs to implement the drawRect: 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, implement hitTest: 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, implement setSelected: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, the setSelected: method should remove the callout subview from the annotation view (see Listing 6-8). In the annotation view’s initWithAnnotation: method, set the canShowCallout property to NO to prevent the map from displaying the standard callout when the user selects the annotation. Listing 6-7 shows an example of implementing hitTest: to handle hits in the callout view that might be outside the bounds of the annotation view.



Listing 6-7  Responding to hits within a custom callout
- (NSView *)hitTest:(NSPoint)point
{
    NSView *hitView = [super hitTest:point];
    if (hitView == nil && self.selected) {
        NSPoint pointInAnnotationView = [self.superview convertPoint:point toView:self];
        NSView *calloutView = self.calloutViewController.view;
        hitView = [calloutView hitTest:pointInAnnotationView];
    }
    return hitView;
}




清单6-8显示了一个实现示例当
用户选择注释视图时,setSelected:animated:to
动画显示自定义标注视图的到达和解除。

Listing 6-8 shows an example of implementing setSelected:animated: to animate the arrival and dismissal of a custom callout view when the user selects the annotation view.



Listing 6-8  Adding and removing a custom callout view
- (void)setSelected:(BOOL)selected
{
    [super setSelected:selected];

    // Get the custom callout view.
    NSView *calloutView = self.calloutViewController.view;
    if (selected) {
        NSRect annotationViewBounds = self.bounds;
        NSRect calloutViewFrame = calloutView.frame;
      // Center the callout view above and to the right of the annotation view.
        calloutViewFrame.origin.x = -(NSWidth(calloutViewFrame) - NSWidth(annotationViewBounds)) * 0.5;
        calloutViewFrame.origin.y = -NSHeight(calloutViewFrame) + 15.0;
        calloutView.frame = calloutViewFrame;

        [self addSubview:calloutView];
    } else {
        [calloutView.animator removeFromSuperview];
    }
}

现在,当我尝试转换此Objective-C时代码到Swift我找不到这个属性:

Now, when I try to convert this Objective-C code to Swift I cannot find this property:

NSView *calloutView = self.calloutViewController.view;

如何访问标注气泡视图?

How can I access the callout bubble view?

问题2)修改默认播放气泡

如前所述,显示的默认标注包含标题,副标题和2个附件视图。我注意到我不能改变字符串的字体样式或泡泡的颜色。此外,如果我的标题有超过24个字符我的配件视图定位搞砸了。
如何避免此问题?

As said before, the default callout displayed has title,subtitle and 2 accessory view. I noticed I cannot change much about the font style of the strings or the colour of the bubble. Also if my title has more then 24 characters my accessory views positioning gets messed up. How can I avoid this problem?

推荐答案

calloutViewController是自定义标注视图的一部分,用于处理事件。你不会在MapKit或其他地方找到它。

苹果的说明很好。要创建自己的标注,您应该按照以下步骤操作:

calloutViewController is a part of custom callout view to handle events. You won't find it in MapKit or elsewhere.
Apples instructions are good. To create your own callout you should follow steps:

1. Create custom MKAnnotationView or MAPinAnnotationView
2. Override setSelected and hitTest methods in your annotation
3. Create your own callout view
4. Override hitTest and pointInside in you callout view
5. Implement MapView delegate methods didSelectAnnotationView, didDeselectAnnotationView

我最终得到了这些允许我处理内部触摸的解决方案callout视图不会丢失选择。

I have ended up with these solution that allows me to handle touches inside callout view without losing selection.

class MapPin: MKAnnotationView {
    class var reuseIdentifier:String {
        return "mapPin"
    }

    private var calloutView:MapPinCallout?
    private var hitOutside:Bool = true

    var preventDeselection:Bool {
        return !hitOutside
    }


    convenience init(annotation:MKAnnotation!) {
        self.init(annotation: annotation, reuseIdentifier: MapPin.reuseIdentifier)

        canShowCallout = false;
    }

    override func setSelected(selected: Bool, animated: Bool) {
        let calloutViewAdded = calloutView?.superview != nil

        if (selected || !selected && hitOutside) {
            super.setSelected(selected, animated: animated)
        }

        self.superview?.bringSubviewToFront(self)

        if (calloutView == nil) {
            calloutView = MapPinCallout()
        }

        if (self.selected && !calloutViewAdded) {
            addSubview(calloutView!)
        }

        if (!self.selected) {
            calloutView?.removeFromSuperview()
        }
    }

    override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
        var hitView = super.hitTest(point, withEvent: event)

        if let callout = calloutView {
            if (hitView == nil && self.selected) {
                hitView = callout.hitTest(point, withEvent: event)
            }
        }

        hitOutside = hitView == nil

        return hitView;
    }
}



标注视图



Callout view

class MapPinCallout: UIView {
    override func hitTest(var point: CGPoint, withEvent event: UIEvent?) -> UIView? {
        let viewPoint = superview?.convertPoint(point, toView: self) ?? point

        let isInsideView = pointInside(viewPoint, withEvent: event)

        var view = super.hitTest(viewPoint, withEvent: event)

        return view
    }

    override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
        return CGRectContainsPoint(bounds, point)
    }
}

如果你需要别的东西,但按钮在callout中响应,在hitTest返回视图之前添加代码来处理特定视图中的触摸

If you need something else but buttons be responsive in callout add code to handle touches in specific views before hitTest returns view

if calloutState == .Expanded && CGRectContainsPoint(tableView.frame, viewPoint) {
    view = tableView.hitTest(convertPoint(viewPoint, toView: tableView), withEvent: event)
}



委托方法



Delegate methods

func mapView(mapView: MKMapView!, didSelectAnnotationView view: MKAnnotationView!) {
    if let mapPin = view as? MapPin {
        updatePinPosition(mapPin)
    }
}

func mapView(mapView: MKMapView!, didDeselectAnnotationView view: MKAnnotationView!) {
    if let mapPin = view as? MapPin {
        if mapPin.preventDeselection {
            mapView.selectAnnotation(view.annotation, animated: false)
        }
    }
}

func updatePinPosition(pin:MapPin) {
    let defaultShift:CGFloat = 50
    let pinPosition = CGPointMake(pin.frame.midX, pin.frame.maxY)

    let y = pinPosition.y - defaultShift

    let controlPoint = CGPointMake(pinPosition.x, y)
    let controlPointCoordinate = mapView.convertPoint(controlPoint, toCoordinateFromView: mapView)

    mapView.setCenterCoordinate(controlPointCoordinate, animated: true)
}

这篇关于自定义iOS8标注泡泡(Swift)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆