When developing a mobile application, there are a lot of points to pay attention to. This is the choice of the technology on which it will be written, and the development of the application architecture, and, in fact, writing the code. Sooner or later a moment comes when the core of the application is there, all the logic is written and the application, in general, works, but ... there is no appearance. Here it is worth thinking about the graphic resources that will be used, since graphics make up the lion's share of the size of the final assembly, be it .apk on Android or .ipa on iOS. Huge assemblies are, in principle, expected for mobile games, even now from PlayMarket you sometimes have to download data volumes up to 2 GB and it's good if you can connect to Wi-Fi during the download or the mobile operator provides a high-speed unlimited connection.But for games this is expected, and a business application of this size involuntarily raises the question βWhere does this come from?β. One of the reasons for the large size of the assembly of a business application can be a significant number of icons and pictures that have to be displayed in it. And also do not forget that a large amount of graphics proportionally affects the performance of the application.
When creating the graphical component of an application, there is often a serious problem. There are a great many mobile devices, from watches to tablets, and their screen resolutions are very different. Because of this, it is often necessary to include graphic resources in an assembly in separate files for each of the existing types. 5 copies for Android and 3 for iOS. This significantly affects the size of the final assembly that you will upload to the store.
We will tell you what can be done in order not to get into such a situation in this article.
Comparison of PNG and SVG formats
The main difference between PNG and SVG formats is that PNG is a bitmap format and SVG is a vector format.
A bitmap is a grid of pixels on a monitor and is used everywhere, be it small icons or huge banners. Among the advantages of this type of graphics, the following should also be noted:
- Raster graphics allow you to create drawings of almost any complexity, without noticeable loss in file size;
- If no image scaling is required, the processing speed of complex images is very high;
- Bitmap representation of images is natural for most I / O devices.
However, there are also downsides.
For example, a bitmap cannot be scaled perfectly. When you enlarge a small picture, the image "lathers" and you can see the pixels of which it consists.
Also, simple images consisting of a large number of dots are large. Therefore, it is recommended to store simple drawings in vector format. All of the above is true for both PNG and any other raster graphics format.
The SVG format, in turn, is not really an image. According to the wikipedia article, SVG is a scalable vector graphics markup language that allows you to read and edit a file as needed.
Also, being a markup language, SVG allows you to apply filters within a document (e.g. blur, extrusion, etc.). They are declared as tags that the viewer is responsible for rendering, which means they do not affect the size of the original file.
In addition to the above, the SVG format has a number of other advantages:
- As a vector format, SVG allows you to scale any part of an image without loss of quality;
- Raster graphics can be inserted into an SVG document;
- Easily integrates with HTML and XHTML documents.
However, there are also disadvantages of this format:
- The more fine detail in the image, the faster the size of the SVG data grows. In some cases, SVG not only does not provide advantages, but also loses to the raster;
- The complexity of use in cartographic applications, since for the correct display of a small part of the image, you need to read the entire document.
There is another disadvantage in the case of mobile app development on the Xamarin platform.
To work correctly with this format, you need to connect additional libraries, or look for workarounds.
Working with SVG format in Xamarin.Android
As mentioned above, Xamarin does not support SVG out of the box. To solve this problem - you can use third party libraries or VectorDrawable. Recently, developers are increasingly preferring the latter, since most of the libraries for Xamarin are focused on cross-platform use in Xamarin.Forms, and solutions for native android are either no longer supported by their developers and are outdated, or there are serious problems when trying to build on their basis library for Xamarin. In this regard, here we will consider using VectorDrawable.
To implement this approach, you will need to use Vector Asset Studio. Finding it is easy enough, you just need Android Studio. So, let's begin:
- Android Studio File -> New -> New Project;
, β Vector Asset Studio, . - drawable -> New -> Vector Asset;
Asset Studio. Material Design, SVG PSD, xml-. . - Asset Studio
, , . - Next
Xamarin.Android.
Enough preparatory work that requires Android Studio to be installed. Unfortunately, at the time of this writing, we could not find correctly working online converters. Some gave an error when trying to convert the provided SVG file, some indicated that it was better to use Vector Asset Studio.
Be that as it may, we received the required file and now we can use it in our project. We will not describe the process of creating a Xamarin.Android project, let's go straight to the point.
First of all, we placed the resulting file in the drawable folder of the project, then, as a demonstration of working with this file, we created 3 ImageViews on the screen.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android ="http://schemas.android.com/apk/res/android"
xmlns:app ="http://schemas.android.com/apk/res-auto"
android:layout_width ="match_parent"
android:layout_height="match_parent"
android:minWidth="25px"
android:minHeight="25px">
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentLeft="true"
android:id="@+id/imageView1"
android:adjustViewBounds="true"
app:srcCompat="@drawable/question_svg"
android:background="@android:color/holo_red_dark"/>
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentRight="true"
android:id="@+id/imageView2"
android:adjustViewBounds="true"
app:src="@drawable/question"
android:background="@android:color/holo_red_dark"/>
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:id="@+id/imageView3"
android:adjustViewBounds="true"
android:layout_marginTop="30dp"
app:srcCompat="@drawable/question"
android:layout_below="@id/imageView1"/>
</RelativeLayout>
In the first ImageView, we tried to assign the original SVG file in the android: src property, in the second, the converted XML file in the same property, and in the third, in the app: srcCompat property (note that this namespace must be specified as indicated in the code ).
The results were already noticeable in the designer, before the application was launched - the only sure way to correctly display our file is given in the third ImageView.
Now you need to make sure that the image is displayed correctly on different devices. For comparison, we chose Huawei Honor 20 Pro and Nexus 5. And also, instead of incorrect display methods, we specified the desired one and resized the ImageView to 150x150 dp, 200x200 dp and 300x300 dp.
Huawei Honor 20 Pro
Nexus 5
As you can see, the quality of the images is the same, which means that we have achieved the desired result.
The resulting file is used from the code in the same way as usual
image.SetImageResource(Resource.Drawable.< >);
Working with SVG format in Xamarin.iOS
In the case of Xamarin.iOS, the situation is the same as with Xamarin.Android, in the sense that working with SVG files is not supported out of the box. However, its implementation does not require much effort. In case of native iOS development, SVG requires SVGKit to be included. It allows you to process such files without resorting to third-party solutions. In the case of Xamarin.iOS, we can use the SVGKit.Binding library . This is a C # wrapper over the original SVGKit.
All we need is to connect the NuGet package to our project. At the time of this writing, the latest version is 1.0.4.
Unfortunately, apparently, this library is no longer supported, but it is quite suitable for use in the project.
Here the class SVGKImage (the SVG image itself) and SVGKFastImageView (the control for displaying such images) are implemented.
void CreateControls()
{
var image_bundle_resource = new SVGKImage("SVGImages/question.svg");
img1 = new SVGKFastImageView(image_bundle_resource);
Add(img1);
img1.Frame = new CGRect(View.Frame.Width / 4, 50, View.Frame.Width / 2, View.Frame.Width / 2);
var image_content = new SVGKImage(Path.Combine(NSBundle.MainBundle.BundlePath, "SVGImages/question1.svg"));
img2 = new SVGKFastImageView(image_content);
Add(img2);
img2.Frame = new CGRect(5, img1.Frame.Bottom + 5, View.Frame.Width - 5, View.Frame.Width - 5);
}
Unfortunately, the library has a number of disadvantages:
- SVGKFastImageView does not support initialization without providing SVGKImage - an error is thrown indicating the need to use the constructor provided in the code;
- There is no way to use SVG files in other controls. However, if this is not necessary, then you can use it.
If the application is critical to use SVG files not only in ImageView, you can use other libraries. For example, FFImageLoading . This is a powerful library that allows you not only to unload SVG images in the native Xamarin.iOS UIImage, but also to do it directly into the desired control, saving development time. There is also functionality for downloading images from the Internet, but we will not consider it in this article.
To use the library, you need to connect two NuGet packages to the project - Xamarin.FFImageLoading and Xamarin.FFImageLoading.SVG.
In the test application, we used a method that loads the SVG image into the UIImageView directly and unloads the image into the native UIImage.
private async Task CreateControls()
{
img1 = new UIImageView();
Add(img1);
img1.Frame = new CGRect(View.Frame.Width / 4, 50, View.Frame.Width/2, View.Frame.Width/2);
ImageService.Instance
.LoadFile("SVGImages/question.svg")
.WithCustomDataResolver(new SvgDataResolver((int)img1.Frame.Width, 0, true))
.Into(img1);
var button = new UIButton() { BackgroundColor = UIColor.Red};
Add(button);
button.Frame = new CGRect(View.Frame.Width/2 - 25, img1.Frame.Bottom + 20, 50, 50);
UIImage imageSVG = await ImageService.Instance
.LoadFile("SVGImages/question.svg")
.WithCustomDataResolver(new SvgDataResolver((int)View.Frame.Width, 0, true))
.AsUIImageAsync();
if(imageSVG != null)
button.SetBackgroundImage(imageSVG, UIControlState.Normal);
}
There is also a method in this library to override the SVG string representation in UIImage, but it only makes sense for small files.
var svgString = @"<svg><rect width=""30"" height=""30"" style=""fill:blue"" /></svg>";
UIImage img = await ImageService.Instance
.LoadString(svgString)
.WithCustomDataResolver(new SvgDataResolver(64, 0, true))
.AsUIImageAsync();
The library also has a number of functionalities, but we will not consider them here. The only thing really worth mentioning is that this library can be used in Xamarin.Android projects as well. For use in Xamarin.Forms projects, there are analogues of this library, which we will discuss below.
Working with SVG format in Xamarin.Forms
By and large, it is not necessary to look for libraries for forms, you can simply connect the platforms known to projects and it takes a long time and dreary to rewrite renders for each control that you want to use. Fortunately, there is a solution that we will share.
Xamarin.FFImageLoading Xamarin.FFImageLoading.SVG, Xamarin.Forms (Xamarin.FFImageLoading.Forms Xamarin.FFImageLoading.SVG.Forms). Xamarin.Forms , , :
- (PCL ) NuGet- β Xamarin.FFImageLoading.Forms Xamarin.FFImageLoading.SVG.Forms;
- AppDelegate.cs iOS-:
public override bool FinishedLaunching( UIApplication app, NSDictionary options ) { global::Xamarin.Forms.Forms.Init(); FFImageLoading.Forms.Platform.CachedImageRenderer.Init(); CachedImageRenderer.InitImageSourceHandler(); var ignore = typeof(SvgCachedImage); LoadApplication(new App()); return base.FinishedLaunching(app, options); }
- MainActivity.cs Android-:
protected override void OnCreate( Bundle savedInstanceState ) { TabLayoutResource = Resource.Layout.Tabbar; ToolbarResource = Resource.Layout.Toolbar; base.OnCreate(savedInstanceState); Xamarin.Essentials.Platform.Init(this, savedInstanceState); global::Xamarin.Forms.Forms.Init(this, savedInstanceState); FFImageLoading.Forms.Platform.CachedImageRenderer.Init(true); CachedImageRenderer.InitImageViewHandler(); var ignore = typeof(SvgCachedImage); LoadApplication(new App()); }
The library implements calls to SVG resources located both in platform projects and EmbeddedResouce PCL projects. SvgCachedImage is used to display SVG images. Unlike the original Image control used in Xamarin.Forms and accepting an ImageSource, SvgCachedImage works with SvgImageSource.
xmlns:svg="clr-namespace:FFImageLoading.Svg.Forms;assembly=FFImageLoading.Svg.Forms"
You can provide it in several ways:
- Specify in the XAML file the name of the SVG file located in the platform project resources:
<svg:SvgCachedImage Source="question.svg" WidthRequest="100" HeightRequest="100"/>
- Specify in the XAML file the full path to the SVG file that is an embedded resource in the PCL project:
<svg:SvgCachedImage Source="resource://SVGFormsTest.SVG.question1.svg" WidthRequest="100" HeightRequest="100"/>
When loading from embedded resources, you can specify the name of another assembly:
resource://SVGFormsTest.SVG.question1.svg?assembly=[ASSEMBLY FULL NAME]
- When working from code, we first give the controls a name:
<svg:SvgCachedImage x:Name="image" WidthRequest="100" HeightRequest="100"/>
Then, depending on which resource is being used, we choose one of the options:
- to use embedded resources
image.Source = SvgImageSource.FromResource("SVGFormsTest.SVG.question1.svg");
-
image.Source = SvgImageSource.FromFile("question.svg");
- to use embedded resources
- Binding 2 :
- , SvgImageSource. , XAML- :
<svg:SvgCachedImage Source="{Binding Source}" WidthRequest="100" HeightRequest="100"/>
- . , . :
<ContentPage.Resources> <ResourceDictionary> <svg:SvgImageSourceConverter x:Key="SourceConverter"/> </ResourceDictionary> </ContentPage.Resources>
:
<svg:SvgCachedImage Source="{Binding Source1, Converter={StaticResource SourceConverter}}" WidthRequest="100" HeightRequest="100"/>
:
public MainVM() { source1 = "question.svg"; } string source1; public string Source1 { get => source1; set { source1 = value; } }
:
public MainVM() { source1 = "resource://SVGFormsTest.SVG.question1.svg"; } string source1; public string Source1 { get => source1; set { source1 = value; } }
, . , , .
- , SvgImageSource. , XAML- :
SVG- , . , , , , , , SVG- . , PNG- , SVG, .
SVG resources also have a minor drawback. They cannot be provided as an application icon, since only PNG and JPEG files can be used as an icon according to Google and Apple guidelines.