You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

cordova-plugin-googlemaps的setIcon在iOS首次执行时报错并致地图冻结

修复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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:52:04