Кластеризация не работает должным образом в iOS 13 - PullRequest
0 голосов
/ 01 ноября 2019

Я делаю кластеризацию аннотаций.

Приведенный ниже код работает нормально и правильно группирует точки в iOS 11 и iOS 12.

Не удается кластеризовать точки, объединить точки в iOS13.

Я не использую какие-либо бета-версии.

Класс TTMapView является оболочкой для MKMapView.

class TTMapView: UIView {

    var mapView = MKMapView()
    private var mapObjects: Dictionary<TTShape, MKShape?> = [:]
    private var _isClusteringEnabled = true

    func addMarker(_ marker: TTPoint) -> TTPoint {
        removeMarker(marker)
        let coordinate = marker.coordinate
        let pointAnnotation = MKPointAnnotation()
        pointAnnotation.coordinate = convertTTCoordinateToCLLocationCoordinate2D(coordinate)
        pointAnnotation.title = marker.title
        pointAnnotation.subtitle = marker.subtitle
        mapObjects.updateValue(pointAnnotation, forKey: marker)
        mapView.addAnnotation(pointAnnotation)
        return marker
    }
}

extension TTMapView: MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        if annotation is MKUserLocation {
            return nil
        }

        if _isClusteringEnabled {
            let point = mapObjects.filter ({ $0.value === annotation }).first?.key as? TTPoint
            print("point ", point)
            return TTClusterAnnotationView(annotation: annotation, reuseIdentifier: TTClusterAnnotationView.ReuseID, image: point?.image, color: point?.tintColor)
        } else {
            let reuseId = "simplePin"
            var pinAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
            if pinAnnotationView == nil {
                pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
                pinAnnotationView?.isDraggable = true
                pinAnnotationView?.canShowCallout = true
            }
            return pinAnnotationView
        }
    }
}

class TTClusterAnnotationView: MKMarkerAnnotationView {

    /// Use this Id for setting annotation
    static let ReuseID = "clusterAnnotation"

    init(annotation: MKAnnotation?, reuseIdentifier: String?, image: UIImage?, color: UIColor? = nil) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
        // Enable clustering by just setting the clusteringIdentifier
        clusteringIdentifier = "clusteringIdentifier"
        glyphImage = image
        glyphTintColor = color
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func prepareForDisplay() {
        super.prepareForDisplay()
        displayPriority = .required
    }
}

1 Ответ

0 голосов
/ 01 ноября 2019

Сделайте представление аннотации кластера (это представление аннотации для кластера, не путайте с существующим представлением аннотации, которое имеет clusteringIdentifier, которое я переименовал в CustomAnnotationView, чтобы избежать путаницы) имеет displayPriority из .required:

class CustomClusterAnnotationView: MKMarkerAnnotationView {
    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
        displayPriority = .required
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override var annotation: MKAnnotation? {
        didSet {
            displayPriority = .required
        }
    }
}

Затем зарегистрируйте этот класс:

mapView.register(CustomClusterAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)

И затем используйте его:

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
    if annotation is MKUserLocation {
        return nil
    }

    if annotation is MKClusterAnnotation {
        return nil
    }

    ...
}

Несколько не связанныхНаблюдения:

  1. Я бы предложил просто добавить ваше изображение и свойства цвета к пользовательскому типу аннотации, а не фильтровать viewFor через mapObjects. Итак:

    class CustomAnnotation: MKPointAnnotation {
        let image: UIImage
        let color: UIColor
    
        init(coordinate: CLLocationCoordinate2D, title: String?, image: UIImage, color: UIColor) {
            self.image = image
            self.color = color
            super.init()
            self.coordinate = coordinate
            self.title = title
        }
    }
    

    Тогда, если вы используете это вместо MKPointAnnotation, ваш пользовательский вид аннотации может извлечь информацию о цвете и изображении прямо из аннотации.

    class CustomAnnotationView: MKMarkerAnnotationView {
        static let reuseID = "CustomAnnotationView"
    
        override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
            super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
            updateForAnnotation()
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override var annotation: MKAnnotation? {
            didSet {
                updateForAnnotation()
            }
        }
    }
    
    private extension CustomAnnotationView {
        func updateForAnnotation() {
            clusteringIdentifier = "CustomAnnotationView"
            displayPriority = .required
            if let annotation = annotation as? CustomAnnotation {
                glyphImage = annotation.image
                glyphTintColor = annotation.color
            } else {
                glyphImage = nil
                glyphTintColor = nil
            }
        }
    }
    

    Примечаниевыше я сбрасываю идентификатор кластера, изображение, глиф и т. д. в didSet из annotation. Это позволяет повторно использовать аннотации. (См. Следующий пункт.)

  2. Логика повторного использования для представления аннотации выводов неверна. И вы вообще не используете повторно, если кластеризация включена. Если нацелена на iOS 11 и более поздние версии, я бы использовал dequeueReusableAnnotationView(withIdentifier:for:), который позаботится обо всем этом для васИтак, я могу зарегистрировать этот идентификатор повторного использования:

    mapView.register(CustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: CustomAnnotationView.reuseID)
    

    И я бы повторил этот процесс для представления аннотации «простой вывод», которое вы показываете, если выключаете кластеризацию.

    mapView.register(CustomSimplePinAnnotationView.self, forAnnotationViewWithReuseIdentifier: CustomSimplePinAnnotationView.reuseID)
    

    И

    class CustomSimplePinAnnotationView: MKPinAnnotationView {
        static let reuseID = "CustomSimplePinAnnotationView"
    
        override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
            super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
            isDraggable = true
            canShowCallout = true
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    

    И тогда ваш viewFor упрощается:

    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        switch annotation {
        case is MKUserLocation:
            return nil
    
        case is MKClusterAnnotation:
            return nil
    
        default:
            if _isClusteringEnabled {
                return mapView.dequeueReusableAnnotationView(withIdentifier: CustomAnnotationView.reuseID, for: annotation)
            } else {
                return mapView.dequeueReusableAnnotationView(withIdentifier: CustomSimplePinAnnotationView.reuseID, for: annotation)
            }
        }
    }
    
...