起因 
做了个播放器控制界面,发现进度滑动条Slider这个控件,如果全用默认不加修饰,那个滑块在iOS上会特别大。
 
要定制滑块,只要修改这个属性:
1 public  Xamarin.Forms.ImageSource ThumbImageSource { get ; set ; }
 
最简单的做法当然是直接加载一张图片。 
但是由于我想做一个方便复用的控件,最好是只包含代码就够,如果还带有资源,就不那么省心了。 
所以想想不如自己画这个小圆块,反正xamarin里也支持动态绘图,且能将绘好的图直接做为图片使用。
于是祭起SkiaSharp这个大杀器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var  slider = this .FindByName<Slider>("PrgSlider" );var  size = (int )slider.Height;SKBitmap bitmap = new  SKBitmap(size, size); using  (SKCanvas bitmapCanvas = new  SKCanvas(bitmap)){     bitmapCanvas.Clear();     SKPaint paint = new  SKPaint     {         Style = SKPaintStyle.Fill,         Color = Color.Pink.ToSKColor(),     };     bitmapCanvas.DrawCircle(size / 2 , size / 2 , size / 2 , paint); } this .FindByName<Slider>("PrgSlider" ).ThumbImageSource = (SKBitmapImageSource)bitmap;
 
但是没想到用了自绘的图像后,居然还是一大一小,iOS上仍然倍大。 
这时我想到了之前改过的一个Xamarin.iOS的bug,(提过pr ):
动态加载的图片,没有根据屏幕缩放系数(retina scale)做相应处理!
 
很快找到它的代码 一看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 [assembly: ExportImageSourceHandler(typeof(SkiaSharp.Views.Forms.SKImageImageSource), typeof(SkiaSharp.Views.Forms.SKImageSourceHandler)) ] [assembly: ExportImageSourceHandler(typeof(SkiaSharp.Views.Forms.SKBitmapImageSource), typeof(SkiaSharp.Views.Forms.SKImageSourceHandler)) ] [assembly: ExportImageSourceHandler(typeof(SkiaSharp.Views.Forms.SKPixmapImageSource), typeof(SkiaSharp.Views.Forms.SKImageSourceHandler)) ] [assembly: ExportImageSourceHandler(typeof(SkiaSharp.Views.Forms.SKPictureImageSource), typeof(SkiaSharp.Views.Forms.SKImageSourceHandler)) ] public  sealed  class  SKImageSourceHandler  : IImageSourceHandler {     public  Task<UIImage> LoadImageAsync (ImageSource imagesource, CancellationToken cancelationToken = default (CancellationToken ), float  scale  = 1f )     {         UIImage image = null ;         var  imageImageSource = imagesource as  SKImageImageSource;         if  (imageImageSource != null )         {             image = imageImageSource.Image?.ToUIImage();         }         var  bitmapImageSource = imagesource as  SKBitmapImageSource;         if  (bitmapImageSource != null )         {             image = bitmapImageSource.Bitmap?.ToUIImage();         }         var  pixmapImageSource = imagesource as  SKPixmapImageSource;         if  (pixmapImageSource != null )         {             image = pixmapImageSource.Pixmap?.ToUIImage();         }         var  pictureImageSource = imagesource as  SKPictureImageSource;         if  (pictureImageSource != null )         {             image = pictureImageSource.Picture?.ToUIImage(pictureImageSource.Dimensions);         }         return  Task.FromResult(image);     } } 
 
果不其然!完全没有用到最后一个参数scale,而且从标注了默认值1f来看,他们可能都不认为这个值会变 😓 
修正很简单,只要加上对scale的处理即可:
1 if  (scale > 1 ) image = new  UIImage(image.CGImage, scale, UIImageOrientation.Up);
 
但问题是如何在不自己编译SkiaSharp的情况下修正它呢?毕竟维护一个xamarin库太费事了 😂
好在问题是出在ImageSourceHandler上,xamarin上所有图像处理器都是通过属性自注册,集中处理,即:
1 2 3 4 5 6 7 8 namespace  Xamarin.Forms { 	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true) ] 	public  sealed  class  ExportImageSourceHandlerAttribute  : HandlerAttribute  	{ 		public  ExportImageSourceHandlerAttribute (Type handler, Type target ) ; 	} } 
 
这个ExportImageSourceHandler为每一种类型的ImageSource指定一个处理类,只要自己写个修正好的类,注册给对应的SkImageImageSource即可。
但是,和它源码中已经注册的处理类会不会冲突呢? 
幸好,该属性父类中有一个Priority成员,就是用来解决绑定到同一目标的多个处理类之间优先级的 😆 
所以,只要在注册自己的类时随便指定一个比默认值大的优先级,就顺利接管了SkImageImageSource的处理了:
[assembly: ExportImageSourceHandler(typeof(SkiaSharp.Views.Forms.SKBitmapImageSource), typeof(mb.SKImageSourceHandler), Priority = mb.SKImageSourceHandler.Priority)]
 
修正后: