适用于iOS的GoogleMaps SDK - SWIFT 3:当隐藏标记并添加地图视图时,cpu会陷入100% [英] GoogleMaps SDK for iOS - SWIFT 3: When hiding a marker and then adding the map view back, the cpu gets stuck at 100%
问题描述
场景
- UIViewController将
self.view
设置为GMSMapView
inviewDidLload
- 方法将创建标记并将它们存储在
self.markers
并将marker.map
设置为self.view as! GMSMapView
到目前为止,该应用表现良好
self.markers.map
设置为 nil
到这里一切顺利,标记从地图上消失了。 b
$ b
- 再一次,希望返回这些标记的另一种方法将所有
self.markers.map = self.view设置为! GMSMapView
在这里,cpu被锁定在100%(在8核心机器的模拟器上) / p>
如果 self.markers.map
被重置为 nil
再次,CPU回到〜0%,一切都很好。
问题
这是a cpu或GoogleMaps SDK的限制?有没有办法避免这个问题?
重现步骤
提取相关代码后,我还添加了类似的条件,其中一个Label被创建为另一个标记的图标。
经过一些测试后,它似乎与只处理标记的数量有关。请参阅 LabelCount
并设置为不同值,在我的cpu中,问题出现在200,但没有100个标记(即400标记,因为标记有额外的标记) / p>
示例代码
导入UIKit
导入GoogleMaps
class ViewController:UIViewController {
//当标记具有UIImage
时,会发现问题//设置下面为false以查看正常的cpu行为
static let LabelsMakeProblem = true
static let LabelCountFine = 100
static let LabelCountProblems = 200
static let LabelCount = ViewController.LabelCountProblems
static let labelWidth = 200
static let labelHeight = 20
var coords = [CLLocationCoordinate2D]()
static let initLat = Double(-33)
static let initLong = Double(-70)
static let zoomThreshold = Float(13 )
var oldZoom:Float!
var marker = [Int:[GMSMarker]]()
var labels = [Int:[GMSMarker]]()
覆盖func viewDidLoad(){
super.viewDidLoad()
//产生一些随机点
让initCoord = CLLocationCoordinate2D(latitude:ViewController.initLat,longitude:ViewController.initLong)
让deltaCoord = 0.001
for i在0 ... 200 {
let multiplier = Double(i)
self.coords.append(CLLocationCoordinate2D(
latitude:initCoord.latitude + multiplier * deltaCoord,
longitude: initCoord.longitude + multiplier * deltaCoord))
}
//创建地图
let camera = GMSCameraPosition.camera(withLatitude:ViewController.initLat,longitude:ViewController.initLong, zoom:ViewController.zoomThreshold * 1.3)
让mapView = GMSMapView.map(withFrame:.zero,camera:camera)
mapView.delegate = self
self.view = mapView
self.oldZoom = mapView.camera.zoom
//添加标记
let label = self.createLabel()
for(idx, co)在self.coords.enumerated(){
//初始标记数组
如果self.markers [idx] == nil {
self.markers [idx] = [GMSMarker]( )
}
if self.labels [idx] == nil {
self.labels [idx] = [GMSMarker]()
}
let marker = GMSMarker (position:coord)
marker.map = mapView
self.markers [idx] ?. append(marker)
如果ViewController.LabelsMakeProblem {
label.text = coord.latitude .description
let contextSize = CGSize(width:ViewController.labelWidth,height:ViewController.labelHeight)
let opaque = false
UIGraphicsBeginImageContextWithOptions(contextSize,opaque,UIScreen.main .scale)
如果let currentContext = UIGraphicsGetCurrentContext(){
let labelBox = CGRect(x:2,y:2,
width:ViewController.labelWidth,height:ViewController.labelHeight)
label.frame = labelBox
label.layer.render(in:currentContext)
let labelImage = UIGraphicsGetImageFromCurrentImageContext()
let labelMarker = GMSMarker(position:coord)
labelMarker。 icon = labelImage
labelMarker.map = mapView
self.labels [idx] ?. append(labelMarker)
}
UIGraphicsEndImageContext()
}
}
}
private func createLabel() - > UILabel {
let label = UILabel()
label.backgroundColor = UIColor.clear
label.shadowColor = UIColor.white
label.shadowOffset = CGSize(width:5,height: 2)
label.textColor = UIColor.black
label.adjustsFontSizeToFitWidth = true
label.textAlignment = .center
返回标签
}
)func hideMarkers(){
用于self.markers.values.makeIterator()中的标记{
用于标记中的标记{
marker.map = nil
}
}
print(标记隐藏)
}
func showMarkers(){
让mapView = self.view as! GMSMapView
var bounds = GMSCoordinateBounds()
用于self.markers.values.makeIterator()中的标记{
用于标记中的标记{
marker.map = mapView
bounds = bounds.includingCoordinate(marker.position)
}
}
print(在缩放时显示标记:\(mapView.camera.zoom))
//确保我们看到标记
let cameraUpdate = GMSCameraUpdate.fit(bounds)
mapView.animate(with:cameraUpdate)
}
func hideLabels(){
标记为self.labels.values.makeIterator(){
标记中的标记{
marker.map = nil
}
}
print(Labels隐藏)
}
func showLabels(){
让mapView = self.view as! GMSMapView
用于self.labels.values.makeIterator()中的标记{)
用于标记中的标记{
marker.map = mapView
}
}
print(在缩放显示标签:\(mapView.camera.zoom))
}
}
扩展ViewController:GMSMapViewDelegate {
///放大时隐藏标签,放大时显示它们
func mapView(_ mapView:GMSMapView,idleAt position:GMSCameraPosition){
print(Zoom update:\(position.zoom) )
如果position.zoom< self.oldZoom&& position.zoom< ViewController.zoomThreshold {
self.hideLabels()
} else if position.zoom> self.oldZoom&& position.zoom> ViewController.zoomThreshold {
self.showLabels()
}
//跟踪更改
self.oldZoom = position.zoom
}
}
这是我使用的群集方法。 b
//用于检测用户滚动地图的方法
@objc(mapView:didChangeCameraPosition :) func mapView(_:GMSMapView,didChange _:GMSCameraPosition) {
self.counter = self.counter + 1
self.requestForMap(counter:self.counter)
}
//如果用户在0.2秒内没有做任何事请求服务器数据
fileprivate func requestForMap(counter:Int){
DispatchQueue.main.asyncAfter(截止日期:.now()+ 0.2){[weak self] in
警卫let`self` = self else {
return
}
if counter == self.counter {
self.sessionManager.session.invalidateAndCancel()
self.requestData()
code>
这在客户端上
//获取可见区域的坐标
扩展GMSMapView {
func boundings() - > [String:Any] {
让screenBounds = UIScreen.main.bounds
让topPoint = CGPoint(x:15,y:60)
让bottomPoint = CGPoint(x:screenBounds.width - 15,y:screenBounds.height)
let shoudBeFull = self.camera.zoom> 15 //如果用户放大请求区域
中的所有数据让bouding = [
top:[
lat:self.projection.coordinate(for:topPoint) .latitude,
lon:self.projection.coordinate(for:topPoint).longitude,
],
bottom:[
lat:self.projection .coordinate(for:bottomPoint).latitude,
lon:self.projection.coordinate(for:bottomPoint).longitude,
],
full:shoudBeFull,
] as [String:Any]
return bouding
}
}
然后将这个数据作为 JSON
传递给服务器,并且服务器获取对象的引脚数据,其坐标位于该边界内。我们正在使用node.js,不知道它是如何工作的。
var pins = [GMSMarker ]
,在我从服务器获取一个对象数组后,通过这个数组,删除那些不在新数据中并添加新数据的元素,这些元素都是新的 Scenario
- UIViewController sets
self.view
asGMSMapView
inviewDidLload
- A method will create markers and store them in
self.markers
and setmarker.map
to theself.view as! GMSMapView
So far the app behaves well
- Later, another method after some action (looking to toggle those markers) sets all
self.markers.map
tonil
Up to here all goes well and the markers are gone from the map
- Again, another method that wants those markers back, sets all
self.markers.map = self.view as! GMSMapView
Here the cpu gets stuck at 100% (on a simulator in an 8 core machine)
If the self.markers.map
gets reset to nil
again, the cpu goes back to ~0% and all is good.
Question
Is this a limitation on cpu or GoogleMaps SDK? Is there a way to avoid the problem?
Steps to reproduce
After extracting the bits of code related, I added also similar conditions where a Label is being created as an icon for another marker.
After some tests it seems it is related on the amount of markers to process only. See LabelCount
and set to different values, in my cpu the problem appeared with 200 but not with 100 markers (ie: 400 markers as there is an extra marker for the label)
Example code
import UIKit
import GoogleMaps
class ViewController: UIViewController {
// The problem is noticed when markers have a UIImage
// Set below to false to see normal cpu behaviour
static let LabelsMakeProblem = true
static let LabelCountFine = 100
static let LabelCountProblems = 200
static let LabelCount = ViewController.LabelCountProblems
static let labelWidth = 200
static let labelHeight = 20
var coords = [CLLocationCoordinate2D]()
static let initLat = Double(-33)
static let initLong = Double(-70)
static let zoomThreshold = Float(13)
var oldZoom : Float!
var markers = [Int: [GMSMarker]]()
var labels = [Int: [GMSMarker]]()
override func viewDidLoad() {
super.viewDidLoad()
// Generate some random points
let initCoord = CLLocationCoordinate2D(latitude: ViewController.initLat, longitude: ViewController.initLong)
let deltaCoord = 0.001
for i in 0...200 {
let multiplier = Double(i)
self.coords.append(CLLocationCoordinate2D(
latitude: initCoord.latitude + multiplier * deltaCoord,
longitude: initCoord.longitude + multiplier * deltaCoord))
}
// Create a map
let camera = GMSCameraPosition.camera(withLatitude: ViewController.initLat, longitude: ViewController.initLong, zoom: ViewController.zoomThreshold * 1.3)
let mapView = GMSMapView.map(withFrame: .zero, camera: camera)
mapView.delegate = self
self.view = mapView
self.oldZoom = mapView.camera.zoom
// Add markers
let label = self.createLabel()
for (idx, coord) in self.coords.enumerated() {
// Init marker arrays
if self.markers[idx] == nil {
self.markers[idx] = [GMSMarker]()
}
if self.labels[idx] == nil {
self.labels[idx] = [GMSMarker]()
}
let marker = GMSMarker(position: coord)
marker.map = mapView
self.markers[idx]?.append(marker)
if ViewController.LabelsMakeProblem {
label.text = coord.latitude.description
let contextSize = CGSize(width: ViewController.labelWidth, height: ViewController.labelHeight)
let opaque = false
UIGraphicsBeginImageContextWithOptions(contextSize, opaque, UIScreen.main.scale)
if let currentContext = UIGraphicsGetCurrentContext(){
let labelBox = CGRect(x: 2, y: 2,
width: ViewController.labelWidth, height: ViewController.labelHeight)
label.frame = labelBox
label.layer.render(in: currentContext)
let labelImage = UIGraphicsGetImageFromCurrentImageContext()
let labelMarker = GMSMarker(position: coord)
labelMarker.icon = labelImage
labelMarker.map = mapView
self.labels[idx]?.append(labelMarker)
}
UIGraphicsEndImageContext()
}
}
}
private func createLabel() -> UILabel{
let label = UILabel()
label.backgroundColor = UIColor.clear
label.shadowColor = UIColor.white
label.shadowOffset = CGSize(width: 5, height: 2)
label.textColor = UIColor.black
label.adjustsFontSizeToFitWidth = true
label.textAlignment = .center
return label
}
func hideMarkers() {
for markers in self.markers.values.makeIterator() {
for marker in markers {
marker.map = nil
}
}
print("Markers hidden")
}
func showMarkers() {
let mapView = self.view as! GMSMapView
var bounds = GMSCoordinateBounds()
for markers in self.markers.values.makeIterator() {
for marker in markers {
marker.map = mapView
bounds = bounds.includingCoordinate(marker.position)
}
}
print("Show markers at zoom:\(mapView.camera.zoom)")
// Ensure we see the markers
let cameraUpdate = GMSCameraUpdate.fit(bounds)
mapView.animate(with: cameraUpdate)
}
func hideLabels() {
for markers in self.labels.values.makeIterator() {
for marker in markers {
marker.map = nil
}
}
print("Labels hidden")
}
func showLabels() {
let mapView = self.view as! GMSMapView
for markers in self.labels.values.makeIterator() {
for marker in markers {
marker.map = mapView
}
}
print("Show labels at zoom:\(mapView.camera.zoom)")
}
}
extension ViewController : GMSMapViewDelegate {
/// Hide labels when zooming out and show them when zooming in
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
print("Zoom update: \(position.zoom)")
if position.zoom < self.oldZoom && position.zoom < ViewController.zoomThreshold {
self.hideLabels()
} else if position.zoom > self.oldZoom && position.zoom > ViewController.zoomThreshold {
self.showLabels()
}
// Track changes
self.oldZoom = position.zoom
}
}
This is clustering approach I am using
//method to detect when user scrolls map
@objc(mapView:didChangeCameraPosition:) func mapView(_: GMSMapView, didChange _: GMSCameraPosition) {
self.counter = self.counter + 1
self.requestForMap(counter: self.counter)
}
//if user did nothing for 0.2 seconds request data from server
fileprivate func requestForMap(counter: Int) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
guard let `self` = self else {
return
}
if counter == self.counter {
self.sessionManager.session.invalidateAndCancel()
self.requestData()
}
}
}
to get pins in area I do this on client
// get coordinates of visible area
extension GMSMapView {
func boundings() -> [String: Any] {
let screenBounds = UIScreen.main.bounds
let topPoint = CGPoint(x: 15, y: 60)
let bottomPoint = CGPoint(x: screenBounds.width - 15, y: screenBounds.height)
let shoudBeFull = self.camera.zoom > 15 //if user is zoomed in a lot request all data in area
let bouding = [
"top": [
"lat": self.projection.coordinate(for: topPoint).latitude,
"lon": self.projection.coordinate(for: topPoint).longitude,
],
"bottom": [
"lat": self.projection.coordinate(for: bottomPoint).latitude,
"lon": self.projection.coordinate(for: bottomPoint).longitude,
],
"full": shoudBeFull,
] as [String: Any]
return bouding
}
}
then this data as JSON
is passed to the server and the server gets pins' data for objects, coordinates of which are inside this bounding. We are using node.js, not sure how it works there.
Then I have an array of currently displayed pins like var pins = [GMSMarker]
, after I get an array of objects from server I go through this array, remove those, which are not in new data and add those, which are new
这篇关于适用于iOS的GoogleMaps SDK - SWIFT 3:当隐藏标记并添加地图视图时,cpu会陷入100%的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!