cordova-plugin-googlemaps的setIcon在iOS首次执行时报错并致地图冻结
setIcon导致的冻结问题 从你提供的Xcode报错信息里,关键线索是这句:
Main Thread Checker: UI API called on a background thread: -[UIWebView request]
iOS的核心规则是所有UI相关操作必须在主线程执行,你的地图插件在调用setIcon时,把修改标记图标的UI逻辑放到了后台线程(看Backtrace里的NSOperationQueue和dispatch队列),首次执行时因为线程竞争导致地图冻结,后续可能因为图标缓存或者线程状态稳定才不再出现问题。
下面给你两种修复思路,根据你的实际情况选择:
一、前端TypeScript代码层面修复(无需修改原生插件)
如果暂时没法修改插件的原生代码,可以通过将setIcon的调用强制切换到主线程来解决,分两种场景:
场景1:使用Ionic/Angular
借助Angular的NgZone确保UI操作在主线程上下文执行:
首先在组件中注入NgZone:
import { NgZone } from '@angular/core'; constructor(private ngZone: NgZone) {}
然后修改_showClickedShop方法,把setIcon调用包裹在ngZone.run()里:
private async _showClickedShop(marker: Marker) { const index = this._sortedFilteredShopsList.findIndex((shop) => shop.id === this.selectedShop.id); const currentMarker = this._markersArray[index]; const isFavouriteToggle = this.isShopFavouriteShop(this.selectedShop.id); const iconCurrent: MarkerIcon = this.iconConfig(this.selectedShop.category, 32, 45, isFavouriteToggle); const iconSelected: MarkerIcon = this.iconConfig(this.selectedShop.category, 42, 55, isFavouriteToggle); // 强制在主线程执行图标修改操作 this.ngZone.run(() => { currentMarker.setIcon(iconCurrent); marker.setIcon(iconSelected); }); this.selectedShop = this._sortedFilteredShopsList[marker.get('index')] this.isFavouriteToggle = this.isShopFavouriteShop(this.selectedShop.id); }
场景2:通用跨平台方案(比如Capacitor/Cordova)
如果不是Angular项目,可以用setTimeout触发主线程执行(setTimeout的回调会自动在主线程运行),同时仅针对iOS平台处理:
import { Capacitor } from '@capacitor/core'; // 或者其他平台判断方式 private async _showClickedShop(marker: Marker) { const index = this._sortedFilteredShopsList.findIndex((shop) => shop.id === this.selectedShop.id); const currentMarker = this._markersArray[index]; const isFavouriteToggle = this.isShopFavouriteShop(this.selectedShop.id); const iconCurrent: MarkerIcon = this.iconConfig(this.selectedShop.category, 32, 45, isFavouriteToggle); const iconSelected: MarkerIcon = this.iconConfig(this.selectedShop.category, 42, 55, isFavouriteToggle); // 仅iOS平台下切换到主线程执行 if (Capacitor.getPlatform() === 'ios') { setTimeout(() => { currentMarker.setIcon(iconCurrent); marker.setIcon(iconSelected); }, 0); } else { currentMarker.setIcon(iconCurrent); marker.setIcon(iconSelected); } this.selectedShop = this._sortedFilteredShopsList[marker.get('index')] this.isFavouriteToggle = this.isShopFavouriteShop(this.selectedShop.id); }
二、原生iOS插件层面修复(彻底解决问题)
如果你有权限修改地图插件的原生代码,最好从根源上修复:找到PluginMarker.m文件中的setIcon_:iconProperty:callbackBlock:方法,把所有涉及UI操作的代码(比如修改地图标记图标、操作UIWebView的部分)包裹在主线程调度里:
- (void)setIcon_:(CDVInvokedUrlCommand*)command iconProperty:(NSDictionary*)iconProperty callbackBlock:(void (^)(id))callbackBlock { // 先处理非UI的逻辑,比如解析图标URL、尺寸等 NSString* iconUrl = iconProperty[@"url"]; NSDictionary* sizeDict = iconProperty[@"size"]; // ... 其他解析逻辑 // 将UI操作放到主线程执行 dispatch_async(dispatch_get_main_queue(), ^{ // 这里执行修改标记图标的代码,比如设置marker的icon // marker.icon = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:iconUrl]]]; // ... 其他UI相关操作 // 回调也要确保在正确线程 callbackBlock(@"success"); }); }
这样就能确保所有UI操作都在iOS要求的主线程执行,从根源上避免线程冲突导致的地图冻结问题。
内容的提问来源于stack exchange,提问作者Killian Perea Ruiz

