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

如何用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; }
    }
}
解决方案

问题原因

  1. 变量拼写错误:你把result拼成了reusult,IDE无法匹配到正确的返回类型WaterBindingData,才会默认识别为IAsyncResult
  2. foreach循环遍历对象错误:你写的foreach (var item in IAsyncResult)是遍历IAsyncResult类型,而不是你定义的返回值变量,自然会报错
  3. 不建议在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 21:06:03