All Posts

Creating Icon Sets for Xamarin.Forms

My AppIconMaker project lets you resize a large icon to all the different dimensions needed by the iOS App Store, as well as three sizes for Android, with the push of a button. If you’ve used the RedCorners.Forms template to create your projects, you can simply drag and drop the output files into your projects and replace the files. Everything fits perfectly!

iOS Icons

The following files are generated for the iOS target:

Tree of logo1024.png_ios
    Contents.json
    Icon-App-20x20@1x.png
    Icon-App-20x20@2x.png
    Icon-App-20x20@3x.png
    Icon-App-29x29@1x.png
    Icon-App-29x29@2x.png
    Icon-App-29x29@2x1.png
    Icon-App-29x29@3x.png
    Icon-App-40x40@1x.png
    Icon-App-40x40@2x.png
    Icon-App-40x40@2x1.png
    Icon-App-40x40@3x.png
    Icon-App-57x57@1x.png
    Icon-App-57x57@2x.png
    Icon-App-60x60@1x.png
    Icon-App-60x60@2x.png
    Icon-App-60x60@3x.png
    Icon-App-72x72@1x.png
    Icon-App-72x72@2x.png
    Icon-App-76x76@1x.png
    Icon-App-76x76@2x.png
    Icon-App-76x76@3x.png
    Icon-App-83.5x83.5@2x.png
    Icon-Small-50x50@1x.png
    Icon-Small-50x50@2x.png
    icon_rect_1024.png

Android Icons

The following files are generated for the Android target:

Tree of logo1024t.png_droid
├───drawable-hdpi
│       icon.png
│
├───drawable-xhdpi
│       icon.png
│
└───drawable-xxhdpi
        icon.png

Xamarin.Forms and Notch

Since the release of iPhone X, we have to deal with devices with notches and make sure our UI elements don’t overlap with the notifications bar, the notch and the task switching bar on the bottom of the screen. However, we can’t just add a fixed padding to everything to resolve this issue, because devices without notch and with smaller safe areas also exist, and we have to support them as well.

This leads to a lot of nasty spaghetti code which can be hard to maintain.

Fortunately, RedCorners.Forms comes to rescue. It provides smart alternatives to ContentPage and ContentView that can automatically adjust their paddings based on the iOS device used. Simply derive your pages from RedCorners.Forms.ContentPage2 or your views from RedCorners.Forms.ContentView2 and set their FixTopPadding and/or FixBottomPadding properties to automatically adjust their paddings based on the device.

<rf:ContentPage2
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:d="http://xamarin.com/schemas/2014/forms/design"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:rf="clr-namespace:RedCorners.Forms;assembly=RedCorners.Forms"
    xmlns:vm="clr-namespace:Demos.ViewModels"
    mc:Ignorable="d"
    x:Class="Demos.Views.FixPaddingPage"
    BackgroundColor="Black"
    FixBottomPadding="{Binding FixBottomPadding}" 
    FixTopPadding="{Binding FixTopPadding}"
    UIStatusBarStyle="{Binding UIStatusBarStyle}">
    <ContentPage.BindingContext>
        <vm:FixPaddingViewModel />
    </ContentPage.BindingContext>
    <Grid BackgroundColor="White">
        <StackLayout HorizontalOptions="Fill" VerticalOptions="Center" Padding="30">
            <Grid>
                <Label Text="FixTopPadding" />
                <Switch HorizontalOptions="End" IsToggled="{Binding FixTopPadding}" />
            </Grid>
            <Grid>
                <Label Text="FixBottomPadding" />
                <Switch HorizontalOptions="End" IsToggled="{Binding FixBottomPadding}" />
            </Grid>
            <Button Text="Close" Command="{Static rf:Values.PopCommand}" />
        </StackLayout>
    </Grid>
</rf:ContentPage2>

View Model:

public class FixPaddingViewModel : BindableModel
{
    public UIStatusBarStyles UIStatusBarStyle =>
        FixTopPadding ? 
            UIStatusBarStyles.LightContent :
            UIStatusBarStyles.Default;

    bool _fixTopPadding = false;
    public bool FixTopPadding
    {
        get => _fixTopPadding;
        set
        {
            SetProperty(ref _fixTopPadding, value);
            RaisePropertyChanged(nameof(UIStatusBarStyle));
        }
    }

    bool _fixBottomPadding = false;
    public bool FixBottomPadding
    {
        get => _fixBottomPadding;
        set => SetProperty(ref _fixBottomPadding, value);
    }
}

In this example, we also change the UIStatusBarStyle to make sure it is visible for any given FixTopPadding value.

Customizing Xamarin.Forms Frame Shadow

