Xamarin.Forms代码设置ImageSource偶发消失 iOS端完全不生效问题
问题根因
- 初始化时序错误:
InitializeComponent()被放在异步方法SetContent中调用,未在CV_PinStyle的构造函数第一行执行,导致XAML控件未完成初始化就被赋值,甚至可能出现空引用导致赋值失效。 - 视图转位图的时序不匹配:
BitmapDescriptorFactory.FromView()会立刻将当前视图的渲染结果转成位图资源,而你动态设置Image.Source是异步操作,图片还没加载完成就已经完成了位图转换,最终生成的Pin图标天然缺失图片内容。iOS对视图渲染的时序校验更严格,因此完全无法展示,Android取决于图片加载速度所以偶发失效。 - 动态资源无强引用:动态生成的
ImageSource没有被持久化引用,GC回收后已加载的图片就会丢失,这就是多个Pin加载时旧Pin图片消失的原因。 - 不必要的数据转换:
byte[]转字符串再转回图片的逻辑冗余,容易出现编码错误导致图片数据损坏。
修复方案
1. 重构CV_PinStyle构造逻辑
把初始化逻辑全部移到构造函数中同步执行,强制视图提前完成布局测量,确保转位图时所有内容已经准备完成:
public partial class CV_PinStyle : ContentView { public CV_PinStyle(EventType events) { // 构造函数第一行必须调用InitializeComponent初始化XAML控件 InitializeComponent(); SetContent(events); // 强制视图完成测量和布局,避免转位图时内容为空 this.Measure(WidthRequest, HeightRequest); this.Layout(new Rectangle(0, 0, WidthRequest, HeightRequest)); } private void SetContent(EventType events) { label_title.Text = events.name; label_price.Text = Converters.GetDecimalPriceFromCents(events.priceInCents) + " €"; // 直接用byte[]生成图片源,省略冗余的字符串转换避免编码错误 byte[] bt = Converters.StringToByteArray(events.eventPictureThumb); img_pic.Source = ImageSource.FromStream(() => new MemoryStream(bt)); } }
2. 等待图片加载完成后再生成Pin图标
如果图片较大需要异步加载,必须等图片加载完成后再调用FromView生成位图:
var pinView = new CV_PinStyle(currentEvent); // 监听图片加载完成事件 var loadTask = new TaskCompletionSource<bool>(); pinView.img_pic.PropertyChanged += (sender, args) => { if (args.PropertyName == nameof(Image.IsLoading) && !pinView.img_pic.IsLoading) { loadTask.TrySetResult(true); } }; // 等待图片加载完成再转位图 await loadTask.Task; var startPin = BitmapDescriptorFactory.FromView(pinView); Pin pin = new Pin { Label = currentEvent.name, Tag = currentEvent.tblEventID+Constants.DELIMITER+currentEvent.userID, Icon = startPin, Type = PinType.Place, Position = position }; gMap?.Pins?.Add(pin);
3. iOS适配
iOS平台存在未加入视觉树的视图转位图为空的已知问题,可临时将视图加入当前页面的隐藏布局容器中,转完位图后再移除,或者添加如下代码强制触发视图渲染:
// iOS平台专属处理 if(Device.RuntimePlatform == Device.iOS) { pinView.BackgroundColor = Color.Transparent; // 强制触发Native层渲染 var nativeView = Xamarin.Forms.Platform.iOS.Platform.CreateRenderer(pinView).NativeView; nativeView.Bounds = new CoreGraphics.CGRect(0, 0, pinView.WidthRequest, pinView.HeightRequest); nativeView.SetNeedsDisplay(); }
4. 添加资源缓存避免图片丢失
全局建立缓存字典保存已经生成的图片源和位图描述符,避免GC回收导致的图片丢失,同时提升加载性能:
// 全局静态缓存 private static Dictionary<string, BitmapDescriptor> _pinIconCache = new Dictionary<string, BitmapDescriptor>(); // 生成Pin前先查缓存 var cacheKey = currentEvent.tblEventID.ToString(); if(!_pinIconCache.TryGetValue(cacheKey, out var startPin)) { // 执行上述生成位图的逻辑 _pinIconCache[cacheKey] = startPin; }
额外注意事项
- 移除原代码中不必要的
Device.BeginInvokeOnMainThread封装,只要构造视图和赋值操作都在主线程执行就不需要额外调度。 - 不要重复创建同一个事件的CV_PinStyle实例,复用缓存的位图描述符即可大幅降低异常概率。
内容的提问来源于stack exchange,提问作者innom
相关产品推荐
相关产品推荐

