Подтвердить что ты не робот

Настройка iOS8 Callout bubble (Swift)

Я хочу настроить пузырь iOS8 MapView Callout, который визуализируется при нажатии на MKAnnotationView. По умолчанию пузырь немного ограничен (имея только Title, Subtitle и 2 аксессуар), поэтому я изо всех сил пытаюсь найти альтернативное решение. Вот два возможных пути и относительные проблемы, с которыми я сталкиваюсь:

ПРОБЛЕМА 1) СОЗДАНИЕ ОБЫЧНОГО ВЫЗОВА

Копаем документацию Apple Я нашел это:

Когда вы используете пользовательский вид вместо стандартной выноски, вам нужно сделайте дополнительную работу, чтобы убедиться, что ваши выноски показывают и прячутся соответственно когда пользователи взаимодействуют с ним. В приведенных ниже шагах описывается процесс создавая настраиваемую выноску, содержащую кнопку:

Создайте подкласс NSView или UIView, который представляет собой пользовательский вызывать. Вероятно, что подкласс должен реализовать drawRect: метод рисования вашего пользовательского контента. Создать контроллер представления который инициализирует представление выноски и выполняет действия, связанные с кнопка. В представлении аннотации выполните hitTest: для ответа на хитов, находящихся за пределами границ аннотаций, но внутри выноски, как показано в листинге 6-7. В аннотации view, реализовать setSelected: анимированный: добавить ваше представление выноска в качестве subview в представлении аннотации, когда пользователь щелкает или зажимает его. Если вид выноски уже отображается, когда пользователь выбирает его, setSelected: метод должен удалить подзаголовок callout из аннотация (см. листинг 6-8). В аннотациях initWithAnnotation: метод, установите для свойства canShowCallout значение NO чтобы карта не отображала стандартную выноску, когда пользователь выбирает аннотацию. В листинге 6-7 показан пример реализации hitTest: обрабатывать удары в виде выноски, которые могут находиться вне границы аннотационного представления.

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  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, я не могу найти это свойство:

NSView *calloutView = self.calloutViewController.view;

Как я могу получить доступ к виду пузырьков выносок?

ПРОБЛЕМА 2) ИЗМЕНЕНИЕ ВЫЗОВА DEFAULT CALLOUT BUBBLE

Как уже говорилось, отображаемая по умолчанию выноска имеет название, субтитры и 2 вида аксессуаров. Я заметил, что не могу многое изменить стиль шрифта строк или цвет пузыря. Кроме того, если мой заголовок имеет более 24 символов, мое расположение позиций в аксессуарах перепуталось. Как я могу избежать этой проблемы?

4b9b3361

Ответ 1

calloutViewController является частью пользовательского представления выносок для обработки событий. Вы не найдете его в MapKit или в другом месте.
Инструкции яблок хороши. Чтобы создать свою собственную выноску, вы должны выполнить следующие шаги:

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

Я закончил с этим решением, которое позволяет мне обрабатывать штрихи внутри вида выноски без потери выбора.

Аннотация

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

Вид выноска

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)
    }
}

Если вам нужно что-то еще, но кнопки будут реагировать в выноске, добавьте код для обработки штрихов в определенных представлениях, прежде чем hitTest вернет представление

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

Методы делегата

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)
}