The latest version of RedCorners.Forms introduces Frame2, an extended version of Xamarin.Forms.Frame which provides additional bindable properties to adjust the shadow radius (aka distance or elevation) and color:

To use Frame2, first you have to include the RedCorners.Forms namespace in your XAML:

xmlns:rf="clr-namespace:RedCorners.Forms;assembly=RedCorners.Forms"

Afterwards, you can use a rf:Frame2 tag and set its ShadowRadius and ShadowColor properties to adjust the shadows:

<rf:Frame2 
    HasShadow="True" 
    ShadowRadius="20" 
    ShadowColor="Red"
    BackgroundColor="White">
    <Grid HorizontalOptions="Fill" HeightRequest="100">
        <Label HorizontalOptions="Center" VerticalOptions="Center" Text="Hello, World" />
    </Grid>
</rf:Frame2>

One issue I experienced was setting high values for ShadowRadius made the whole iOS system crash! So make sure you know what you’re doing.

Note that since the algorithms are different on each platform, the same values would produce different looking shadows. So it is advised that you provide different values for each platform and test the results.

Read and Write GPS coordinates with RedCorners.ExifLibrary

I recently published RedCorners.ExifLibrary, a NuGet that lets you read and write EXIF metadata in image files. This is basically a fork of the ExifLibrary project by oozcitak, with four minor changes:

  • It removes dependencies to System.Drawing, making the library work across all .net Standard 2.0 compatible runtimes, including Xamarin.iOS and Xamarin.Android.
  • It adds a conversion helper method for going back and forth between floating point or Degrees/Minutes/Seconds representations for coordinates.
  • It adds helper methods to quickly read and write GPS coordinates in the image files.
  • Everything is moved to the RedCorners.ExifLibrary namespace, in order to make it possible to use both the original library and my version of it simultaneously.
  • It has been tested on Xamarin.iOS, Xamarin.Android, .NET Core and .NET Framework on JPEG images.

You can get the NuGet from here: https://www.nuget.org/packages/RedCorners.ExifLibrary

Some quick examples:

using RedCorners.ExifLibrary;

// Read GPS Coordinates
var file = ImageFile.FromFile(Path);
var coords = file.GetGPSCoords();
if (coords.HasValue)
    (Latitude, Longitude) = coords.Value;

// Write GPS Coordinates
var file = ImageFile.FromFile(Path);
file.SetGPSCoords(Latitude, Longitude);
file.Save(Path);

More information on GitHub.

Lines in Unity

TL;DR: Download NeatLine from https://github.com/samafshari/NeatLine!

It is sometimes amazing how you want to do a very simple task, but you cannot find a simple and to-the-point library that does it for you. A few of my games require basic 2D lines, and Unity itself does not have the functionality to create 2D lines. There are a few complex assets on the Asset Store, but their licensing is not quite obvious, and I hesitate to open source my projects when using those assets.

Therefore I went ahead and created a simple dependencyless component that does one thing: Drawing 2D lines.

You can get it from GitHub using the link above. The main component is called NeatLine, which can be added to your Scene by clicking on the GameObject>2D Object>NeatLine menu item.

You can control four things about your line:

  • Vector2 HeadLocalPosition: Position of the first point, relative to the local transform.
  • Vector2 TailLocalPosition: Position of the second point, relative to the local transform.
  • float Thickness: Line’s thickness.
  • Color Color: Line’s color.

All four properties can be modified at edit or runtime.

Here’s how it works

To draw a custom shape in Unity, we need three things: MeshRenderer, MeshFilter and Material.

meshFilter = gameObject.AddComponent();
meshRenderer = gameObject.AddComponent();
material = Resources.Load("NeatLineMaterial");

But before drawing a shape, we need to create the shape (Mesh) itself, i.e. calculating vertices, triangles and indices.

Calculating vertices is easy. We need to get the normal vector, add thickness to it and get the four corners of the line:

Vector3[] GetPolygon()
{
    var vec = TailLocalPosition - HeadLocalPosition;
    var unit = vec.normalized;
    var halfCross = new Vector3(unit.y, -unit.x, 0) * 0.5f * Thickness;

    return new[]
    {
        new Vector3(HeadLocalPosition.x, HeadLocalPosition.y) - halfCross,
        new Vector3(HeadLocalPosition.x, HeadLocalPosition.y) + halfCross,
        new Vector3(TailLocalPosition.x, TailLocalPosition.y) + halfCross,
        new Vector3(TailLocalPosition.x, TailLocalPosition.y) - halfCross
    };
}

Based on these four vertices, the indices of our line are:

new[] { 0, 1, 2, 2, 3, 0 };

And these are enough for us to create the Mesh:

