2017-02-11 2 views
0

Я рисую несколько MKAnnotations на MKMapView. Я бы хотел, чтобы пользователи VoiceOver могли продолжать панорамирование/масштабирование карты, как обычно, но я также хотел бы, чтобы они могли быстро и легко перемещаться по моему MKAnnotations, если они захотят. Я чувствую, что пользовательский ротор - идеальное решение для этого.Создайте собственный ротор VoiceOver для навигации по MKAnnotationViews?

ответ

2

Автоответчик здесь, потому что я потратил безумное количество времени на правильное решение, и подумал, что кому-то может понадобиться это. В то время, когда мне нужно было это разработать, вряд ли были примеры онлайн, которые подробно рассказывают о создании пользовательских роторов, а документация Apple очень скудна. Я, наконец, понял это, после просмотра и последующего (и паузы на экранах кода) WWDC Session 202 (начинается в 24:17).

Самое сложное, что мне нужно было выяснить, - как надежно вернуть UIAccessibilityCustomRotorItemResult. Для MKMapView вы хотите вернуть MKAnnotationView s, но аннотация не гарантирует наличие связанного представления (они переработаны, а если аннотация включена вне экрана, есть хороший шанс, что ее просмотр был повторно использован), поэтому мои первые попытки продолжал оставлять некоторые или большинство моих аннотаций.

Магия в создании анимированного: свойство ложно:

self.mapView.setCenter(requestedAnnotation.coordinate, animated: false) 

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

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

func configureCustomRotors() { 
    let favoritesRotor = UIAccessibilityCustomRotor(name: "Bridges") { predicate in 
    let forward = (predicate.searchDirection == .next) 

    // which element is currently highlighted 
    let currentAnnotationView = predicate.currentItem.targetElement as? MKPinAnnotationView 
    let currentAnnotation = (currentAnnotationView?.annotation as? BridgeAnnotation) 

    // easy reference to all possible annotations 
    let allAnnotations = self.mapView.annotations.filter { $0 is BridgeAnnotation } 

    // we'll start our index either 1 less or 1 more, so we enter at either 0 or last element 
    var currentIndex = forward ? -1 : allAnnotations.count 

    // set our index to currentAnnotation's index if we can find it in allAnnotations 
    if let currentAnnotation = currentAnnotation { 
     if let index = allAnnotations.index(where: { (annotation) -> Bool in 
     return (annotation.coordinate.latitude == currentAnnotation.coordinate.latitude) && 
    (annotation.coordinate.longitude == currentAnnotation.coordinate.longitude) 
     }) { 
      currentIndex = index 
     } 
    } 

    // now that we have our currentIndex, here's a helper to give us the next element 
    // the user is requesting 
    let nextIndex = {(index:Int) -> Int in forward ? index + 1 : index - 1} 

    currentIndex = nextIndex(currentIndex) 

    while currentIndex >= 0 && currentIndex < allAnnotations.count { 
     let requestedAnnotation = allAnnotations[currentIndex] 

     // i can't stress how important it is to have animated set to false. save yourself the 10 hours i burnt, and just go with it. if you set it to true, the map starts moving to the annotation, but there's no guarantee the annotation has an associated view yet, because it could still be animating. in which case the line below this one will be nil, and you'll have a whole bunch of annotations that can't be navigated to 
     self.mapView.setCenter(requestedAnnotation.coordinate, animated: false) 
     if let annotationView = self.mapView.view(for: requestedAnnotation) { 
     return UIAccessibilityCustomRotorItemResult(targetElement: annotationView, targetRange: nil) 
     } 

     currentIndex = nextIndex(currentIndex) 
    } 

    return nil 
    } 

    self.accessibilityCustomRotors = [favoritesRotor] 
} 
Смежные вопросы