2017-02-11 15 views
0

MKMapViewに複数のMKAnnotationsをプロットします。私はVoiceOverのユーザーに通常のように地図のパン/ズームを継続できるようにしたいと思いますが、私が選択した場合はMKAnnotationsをすばやく簡単にナビゲートすることもできます。私はカスタムローターがこれのための完璧な解決策であるように感じる。MKAnnotationViewsをナビゲートするためのカスタムVoiceOver Rotorを作成しますか?

答えて

2

私はこの権利を得るのに非常識な時間を費やしたので、誰かがこれを必要とするかもしれないと考えたので、ここで自分自身で答える。私がこれを開発する必要がある時には、カスタムローターの作成について詳しく説明するオンラインの例はほとんどなく、Appleの文書は非常にまばらです。私は最後に、WWDC Session 202(24:17から始まる)を見て、それに従った後(そしてコードの画面を一時停止した後)、それを理解しました。

私が理解する必要があった最も難しいことは、信頼できる方法でUIAccessibilityCustomRotorItemResultを返すことでした。 MKMapViewの場合はMKAnnotationView秒を返しますが、アノテーションには関連付けられたビューがあることが保証されていません(リサイクルされており、アノテーションがオフスクリーンの場合は再利用されている可能性が高いため)私の注釈の一部または大部分を残していました。 falseにプロパティ:

魔法のアニメーションを設定している

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