var mesh = new Mesh();
meshFilter.mesh = mesh;
mesh.vertices = GetPolygon();
mesh.triangles = new[] { 0, 1, 2, 2, 3, 0 };
mesh.RecalculateNormals();
mesh.RecalculateBounds();

The last step is to assign the color to the Material:

material.color = Color;

Alternatively, you can assign individual colors to each vertex:

mesh.colors = new[] { Color, Color, Color, Color };

And there we go. We have a line!

NeatPolyline

If you are seeking some more advanced features, such as multiple vertices per line, or individual color and thickness settings per vertex, you can use NeatPolyline.

For more information, please check out the GitHub page of the project.

USB devices won’t show up on VMware’s list

I just had an issue where some of my USB devices (e.g. an iPhone) were not showing up on VMware’s VM>Removable Devices menu.

The reason was, for some reason, the VMware USB Arbitration Service was stopped. Starting it manually solved the issue.

RestSharp returns 0 for custom ASP.NET Core middleware response

A normal behavior of RestSharp is to return StatusCode = 0 for responses that won’t go through (e.g. unreachable server). However, a tricky behavior of RestSharp is to also return 0 if the response has content, but the Content-Length is undefined.

If you are confused why your RestSharp is returning 0 while Postman and Chrome work fine, make sure you are setting the response Content-Length to the actual content length.

public async Task Invoke(HttpContext context)
{
    //instead of await next(context);
    var result = JsonConvert.SerializeObject(new
    {
        Message = "Hello, World!"
    });
    context.Response.ContentType = "application/json; charset=utf-8";
    context.Response.StatusCode = 200;
    context.Response.ContentLength = result.Length + 2;
    await context.Response.WriteAsync("\r\n" + result);
    await context.Response.Body.FlushAsync();
}

Images captured by iOS show up rotated on Android

SkiaSharp is a Xamarin-friendly NuGet that lets you do powerful image processing and rendering tasks. You can use it to quickly resize, compress or re-encode your images (i.e. change their format). This example shows how to change a JPEG file to PNG with SkiaSharp:

using (var istream = File.OpenRead(originalPath))
    using (var skBitmap = SKBitmap.Decode(istream))
        using (var image = SKImage.FromBitmap(skBitmap))
            using (var ostream = File.OpenWrite(newPath))
                image.Encode(SKEncodedImageFormat.Png, 100)
                    .SaveTo(ostream);

Images captured by iOS show up rotated on Android

When you capture a photo with TakePhotoAsync on the iOS, regardless of the SaveMetaData flag or RotateImage, it might be stored rotated. However, you won’t notice it on the iOS itself, or on Windows, since the orientation information is stored in the EXIF metadata of the captured JPEG file.

iOS or Windows show the image correctly because they take into account the EXIF orientation, but the Image view of Xamarin.Forms on Android ignores this flag, thus making the image show up as-is, i.e. rotated.

If you store the image in a different format than JPEG (e.g. PNG) on the iOS, you can also see the image rotated there, because PNG doesn’t have an orientation flag.

If you are using Xamarin.Forms, consider using CachedImage from FFImageLoading instead of the built-in Image view. CachedImage looks at the orientation information on Android, and rotates the image accordingly before rendering it.

However, if you are uploading the image to an ASP.NET core application and need to process it, you can rotate it manually based on the EXIF information. .NET core by itself doesn’t have image manipulation capabilities, but you can install the CoreCompat.System.Drawing NuGet package, which is a port of System.Drawing for .NET core.

First, load the image as an Image object:

var image = Image.FromFile(path);
Then see if it has the rotation value, and rotate accordingly:

if (image.PropertyIdList.Contains(0x112))
{
    int rotationValue = image.GetPropertyItem(0x112).Value[0];
    if (rotationValue == 8) 
        image.RotateFlip(rotateFlipType: RotateFlipType.Rotate270FlipNone);
    else if (rotationValue == 3) 
        image.RotateFlip(rotateFlipType: RotateFlipType.Rotate180FlipNone);
    else if (rotateValue == 6)
        image.RotateFlip(rotateFlipType: RotateFlipType.Rotate90FlipNone);
}

Optionally, you can save the image on the original file:

image.Save(path);

Checking whether a DLL is 32-bit or 64-bit

A very easy method to check whether a particular DLL is x86-64 compatible or not is by using the file command in Linux. If you have bash installed on your Windows, you can type:

file MY_DLL.dll

If it’s a 64-bit DLL, you will see:

PE32+ executable (DLL) (GUI) x86-64, for MS Windows

Otherwise, it prints:

PE32 executable (DLL) (GUI) Intel 80386, for MS Windows