2

Starting with iOS 13.0+, UIImage class has method withTintColor.

How do equivalent on iOS 12.4, to support older iPhones that can't run iOS 13?


The overall goal is to draw a tinted image, with a black or white glow around it (dark or light theme).

This is done as part of a Xamarin.Forms.Platform.iOS.PlatformEffect.

Xamarin C# iOS code that draws, into a bitmap, the glow overlaid by a tinted image - works on iOS 13:

private static UIImage ApplyGlow2( UIImage src, double radius, Xamarin.Forms.Color color,
                                   Xamarin.Forms.Color? tintColor, int outW, int outH)
{
    int glowRadius = (int)radius;
    // Increase size of the initial image.
    int marginX2 = glowRadius * 2;
    int margin = glowRadius;

    // Resize original image to display size.
    UIGraphics.BeginImageContext(new CGSize(outW, outH));
    src.Draw(new CGRect(0, 0, outW, outH));
    src = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();


    // Add margins.
    UIGraphics.BeginImageContext(new CGSize(outW + marginX2, outH + marginX2));
    CGContext context = UIGraphics.GetCurrentContext();
    CGRect drawRect = new CGRect(margin, margin, outW, outH);

    src.Draw(drawRect);

    src = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();

    // Create color overlay.
    UIGraphics.BeginImageContext(new CGSize(outW + marginX2, outH + marginX2));
    context = UIGraphics.GetCurrentContext();
    drawRect = new CGRect(0, 0, outW + marginX2, outH + marginX2);
    context.TranslateCTM(0, outH + marginX2);
    context.ScaleCTM(1.0f, -1.0f);


    context.SetBlendMode(CGBlendMode.Normal);
    context.SetFillColor(color.ToCGColor());
    context.FillRect(drawRect);

    context.SetBlendMode(CGBlendMode.DestinationIn);
    context.DrawImage(drawRect, src.CGImage);


    UIImage ret = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();

    // Create blurred image;
    // create a CIGaussianBlur filter with the input image.
    CIGaussianBlur gaussian_blur = new CIGaussianBlur()
    {
        InputImage = ret.CGImage,
        Radius = (float)radius * 0.5f,
    };
    CIImage output = gaussian_blur.OutputImage;
    CIContext context2 = CIContext.FromOptions(null);
    CGImage cgimage = context2.CreateCGImage(output, new CGRect(new CGPoint(0,0), ret.Size));
    UIImage blurredImage = UIImage.FromImage(cgimage);


    // -- Merge blur with original. --      
    UIGraphics.BeginImageContext(new CGSize(outW + marginX2, outH + marginX2));
    //CGContext context3 = UIGraphics.GetCurrentContext();

    // Blur.
    CGRect drawRect2 = new CGRect(0, 0, outW + marginX2, outH + marginX2);
    CGRect drawRect3 = new CGRect(1, 1, outW + marginX2, outH + marginX2);
    CGRect drawRect4 = new CGRect(2, 2, outW + marginX2, outH + marginX2);
    blurredImage.Draw(drawRect2);
    blurredImage.Draw(drawRect3);
    blurredImage.Draw(drawRect4);

    if (tintColor != null) {
        // Draw tinted original (over blur).
        var tintedImage = src.ApplyTintColor( tintColor.Value.ToUIColor() );
        tintedImage.Draw( drawRect2 );
    } else {
        // Draw original (over blur).
        src.Draw( drawRect2 );
    }
    ret = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();

    return ret;
}

The lines from above that are most relevant to my question:

// Draw tinted original (over blur).
var tintedImage = src.ApplyTintColor( tintColor.Value.ToUIColor() );
tintedImage.Draw( drawRect2 );

What I've tried:

iOS 12 does have a TintColor that can be applied while rendering into a UIImageView, but I don't see a way to use that when drawing programmatically into a UIImage:

// Apply tint color. UIImageView Control.
Control.Image = Control.Image.ImageWithRenderingMode(UIKit.UIImageRenderingMode.AlwaysTemplate);
Control.TintColor = ((TintedImage)Element).TintColor.ToUIColor();

The above code is similar to Using Tint color on UIImageView, which is the closest SO post I see.

There are several other similar posts, but AFAIK they all use uiimageview.tintColor, and don't add any additional info relevant to drawing into a Bitmap (not a view).

How to apply a tintColor to a UIImage?


If there is some way to create a UIView that isn't part of view hierarchy, set its TintColor, then render that into a bitmap, that might be a way to solve this. But I'm not seeing how to do that. One step of this approach would be similar to these posts:


I'm programming via Xamarin.iOS, but a solution in any iOS language will be accepted.

ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196

1 Answers1

0

(Xamarin C# iOS)
Replace these lines:

// Draw tinted original (over blur).
var tintedImage = src.ApplyTintColor( tintColor.Value.ToUIColor() );
tintedImage.Draw( drawRect2 );

with:

// Draw tinted original (over blur).
bool onIos13Plus = UIDevice.CurrentDevice.CheckSystemVersion( 13, 0 );
if (onIos13Plus) {
    UIImage tintedImage = src.ApplyTintColor( tintColor.Value.ToUIColor() );
    tintedImage.Draw( drawRect2 );
} else {   // iOS 12.
    CGRect drawRectT = new CGRect( 0, 0, outW, outH );
    var imageView = new UIImageView( drawRectT );
    imageView.Image = src.ImageWithRenderingMode( UIKit.UIImageRenderingMode.AlwaysTemplate );
    imageView.TintColor = tintColor.Value.ToUIColor();

    UIGraphics.BeginImageContextWithOptions( imageView.Bounds.Size, imageView.Opaque, 0.0f);
    imageView.Layer.RenderInContext( UIGraphics.GetCurrentContext() );
    UIImage tintedImage = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();
    tintedImage.Draw( drawRect2 );
}
ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196