如何用Xamarin.Forms.Maps在iOS端C#循环API分页修改地图标记颜色
问题描述
我需要校验API每个分页的返回值,根据对比结果修改地图标记的颜色:
- 使用的分页API地址为
http://194.141.118.43:3001/?id=0,其中id参数范围为0到16,对应17个站点 - 颜色匹配规则:若接口返回AL=1则标记为绿色,AL=2为黄色,AL=3为橙色,AL=4为红色,需要取每个站点返回的最大AL值设置对应标记颜色
- 当前使用
http://194.141.118.43:3001/stations接口校验Alertlevelwebsite属性,现在需要切换为逐站点调用前述分页接口 - 我参照Xamarin.Forms.Maps官方示例,在
CustomMapRenderer类的GetViewForAnnotation方法中修改标记颜色
现有问题
在GetViewForAnnotation中调用接口后,输入result.后IDE自动补全为IAsyncResult,无法识别实际返回的属性,也不能正常写foreach循环,希望获取循环调用所有API分页修改标记颜色的示例。
相关代码
GetViewForAnnotation方法现有代码
protected override MKAnnotationView GetViewForAnnotation(MKMapView mapView, IMKAnnotation annotation) { MKAnnotationView annotationView = null; if (annotation is MKUserLocation) return null; var customPin = GetCustomPin(annotation as MKPointAnnotation); //获取值 c_annotation = annotation; if (customPin == null) { throw new Exception("未找到自定义标记"); } annotationView = mapView.DequeueReusableAnnotation(customPin.Name); if (annotationView == null) { annotationView = new CustomMKAnnotationView(annotation, customPin.Name); annotationView.CalloutOffset = new CGPoint(0, 0); ((CustomMKAnnotationView)annotationView).Name = customPin.Name; ((CustomMKAnnotationView)annotationView).Url = customPin.Url; ((CustomMKAnnotationView)annotationView).Address = customPin.Address; //添加第一行 ((CustomMKAnnotationView)annotationView).AlertLevel = customPin.AlertLevel; if (customPin.AlertLevel == 1) { annotationView.Image = UIImage.FromFile("green.png"); } else if (customPin.AlertLevel == 2) { annotationView.Image = UIImage.FromFile("yellow.png"); } else if (customPin.AlertLevel == 3) { annotationView.Image = UIImage.FromFile("orange.png"); } else if (customPin.AlertLevel == 4) { annotationView.Image = UIImage.FromFile("red.png"); } //添加第二行 ((CustomMKAnnotationView)annotationView).CodeNum = customPin.CodeNum; ((CustomMKAnnotationView)annotationView).MapCode = customPin.MapCode; //此处为站点接口的请求地址生成方法 string GenerateRequestUriStations(string endpoint) { string requestUri = endpoint; requestUri += $"stations"; return requestUri; } //此处需要循环0到16的所有分页,按照上述if语句修改标记图标 string GenerateRequestUri(string endpoint) { string requestUri = endpoint; requestUri += $"?id=0"; return requestUri; } //我需要对比result.WaterData.Ardaforecast的值,但这里无法访问该属性,也不能写foreach循环,不清楚原因 var reusult = _restServiceData.GetWaterDataForecast(GenerateRequestUriStations(Constants.EndPoint), GenerateRequestUri(Constants.EndPoint)); } annotationView.CanShowCallout = true; configureDetailView(annotationView); return annotationView; }
报错代码段
var reusult = _restServiceData.GetWaterDataForecast(GenerateRequestUriStations(Constants.EndPoint), GenerateRequestUri(Constants.EndPoint)); foreach (var item in IAsyncResult) { }
GetDataFromAPI方法
public IEnumerable<AlertLevel> GetDataFromAPI(int mapCode) { var listAlert = new List<AlertLevel>(); var reusult = _restServiceData.GetWaterDataForecast(GenerateRequestUriStations(Constants.EndPoint), GenerateRequestUri(Constants.EndPoint, mapCode)); foreach (var item in reusult.WaterData.Ardaforecast[0].Items) { var currentData = new AlertLevel() { dateForecast = item.DateTimeForecast, levelForecast = item.AlertLevelForecast }; listAlert.Add(currentData); } return listAlert; }
GetWaterDataForecast方法
using System; using System.Diagnostics; using System.Net.Http; using System.Threading.Tasks; using MaritsaTundzhaForecast.Models; using Newtonsoft.Json; namespace MaritsaTundzhaForecast.Services { public class RestServiceData { HttpClient _client1; HttpClient _client2; public RestServiceData() { _client1 = new HttpClient(); _client2 = new HttpClient(); } public WaterBindingData GetWaterDataForecast(string query, string query2) { WaterDataJson waterData = new WaterDataJson(); WaterStationsJson waterStations = new WaterStationsJson(); WaterBindingData result = new WaterBindingData(); try { var task = Task.Run(() => _client1.GetAsync(query)); task.Wait(); var response = task.Result; var task2 = Task.Run(() => _client2.GetAsync(query2)); task2.Wait(); var response2 = task2.Result; if (response.IsSuccessStatusCode && response2.IsSuccessStatusCode) { var content = response.Content.ReadAsStringAsync().Result; var content2 = response2.Content.ReadAsStringAsync().Result; var json = content2.Replace("\"ardaforecast\":[[", "\"ardaforecast\":[ {\"items\": [") .Replace("}],{\"fieldCount\"", "}],\"details\":{\"fieldCount\"") .Replace("}]}", "}}]}"); waterData = JsonConvert.DeserializeObject<WaterDataJson>(json); waterStations = JsonConvert.DeserializeObject<WaterStationsJson>(content); result.WaterData = waterData; result.WaterStation = waterStations; } } catch (Exception ex) { Debug.WriteLine("\t\tERROR {0}", ex.Message); } return result; } } }
相关实体类定义
WaterBindingData类
using System; namespace MaritsaTundzhaForecast.Models { public class WaterBindingData { public WaterDataJson WaterData { get; set; } public WaterStationsJson WaterStation { get; set; } } }
WaterDataJson与WaterStations相关类
using System; using System.Collections.Generic; using Newtonsoft.Json; namespace MaritsaTundzhaForecast { public class WaterDataJson { public List<ForecastBody> Ardaforecast { get; set; } } public class ForecastBody { public ForecastItem[] Items { get; set; } public ForecastDetails Details { get; set; } } public class ForecastItem { [JsonProperty("Dt")] public DateTime DateTimeForecast { get; set; } [JsonProperty("AL")] public int AlertLevelForecast { get; set; } } public class ForecastDetails { public int fieldCount { get; set; } public int affectedRows { get; set; } public int insertId { get; set; } public int serverStatus { get; set; } public int warningCount { get; set; } public int changedRows { get; set; } public string message { get; set; } public bool protocol41 { get; set; } } } using System; using System.Collections.Generic; using Newtonsoft.Json; namespace MaritsaTundzhaForecast.Models { public class WaterStationsJson { public List<ForecastStations> Stations { get; set; } } public class ForecastStations { [JsonProperty("Map_code")] public int MapCode { get; set; } [JsonProperty("NAME_IME")] public string NameEN { get; set; } [JsonProperty("NAME_CYR")] public string NameBG { get; set; } [JsonProperty("Alertlevelwebsite")] public int AlertLevelStation { get; set; } [JsonProperty("CODENUM")] public int CodeNum { get; set; } } }
解决方案
问题原因
- 变量拼写错误:你把
result拼成了reusult,IDE无法匹配到正确的返回类型WaterBindingData,才会默认识别为IAsyncResult - foreach循环遍历对象错误:你写的
foreach (var item in IAsyncResult)是遍历IAsyncResult类型,而不是你定义的返回值变量,自然会报错 - 不建议在
GetViewForAnnotation方法中执行接口请求:这个方法是地图标记渲染的实时回调,阻塞请求会导致地图滑动卡顿、标记加载延迟,建议提前请求完所有站点的AL值,赋值给CustomPin后再刷新标记。
完整修改示例
步骤1:新增批量获取所有站点最大AL值的方法
放在你的地图业务逻辑类中,提前调用获取所有站点的预警等级:
public Dictionary<int, int> LoadAllStationMaxAlertLevel() { var stationMaxAlDict = new Dictionary<int, int>(); // 遍历0-16所有站点id for (int stationId = 0; stationId <= 16; stationId++) { try { string stationsApi = $"{Constants.EndPoint}stations"; string singleStationApi = $"{Constants.EndPoint}?id={stationId}"; var apiData = _restServiceData.GetWaterDataForecast(stationsApi, singleStationApi); // 判空后取该站点所有预报数据的最大AL值 if (apiData?.WaterData?.Ardaforecast?.Any() == true && apiData.WaterData.Ardaforecast[0].Items?.Any() == true) { int maxAlertLevel = apiData.WaterData.Ardaforecast[0].Items .Max(item => item.AlertLevelForecast); // 以站点MapCode为key存储最大AL值 int mapCode = apiData.WaterStation.Stations .First(s => s.MapCode == stationId).MapCode; stationMaxAlDict[mapCode] = maxAlertLevel; } } catch (Exception ex) { Debug.WriteLine($"站点{stationId}请求失败:{ex.Message}"); } } return stationMaxAlDict; }
步骤2:给CustomPin赋值后刷新地图
在地图初始化/数据刷新的位置调用上述方法:
// 示例调用位置:地图初始化完成后 var allStationAl = LoadAllStationMaxAlertLevel(); foreach (var pin in customMap.Pins.Cast<CustomPin>()) { if (allStationAl.TryGetValue(pin.MapCode, out int maxAl)) { pin.AlertLevel = maxAl; } } // 刷新地图标记 mapView.ReloadInputViews();
步骤3:简化GetViewForAnnotation方法
删除方法内的接口请求逻辑,保留原有颜色判断逻辑即可,无需额外修改。
内容的提问来源于stack exchange,提问作者Blagovest Pizhev
相关产品推荐
相关产品推荐

