Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

Auto rendering 20+ icons for iOS, Droid and WinPhone Apps

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", "[email protected]", 640, 960);
                Render("iOS\\Resources", "[email protected]", 640, 1136);
                Render("iOS\\Resources", "Default-Landscape.png", 1024, 748);
                Render("iOS\\Resources", "[email protected]", 2048, 1496);
                Render("iOS\\Resources", "Default-Portrait.png", 768, 1004);
                Render("iOS\\Resources", "[email protected]", 1536, 2008);
                Render("iOS\\Resources", "Icon.png", 57, 57);
                Render("iOS\\Resources", "[email protected]", 114, 114);
                Render("iOS\\Resources", "Icon-60.png", 60, 60);
                Render("iOS\\Resources", "[email protected]", 120, 120);
                Render("iOS\\Resources", "[email protected]", 180, 180);
                Render("iOS\\Resources", "Icon-72.png", 72, 72);
                Render("iOS\\Resources", "[email protected]", 144, 144);
                Render("iOS\\Resources", "Icon-76.png", 76, 76);
                Render("iOS\\Resources", "[email protected]", 152, 152);
                Render("iOS\\Resources", "Icon-Small.png", 29, 29);
                Render("iOS\\Resources", "[email protected]", 58, 58);
                Render("iOS\\Resources", "[email protected]", 87, 87);
                Render("iOS\\Resources", "Icon-Small-40.png", 40, 40);
                Render("iOS\\Resources", "[email protected]", 80, 80);
                Render("iOS\\Resources", "[email protected]", 120, 120);
                Render("iOS\\Resources", "Icon-Small-50.png", 50, 50);
                Render("iOS\\Resources", "[email protected]", 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);
            }
        }
    }
}

Posts

  • ShantimohanElchuriShantimohanElchuri USMember ✭✭✭✭✭

    Ah... @JohnMichaelHauck much appreciated...I will definitely try it out. After all "Necessity (of the lazy guys) is the mother of (useful code) invention"...do you agree?

  • ianmercerianmercer USMember ✭✭

    @JohnMichaelHauck Creating 20+ variants of every icon and then different sizes for toolbars and app store icons is indeed a major chore.

    My solution was to create an AddIn for Xamarin Studio which I have just published. It's available from the AddIn Gallery in Xamarin Studio (Mac) and the instructions for it are here: https://github.com/IanMercer/imagemultiplier

    It takes SVG files and lists of sizes and file locations for the output and 'multiplies' each SVG by the appropriate instructions to create PNG files with all the desired sizes, file names and locations.

    Feedback, issues and pull requests welcome.

  • @ianmercer, nice! I knew I must not be alone out here.

  • voidvoid DKBeta ✭✭✭

    @ianmercer

    Nice addin ! I would love to use it. But. When will it support non-square sag's?

  • GeorgeCookGeorgeCook PEUniversity ✭✭✭
    edited July 2015

    if it's for graphics in your app, just use SVG : https://github.com/paulpatarinski/Xamarin.Forms.Plugins/tree/master/SVG I believe there's a nuget. All graphics in my app (icons, backgrounds, everything) are svg - one file on all platforms, always looks great irrespective of res, small filesize.. took me about 4 hours to hack the open source library to fit my purposes.

    and if it's for app icons, etc - I always use this : http://makeappicon.com/

  • voidvoid DKBeta ✭✭✭
    edited July 2015

    Right now I use sketch. And I like it. But I would rather add the SVGs to a folder in my project and have the XS addin generate the PNGs and put them in their right places.

    I'm hesitant to employ a component that does this at runtime. Seems a bit unnecessary.

  • FrancoisMFrancoisM FRUniversity ✭✭
    edited December 2015

    @GeorgeCook
    makeappicon.com (and others) generates a 88 file for 3x 29 instead of the required 87 in xamarin. What do you do then?

    => got it, you directly use the generated iconset :-)

  • FrancoisMFrancoisM FRUniversity ✭✭

    Actually I didn't manage to directly use the generated asset catalog with visual studio. I have to manually select each pic in the asset catalog I have to create for the project. And the 87 size is then missing.

  • MakarkinPROMakarkinPRO USMember

    If I undestood in orrectly here the plugin, which allow us get 1Splash Screen and 1Icon both of them should MAX resolution. And it'll generate all needed (Android, Android Tablet, iPhone (all of them), iPad (all of them), WinPhone (all of them)) icons and splash screens?

Sign In or Register to comment.