我将尝试解释我的问题,以及我到目前为止所做的事情。
简介:
我正在使用 iOS Utils Library来自 Google map ,以便在 map 上显示大约 300 个标记。
用于聚类的算法是 GMUNonHierarchicalDistanceBasedAlgorithm
。
基本上,我们的用户可以通过他们的窗口向我们发送他们观察到的天气,这样我们就可以显示世界各地的实时天气。
它使我们能够改进和/或调整天气预报。
但我的滚动/缩放体验一点也不流畅。顺便说一句,我正在用 iPhone X 测试它......
让我们进入问题的核心:
下面是我如何配置 ClusterManager
private func configureCluster(array: [Observation]) -> Void {
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView,
clusterIconGenerator: iconGenerator)
renderer.delegate = self
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm,
renderer: renderer)
clusterManager.add(array)
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
}
这是我的Observation
类,我尽量保持简单:
class Observation : NSObject, GMUClusterItem {
static var ICON_SIZE = 30
let timestamp: Double
let idObs: String
let position: CLLocationCoordinate2D
let idPicto: [Int]
let token: String
let comment: String
let altitude: Double
init(timestamp: Double, idObs: String, coordinate: CLLocationCoordinate2D, idPicto: [Int], token: String, comment: String, altitude: Double) {
self.timestamp = timestamp
self.idObs = idObs
self.position = coordinate
self.idPicto = idPicto
self.token = token
self.comment = comment
self.altitude = altitude
}
}
最后,渲染的委托(delegate)方法:
func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) {
if let cluster = marker.userData as? GMUCluster {
if let listObs = cluster.items as? [Observation] {
if listObs.count > 1 {
let sortedObs = listObs.sorted(by: { $0.timestamp > $1.timestamp })
if let mostRecentObs = sortedObs.first {
DispatchQueue.main.async {
self.setIconViewForMarker(marker: marker, obs: mostRecentObs)
}
}
} else {
if let obs = listObs.last {
DispatchQueue.main.async {
self.setIconViewForMarker(marker: marker, obs: obs)
}
}
}
}
}
}
用户只能发送一个观测值,但该观测值可以由各种天气现象(如云 + 雨 + 风)组成,如果需要,也可以只发送下雨。
区分它们,如果只有一种现象,则直接设置marker.iconView
属性。
另一方面,如果是对多个现象的观察,我将创建一个 View ,其中包含表示现象的所有图像。
func setIconViewForMarker(marker: GMSMarker, obs: Observation) -> Void {
let isYourObs = Observation.isOwnObservation(id: obs.idObs) ? true : false
if isYourObs {
marker.iconView = Observation.viewForPhenomenomArray(ids: obs.idPicto, isYourObs: isYourObs)
} else {
// Observation with more than 1 phenomenom
if obs.idPicto.count > 1 {
marker.iconView = Observation.viewForPhenomenomArray(ids: obs.idPicto, isYourObs: isYourObs)
// Observation with only 1 phenomenom
} else if obs.idPicto.count == 1 {
if let id = obs.idPicto.last {
marker.iconView = Observation.setImageForPhenomenom(id: id)
}
}
}
}
最后一段代码,向您展示我是如何构建这个自定义 View 的(我想我的问题可能就在这里)
class func viewForPhenomenomArray(ids: [Int], isYourObs: Bool) -> UIView {
let popupView = UIView()
popupView.frame = CGRect.init(x: 0, y: 0, width: (ICON_SIZE * ids.count) + ((ids.count + 1) * 5) , height: ICON_SIZE)
if (isYourObs) {
popupView.backgroundColor = UIColor(red:0.25, green:0.61, blue:0.20, alpha:1)
} else {
popupView.backgroundColor = UIColor(red:0.00, green:0.31, blue:0.57, alpha:1)
}
popupView.layer.cornerRadius = 12
for (index, element) in ids.enumerated() {
let imageView = UIImageView(image: Observation.getPictoFromID(id: element))
imageView.frame = CGRect(x: ((index + 1) * 5) + index * ICON_SIZE, y: 0, width: ICON_SIZE, height: ICON_SIZE)
popupView.addSubview(imageView)
}
return popupView
}
我也尝试过使用非常小的图像,以了解问题是否来自在 map 上渲染大量 PNG,但说真的,它是 iPhone X,它应该能够在 map 上渲染一些简单的天气图标。
你认为我做错了什么吗?还是 Google Maps SDK 中的已知问题? (我看过它固定在 30 fps)
您认为在 map 上渲染大量图像(如 marker.image)需要那么多 GPU 吗?达到完全无法接受体验的地步?
如果你有任何建议,我都会采纳。
最佳答案
我遇到了同样的问题。经过大量调试甚至检查谷歌代码后,我得出的结论是,问题出自 GMUDefaultClusterIconGenerator
。此类在运行时为您显示的给定集群大小创建图像。因此,当您放大或缩小 map 时,集群大小将会更新,并且此类会为新数字创建新图像(即使它会保留图像缓存,如果重复相同的数字)。
所以,我找到的解决方案是使用buckets
。看到这个新术语,您会感到惊讶。让我通过一个简单的例子来解释桶的概念。
假设您将存储桶大小设置为 10、20、50、100、200、500、1000。
- 现在,如果您的集群是 3,那么它将显示 3。
- 如果簇大小 = 8,则显示 = 8。
- 如果簇大小 = 16,则显示 = 10+。
- 如果簇大小 = 22,则显示 = 20+。
- 如果簇大小 = 48,则显示 = 20+。
- 如果簇大小 = 91,则显示 = 50+。
- 如果簇大小 = 177,则显示 = 100+。
- 如果簇大小 = 502,则显示 = 500+。
- 如果簇大小 = 1200004,则显示 = 1000+。
现在在这里,对于任何簇大小,要渲染的标记图像将来自 1、2、3、4、5、6、7、8、9、10+、20+、50+、 100+、200+、500+、1000+。因为它缓存图像,所以这些图像将被重用。因此,它用于创建新图像的时间+cpu 降低了(只需要创建很少的图像)。
关于桶,您现在一定已经明白了。因为,如果集群的数量非常少,那么集群的大小很重要,但如果增加,那么桶的大小就足以了解集群的大小。
现在,问题是如何实现这一点。
实际上,GMUDefaultClusterIconGenerator
类已经实现了这个功能,你只需要把它的初始化改成这样:
let iconGenerator = GMUDefaultClusterIconGenerator(buckets: [ 10, 20, 50, 100, 200, 500, 1000])
GMUDefaultClusterIconGenerator
类还有其他初始化方法,通过使用这些方法,您可以为不同的桶提供不同的背景颜色,为不同的桶提供不同的背景图像等等。
如果需要任何进一步的帮助,请告诉我。
关于ios - iOS 上 GMUClusterRenderer(谷歌地图集群)的滚动/缩放体验非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54145142/