ОК, iOS 11 и более поздние решения довольно просты.У вас есть два вида аннотаций, один для ваших собственных аннотаций и один для кластеров аннотаций.Ваш основной вид аннотации просто должен указать clusteringIdentifier
, когда он инициализируется и когда изменяется свойство annotation
:
class UserAnnotationView: MKMarkerAnnotationView {
static let preferredClusteringIdentifier = Bundle.main.bundleIdentifier! + ".UserAnnotationView"
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
clusteringIdentifier = UserAnnotationView.preferredClusteringIdentifier
collisionMode = .circle
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var annotation: MKAnnotation? {
willSet {
clusteringIdentifier = UserAnnotationView.preferredClusteringIdentifier
}
}
}
И ваш вид аннотации кластера должен просто обновить свое изображение, когда его свойство annotation
обновлено:
class UserClusterAnnotationView: MKAnnotationView {
static let preferredClusteringIdentifier = Bundle.main.bundleIdentifier! + ".UserClusterAnnotationView"
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
collisionMode = .circle
updateImage()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var annotation: MKAnnotation? { didSet { updateImage() } }
private func updateImage() {
if let clusterAnnotation = annotation as? MKClusterAnnotation {
self.image = image(count: clusterAnnotation.memberAnnotations.count)
} else {
self.image = image(count: 1)
}
}
func image(count: Int) -> UIImage {
let bounds = CGRect(origin: .zero, size: CGSize(width: 40, height: 40))
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { _ in
// Fill full circle with tricycle color
AppTheme.blueColor.setFill()
UIBezierPath(ovalIn: bounds).fill()
// Fill inner circle with white color
UIColor.white.setFill()
UIBezierPath(ovalIn: bounds.insetBy(dx: 8, dy: 8)).fill()
// Finally draw count text vertically and horizontally centered
let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.black,
.font: UIFont.boldSystemFont(ofSize: 20)
]
let text = "\(count)"
let size = text.size(withAttributes: attributes)
let origin = CGPoint(x: bounds.midX - size.width / 2, y: bounds.midY - size.height / 2)
let rect = CGRect(origin: origin, size: size)
text.draw(in: rect, withAttributes: attributes)
}
}
}
Затем все, что вам нужно сделать, это зарегистрировать ваши классы:
mapView.register(UserAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
mapView.register(UserClusterAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)
Нет mapView(_:viewFor:)
реализация не требуется (и не желательно).Но приведенные выше результаты (с отображением анимации по умолчанию при уменьшении и увеличении масштаба):
Теперь, очевидно, вы можете изменить UserAnnotationView
как пожелаешь.(Ваш вопрос не указал, как будет выглядеть стандартное однотарное представление аннотации).Но, установив clusteringIdentifier
и зарегистрировав MKMapViewDefaultClusterAnnotationViewReuseIdentifier
, вы довольно легко получаете кластеризацию в iOS 11 и более поздних версиях.
Если вы действительно хотите, чтобы представления аннотаций кластера выглядели как стандартные представления аннотаций, вы можете зарегистрироватьодин и тот же класс представлений аннотаций для обоих:
mapView.register(UserClusterAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
mapView.register(UserClusterAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)
Но тогда вам нужно дать кластерному аннотационному представлению тот же clusteringIdentifier, который мы ранее дали для стандартного аннотационного представления:
class UserClusterAnnotationView: MKAnnotationView {
static let preferredClusteringIdentifier = Bundle.main.bundleIdentifier! + ".UserClusterAnnotationView"
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
clusteringIdentifier = UserClusterAnnotationView.preferredClusteringIdentifier
collisionMode = .circle
updateImage()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var annotation: MKAnnotation? {
didSet {
clusteringIdentifier = UserClusterAnnotationView.preferredClusteringIdentifier
updateImage()
}
}
private func updateImage() {
if let clusterAnnotation = annotation as? MKClusterAnnotation {
self.image = image(count: clusterAnnotation.memberAnnotations.count)
} else {
self.image = image(count: 1)
}
}
func image(count: Int) -> UIImage {
let bounds = CGRect(origin: .zero, size: CGSize(width: 40, height: 40))
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { _ in
// Fill full circle with tricycle color
AppTheme.blueColor.setFill()
UIBezierPath(ovalIn: bounds).fill()
// Fill inner circle with white color
UIColor.white.setFill()
UIBezierPath(ovalIn: bounds.insetBy(dx: 8, dy: 8)).fill()
// Finally draw count text vertically and horizontally centered
let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.black,
.font: UIFont.boldSystemFont(ofSize: 20)
]
let text = "\(count)"
let size = text.size(withAttributes: attributes)
let origin = CGPoint(x: bounds.midX - size.width / 2, y: bounds.midY - size.height / 2)
let rect = CGRect(origin: origin, size: size)
text.draw(in: rect, withAttributes: attributes)
}
}
}
выходы:
Лично я думаю, что это немного сбивает с толку, но если это то, что вы собираетесь, это один из способов добиться этого.
Теперь, если вам действительно нужно поддерживать версии iOS до 11 и вам нужна кластеризация, вам придется самостоятельно выполнять всю эту логику кластеризации (или найти стороннюю библиотеку для этого).Apple показывает, как это сделать в WWDC 2011 Географическая визуализация информации с помощью MapKit .Концепция, которую они используют, это понятие деления видимой карты на сетку, и если в конкретной сетке есть несколько аннотаций, они удаляют их и добавляют одну «кластерную» аннотацию.И они иллюстрируют, как вы можете даже визуально анимировать перемещение аннотаций внутри и из кластера, чтобы пользователь мог понять, что происходит, когда они увеличивают и уменьшают масштаб.Это хорошая отправная точка, когда вы погрузитесь в это.
Это нетривиально, поэтому я долго и усердно думаю о том, хочу ли я реализовать это сам.Я либо откажусь от версий iOS до 11, либо найду стороннюю реализацию (и у этого вопроса, на который вы ссылаетесь есть множество примеров).