The number of icons needed to create a Xamarin Forms app is most irritating. That motivated me to hack together a WPF desktop application that renders application icons / splash screen using XAML (and places each file into my three-platform solution). It's not pretty, so I'm sure there has to be a better way. Is there a better way?
By the way, I also deleted the iOS LaunchScreen.storyboard from the .csproj file (and from the info.plist) so that way iOS just shows a splash screen image on startup.
MainWindow.xaml
<Window
x:Class="IconGenerator.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Icon Generator" Height="2048" Width="2048">
<Border x:Name="TheBorder" VerticalAlignment="Top" HorizontalAlignment="Left">
<Border.Background>
<LinearGradientBrush EndPoint="1,1" StartPoint="0,0">
<GradientStop Color="#FFA06000"/>
<GradientStop Color="#FFFF8B00" Offset=".4"/>
</LinearGradientBrush>
</Border.Background>
<Viewbox x:Name="TheIcon" Width="300" Height="300" VerticalAlignment="Bottom" HorizontalAlignment="Right">
<Grid>
<Path Data="M781.738,0.5 L782.188,0.896035 C828.028,42.1765 882.078,128.427 924.468,234.629 C938.598,270.03 950.258,304.715 959.358,337.505 L959.818,339.224 L960.598,341.338 C966.666,356.545 978.763,367.548 993.043,372.696 L999.238,374.459 L999.238,754.031 L0.5,754.031 L40.2531,726.078 L91.0831,692.867 L90.9501,692.385 L92.5061,691.09 C107.907,677.617 115.142,656.047 109.324,635.004 L108.669,632.849 L107.997,631.199 C95.4081,599.586 83.7211,564.909 73.5641,528.172 C42.7991,416.89 33.7981,314.621 45.8601,254.232 L46.4461,251.449 L54.5831,246.618 C70.9701,237.161 88.0621,228.111 105.782,219.561 C123.503,211.011 141.218,203.266 158.828,196.324 L161.928,195.144 L162.468,195.409 C217.478,223.335 292.108,292.547 360.678,384.056 C383.538,414.559 403.818,445.013 421.138,474.306 L422.028,475.846 L423.338,477.683 C436.428,495.154 457.918,502.62 477.998,498.675 L479.178,498.413 L504.438,489.024 C517.168,484.435 529.958,479.967 542.798,475.621 L583.788,462.314 L583.788,462.131 L585.628,461.286 C604.008,452.285 616.598,433.341 616.458,411.51 L616.388,409.258 L616.168,407.49 C612.248,373.69 609.988,337.167 609.738,299.052 C608.998,183.598 626.928,82.5111 654.288,27.3422 L655.578,24.8078 L664.688,22.2609 C682.978,17.3954 701.838,13.1057 721.168,9.46278 C740.508,5.81976 759.628,2.95322 778.428,0.831309 z" HorizontalAlignment="Left" Height="754" Margin="0.262,245.469,0,0" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Width="1000">
<Path.Fill>
<LinearGradientBrush EndPoint="1,1" MappingMode="RelativeToBoundingBox" StartPoint="0,0">
<GradientStop Color="Black" Offset="1"/>
<GradientStop Color="#FFCBCBCB"/>
</LinearGradientBrush>
</Path.Fill>
</Path>
</Grid>
</Viewbox>
</Border>
</Window>
MainWindow.xaml.cs
using System;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
namespace IconGenerator
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
Dispatcher.BeginInvoke((Action) (() =>
{
// From default Xamarin project
Render("Droid\\Resources\\drawable", "icon.png", 72, 72);
Render("Droid\\Resources\\drawable-hdpi", "icon.png", 72, 72);
Render("Droid\\Resources\\drawable-xhdpi", "icon.png", 96, 96);
Render("Droid\\Resources\\drawable-xxhdpi", "icon.png", 144, 144);
// From edited Xamarin project (project properties, iOS Application)
Render("iOS\\Resources", "Default.png", 320, 480);
Render("iOS\\Resources", "Default@2x.png", 640, 960);
Render("iOS\\Resources", "Default-568h@2x.png", 640, 1136);
Render("iOS\\Resources", "Default-Landscape.png", 1024, 748);
Render("iOS\\Resources", "Default-Landscape@2x.png", 2048, 1496);
Render("iOS\\Resources", "Default-Portrait.png", 768, 1004);
Render("iOS\\Resources", "Default-Portrait@2x.png", 1536, 2008);
Render("iOS\\Resources", "Icon.png", 57, 57);
Render("iOS\\Resources", "Icon@2x.png", 114, 114);
Render("iOS\\Resources", "Icon-60.png", 60, 60);
Render("iOS\\Resources", "Icon-60@2x.png", 120, 120);
Render("iOS\\Resources", "Icon-60@3x.png", 180, 180);
Render("iOS\\Resources", "Icon-72.png", 72, 72);
Render("iOS\\Resources", "Icon-72@2x.png", 144, 144);
Render("iOS\\Resources", "Icon-76.png", 76, 76);
Render("iOS\\Resources", "Icon-76@2x.png", 152, 152);
Render("iOS\\Resources", "Icon-Small.png", 29, 29);
Render("iOS\\Resources", "Icon-Small@2x.png", 58, 58);
Render("iOS\\Resources", "Icon-Small@3x.png", 87, 87);
Render("iOS\\Resources", "Icon-Small-40.png", 40, 40);
Render("iOS\\Resources", "Icon-Small-40@2x.png", 80, 80);
Render("iOS\\Resources", "Icon-Small-40@3x.png", 120, 120);
Render("iOS\\Resources", "Icon-Small-50.png", 50, 50);
Render("iOS\\Resources", "Icon-Small-50@2x.png", 100, 100);
// From default Xamarin project
Render("WinPhone", "SplashScreenImage.jpg", 720, 1280);
Render("WinPhone\\Assets", "AlignmentGrid.png", 768, 1280);
Render("WinPhone\\Assets", "ApplicationIcon.png", 100, 100);
Render("WinPhone\\Assets", "BadgeLogo.png", 58, 58);
Render("WinPhone\\Assets", "Logo.png", 106, 106);
Render("WinPhone\\Assets", "SplashScreen.png", 1152, 1920);
Render("WinPhone\\Assets", "SquareTile71x71.png", 170, 170);
Render("WinPhone\\Assets", "SquareTile150x150.png", 360, 360);
Render("WinPhone\\Assets", "StoreLogo.png", 120, 120);
Render("WinPhone\\Assets", "WideLogo.png", 744, 360);
Render("WinPhone\\Assets\\Tiles", "FlipCycleTileLarge.png", 691, 336);
Render("WinPhone\\Assets\\Tiles", "FlipCycleTileMedium.png", 336, 336);
Render("WinPhone\\Assets\\Tiles", "FlipCycleTileSmall.png", 159, 159);
Render("WinPhone\\Assets\\Tiles", "IconicTileMediumLarge.png", 130, 202);
Render("WinPhone\\Assets\\Tiles", "IconicTileSmall.png", 70, 110);
Application.Current.Shutdown();
}), DispatcherPriority.ApplicationIdle);
}
private void Render(string folder, string name, int width, int height)
{
TheIcon.Width = width;
TheIcon.Height = height;
folder = "..\\..\\..\\Abc123\\Abc123." + folder;
Dispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle);
var renderTargetBitmap = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(TheBorder);
var bitmapEncoder = (Path.GetExtension(name) == ".png") ? (BitmapEncoder)new PngBitmapEncoder() : new JpegBitmapEncoder();
var bitmapFrame = BitmapFrame.Create(renderTargetBitmap);
bitmapEncoder.Frames.Add(bitmapFrame);
Directory.CreateDirectory(folder);
using (var file = File.OpenWrite(Path.Combine(folder, name)))
{
bitmapEncoder.Save(file);
}
}
}
}