我有一个正在使用SwiftUI UIViewRepresentable
处理的MKMapView。问题是我的集群无法按预期工作。我的代码产生了以下一系列问题。
- Map加载,可群集注解按预期群集。
- 放大群集时会将它们解群集。
- 缩小时,它们会重新聚集,但是如果我进一步缩小,附加的注解不会与组聚集。
- 在遇到这个bug后再放大,将不会导致任何类型的聚类。
集群标注视图
final class ClusterLocationAnnotationView: MKAnnotationView {
static let identifier = "cluster-location-annotation-identifier"
// Omitted irrelevant `UILabel` and other views.
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
displayPriority = .defaultHigh
collisionMode = .rectangle
clusteringIdentifier = "location-detail-cluster-id"
canShowCallout = false
setupUI()
}
// Removed Required Init
private func setupUI() {
addSubview(backgroundView)
addSubview(label)
addSubview(offersLabel)
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: backgroundView.centerXAnchor),
label.centerYAnchor.constraint(equalTo: backgroundView.centerYAnchor)
])
offersLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
offersLabel.topAnchor.constraint(equalTo: backgroundView.bottomAnchor, constant: 4), // adjust the constant as needed
offersLabel.centerXAnchor.constraint(equalTo: backgroundView.centerXAnchor)
])
}
// MARK: - Configure Cluster
func configure(appConfig: AppConfig, clusterLocations: [LocationDetail]) {
backgroundView.backgroundColor = UIColor(cgColor: appConfig.secondaryColor.cgColor ?? UIColor.black.cgColor)
backgroundView.layer.cornerRadius = appConfig.globalCornerRadii
label.text = clusterLocations.count.description
label.textColor = UIColor(cgColor: appConfig.secondaryColor.contrastingColor().cgColor ?? UIColor.white.cgColor)
// Set number of locationDetails that contain an offer, to the text, eg. offersLabel.text = locationsWithOffersCount
var offerCount = 0
for location in clusterLocations {
offerCount += appConfig.hasOffer(for: location) ? 1 : 0
}
offersLabel.text = "\(offerCount) Offer(s) Available"
}
注解视图
final class LocationAnnotationView: MKAnnotationView {
static let identifier = "location-annotation-identifier"
// Removed irrelevant views.
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
displayPriority = .defaultHigh
collisionMode = .rectangle
clusteringIdentifier = "location-detail-cluster-id"
canShowCallout = true
setupUI()
}
//Removed Required INIT
private func setupUI() {
addSubview(backgroundView)
addSubview(imageView)
addSubview(label)
backgroundView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
backgroundView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
backgroundView.centerYAnchor.constraint(equalTo: self.centerYAnchor),
backgroundView.widthAnchor.constraint(equalToConstant: 40),
backgroundView.heightAnchor.constraint(equalToConstant: 40)
])
imageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
imageView.centerXAnchor.constraint(equalTo: backgroundView.centerXAnchor),
imageView.centerYAnchor.constraint(equalTo: backgroundView.centerYAnchor),
imageView.widthAnchor.constraint(equalToConstant: 30),
imageView.heightAnchor.constraint(equalToConstant: 30)
])
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.topAnchor.constraint(equalTo: backgroundView.bottomAnchor, constant: 4),
label.centerXAnchor.constraint(equalTo: backgroundView.centerXAnchor)
])
}
func configure(with location: LocationDetail, and category: Category, appConfig: AppConfig) {
backgroundView.backgroundColor = UIColor(cgColor: appConfig.secondaryColor.cgColor ?? UIColor.black.cgColor)
backgroundView.layer.cornerRadius = appConfig.globalCornerRadii
imageView.image = Iconoir.uiImage(from: category.iconName)?.withRenderingMode(.alwaysTemplate)
imageView.tintColor = UIColor(cgColor: appConfig.secondaryColor.contrastingColor().cgColor ?? UIColor.white.cgColor)
self.label.text = location.locationName
}
}
协调器MKMapViewDelegate函数
func mapView(_ mapView: MKMapView, clusterAnnotationForMemberAnnotations memberAnnotations: [MKAnnotation]) -> MKClusterAnnotation {
return MKClusterAnnotation(memberAnnotations: memberAnnotations)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let appConfig = parent.appConfig
if let cluster = annotation as? MKClusterAnnotation {
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: ClusterLocationAnnotationView.identifier, for: cluster) as? ClusterLocationAnnotationView
annotationView?.frame.size.height = 40
annotationView?.frame.size.width = 40
annotationView?.configure(appConfig: appConfig, clusterLocations: cluster.memberAnnotations as! [LocationDetail])
return annotationView
} else if let location = annotation as? LocationDetail {
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: LocationAnnotationView.identifier, for: annotation) as? LocationAnnotationView
annotationView?.frame.size.height = 40
annotationView?.frame.size.width = 40
let category = appConfig.category(from: location.categoryId)
annotationView?.configure(with: location, and: category, appConfig: appConfig)
return annotationView
}
return nil
}
更新UIView
func updateUIView(_ mapView: MKMapView, context: Context) {
let coordinator = context.coordinator
mapView.mapType = mapType
mapView.userTrackingMode = trackingMode
handleLocationChange(mapView, coordinator: coordinator)
}
func handleLocationChange(_ mapView: MKMapView, coordinator: Coordinator) {
if coordinator.lastLocations.count != locations.count {
mapView.removeAnnotations(mapView.annotations)
mapView.addAnnotations(locations)
mapView.setRegion(getRegion(for: locations), animated: true)
coordinator.lastLocations = locations
DispatchQueue.main.async {
trackingMode = .none
}
}
}
2条答案
按热度按时间r7s23pms1#
handleLocationChange需要被移动到协调器中,以便可以在Map区域更改事件中调用它。
也可能删除位置计数检查,它看起来容易出错。
rnmwe5a22#
有些问题需要解决。首先,我在我的
Coordinator
上子类化了MKMapView
,我不认为这是真正需要的,因为我可以访问updateUIView
中的mapView
示例。我需要更新我的
makeUIView
,以根据更新的状态正确更新。以前,我错误地处理了我的@State
,并从makeUIView
和UIViewRepresentable
结构更新了@State
。这里的一般规则是,任何从UIViewRepresentable
更新的值,也就是从SwiftUI发布的State
值,都应该更新Coordinator
上的值。Coordinator
更新的任何值都应该发布到UIViewRepresentable
结构中的State
变量。这将反过来导致调用updateUIView(..)
函数,用新更新的State
值更新Coordinator
的属性值。为了解决
MKAnnotation
集群问题,我检查了clusteringIdentifier
的代码,我在LocationDetail
上设置了clusteringIdentifier
,并在LocationAnnotationView
和ClusterLocationAnnotationView
上设置了clusteringIdentifier
,因此我删除了该值,仅在调用viewForAnnotation(:)
时使用它。