Настройка пузыря выноски iOS8 (Swift)
Я хочу настроить всплывающую подсказку iOS8 MapView Callout, которая отображается при нажатии на MKAnnotationView. Пузырь по умолчанию немного ограничивает (имеет только заголовок, субтитры и 2 дополнительных вида), поэтому я изо всех сил пытаюсь найти альтернативное решение. Вот два возможных пути и относительные проблемы, с которыми я сталкиваюсь:
ПРОБЛЕМА 1) СОЗДАНИЕ ТАМОЖЕННОЙ ВЫПУСКАЮЩЕЙСЯ Пузыри
Копая документацию Apple, я нашел это:
Когда вы используете настраиваемое представление вместо стандартного выноски, вам нужно проделать дополнительную работу, чтобы убедиться, что выноска отображается и соответствующим образом скрывается, когда пользователи взаимодействуют с ней. Следующие шаги описывают процесс создания настраиваемой выноски, содержащей кнопку:
Разработка NSView или UIView подкласса, который представляет пользовательский выноски. Вполне вероятно, что подкласс должен реализовать метод drawRect: для рисования вашего пользовательского контента. Создайте контроллер представления, который инициализирует представление выноски и выполняет действие, связанное с кнопкой. В представлении аннотации реализуйте hitTest: для ответа на попадания, которые находятся за пределами представления аннотации, но находятся за пределами представления выноски, как показано в Перечислении 6-7. В представлении аннотации реализуйте setSelected:animated: чтобы добавить ваше представление выноски в качестве подпредставления представления аннотации, когда пользователь щелкает или нажимает на него. Если представление выноски уже видно, когда пользователь выбирает его, метод setSelected: должен удалить подпредставление выноски из представления аннотации (см. Листинг 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: для анимации появления и отмены пользовательского представления выноски, когда пользователь выбирает представление аннотации.
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) ИЗМЕНЕНИЕ ПУНКТА УСТАНОВКИ ПО УМОЛЧАНИЮ
Как было сказано ранее, отображаемая выноска по умолчанию имеет вид заголовка, субтитра и 2 аксессуаров. Я заметил, что не могу сильно изменить стиль шрифта строк или цвет пузыря. Также, если мой заголовок содержит более 24 символов, моё расположение вспомогательных видов испортилось. Как я могу избежать этой проблемы?
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)
}